#ifndef __OMP_H__
#define __OMP_H__

#include <stdint.h>
#include <pthread.h>

/* Define the lock data types */
typedef pthread_mutex_t omp_lock_t;
typedef pthread_mutex_t omp_nest_lock_t;

/* Define the schedule kinds */
typedef enum omp_sched_t
{
  omp_sched_static = 1,
  omp_sched_dynamic = 2,
  omp_sched_guided = 3,
  omp_sched_auto = 4
} omp_sched_t;

/* Exported OpenMP functions */
#ifdef __cplusplus
extern "C"
{
#endif

/* Returns the number of teams in the current teams region */
extern int omp_get_num_teams( void);

/* Returns the team number of the calling thread */
extern int omp_get_team_num( void);

/* Set total number of threads to be used in subsequent parallel regions */
extern void omp_set_num_threads (int num_threads);

/* Returns the number of threads in the current team */
extern int omp_get_num_threads (void);

/* Returns upper bound on the number of threads to form a new team */
extern int omp_get_max_threads (void);

/* Returns the current thread number (index) within thread team  */
extern int omp_get_thread_num (void);

/* Returns the number of processor cores available to the program */
extern int omp_get_num_procs (void);

/* Returns true if the routine is executed in a final task */
extern int omp_in_final (void);

/* Returns true if the call is enclosed by an active parallel region */
extern int omp_in_parallel (void);

/* Enabling of dynamic adjustiment of number of threads is UNSUPPORTED */
extern void omp_set_dynamic (int dynamic_threads);

/* Dynamic adjustiment of number of threads is UNSUPPORTED */
extern int omp_get_dynamic (void);

/* Enabling of nested parallelism is UNSUPPORTED */
extern void omp_set_nested (int nested);

/* Nested parallelism is disabled */
extern int omp_get_nested (void);

/* Only auto (static) scheduler is supported */
extern void omp_set_schedule (omp_sched_t kind, int modifier);

/* Get current scheduling kind. Only auto */
extern void omp_get_schedule (omp_sched_t *kind, int *modifier);

/* Returns maximum number of OpenMP threads available to program */
extern int omp_get_thread_limit (void);

/* Only single level of nested active parallel regions is supported */
extern void omp_set_max_active_levels (int max_active_levels);

/* Get the maximum number of nested active parallel regions */
extern int omp_get_max_active_levels (void);

/* Returns number of parallel regions enclosing the call */
extern int omp_get_level (void);

/* Returns the thread number of the ancestor at level level */
extern int omp_get_ancestor_thread_num (int level);

/* Returns the current team size at nesting level level */
extern int omp_get_team_size (int level);

/* Returns number of nested active parallel regions */
extern int omp_get_active_level (void);

/* C locks: init */
extern void omp_init_lock (omp_lock_t *lock);

/* C locks: destroy */
extern void omp_destroy_lock (omp_lock_t *lock);

/* C locks: lock */
extern void omp_set_lock (omp_lock_t *lock);

/* C locks: unlock */
extern void omp_unset_lock (omp_lock_t *lock);

/* C locks: trylock (nonblocking) */
extern int omp_test_lock (omp_lock_t *lock);

/* C nested locks: init */
extern void omp_init_nest_lock (omp_nest_lock_t *lock);

/* C nested locks: destroy */
extern void omp_destroy_nest_lock (omp_nest_lock_t *lock);

/* C nested locks: lock */
extern void omp_set_nest_lock (omp_nest_lock_t *lock);

/* C nested locks: unlock */
extern void omp_unset_nest_lock (omp_nest_lock_t *lock);

/* C nested locks: trylock (nonblocking) */
extern int omp_test_nest_lock (omp_nest_lock_t *lock);

/*  */
extern double omp_get_wtime (void);

/*  */
extern double omp_get_wtick (void);


#if defined(FCC_H) || defined(OMP_FORTRAN_WRAPPER)
/* lfortran - fortran wrappers */
typedef union
{
    uint64_t uint;
    omp_lock_t *lock;
} omp_fortran_lock_t;

typedef union
{
    uint64_t uint;
    omp_nest_lock_t *lock;
} omp_fortran_nest_lock_t;

/* Fortran 8-byte locks: init */
extern void omp_init_lock_ (omp_fortran_lock_t *lock);

/* Fortran 8-byte locks: destroy */
extern void omp_destroy_lock_ (omp_fortran_lock_t *lock);

/* Fortran 8-byte locks: lock */
extern void omp_set_lock_ (omp_fortran_lock_t *lock);

/* Fortran 8-byte locks: unlock */
extern void omp_unset_lock_ (omp_fortran_lock_t *lock);

/* Fortran 8-byte locks: trylock (nonblocking) */
extern int omp_test_lock_ (omp_fortran_lock_t *lock);

/* Fortran 8-byte nested locks: init */
extern void omp_init_nest_lock_ (omp_fortran_nest_lock_t *lock);

/* Fortran 8-byte nested locks: destroy */
extern void omp_destroy_nest_lock_ (omp_fortran_nest_lock_t *lock);

/* Fortran 8-byte nested locks: lock */
extern void omp_set_nest_lock_ (omp_fortran_nest_lock_t *lock);

/* Fortran 8-byte nested locks: unlock */
extern void omp_unset_nest_lock_ (omp_fortran_nest_lock_t *lock);

/* Fortran 8-byte nested locks: trylock (nonblocking) */
extern int omp_test_nest_lock_ (omp_fortran_nest_lock_t *lock);

#endif

#ifdef __cplusplus
}
#endif

#endif /* __OMP_H__ */
