static char rcsid[] = "$Id: ompc_runtime.c,v 1.38 2002/10/08 13:44:37 a-hasega Exp $";
/* 
 * $RWC_Release: Omni-1.6 $
 * $RWC_Copyright:
 *  Omni Compiler Software Version 1.5-1.6
 *  Copyright (C) 2002 PC Cluster Consortium
 *  
 *  This software is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License version
 *  2.1 published by the Free Software Foundation.
 *  
 *  Omni Compiler Software Version 1.0-1.4
 *  Copyright (C) 1999, 2000, 2001.
 *   Tsukuba Research Center, Real World Computing Partnership, Japan.
 *  
 *  Please check the Copyright and License information in the files named
 *  COPYRIGHT and LICENSE under the top  directory of the Omni Compiler
 *  Software release kit.
 *  
 *  
 *  $
 */
#include <stdlib.h>
#include "exc_platform.h"
#include "ompclib.h"

extern _ompc_lock_t _ompc_thread_lock;
extern struct ompc_thread *_ompc_current_thread(void);
void _ompc_static_bsched_tid(struct ompc_thread *tp, 
			     int *lb, int *ub, int *step);
void _ompc_static_csched_tid(struct ompc_thread *tp, 
			     int *lb, int *up, int *step);
void _ompc_static_sched_init_tid(struct ompc_thread *tp,
		       int lb, int up, int step, int chunk_size);
void _ompc_dynamic_sched_init_tid(struct ompc_thread *tp,
		       int lb, int up, int step, int chunk_size);
int _ompc_static_sched_next_tid(struct ompc_thread *tp, int *lb,int *ub);
int _ompc_dynamic_sched_next_tid(struct ompc_thread *tp, int *lb,int *ub,int guided);
int _ompc_do_single_tid(struct ompc_thread *tp);

void _ompc_reduction(void *in_p,void *out_p,int type, int op);

/*
 * compiler runtime
 */

int _ompc_thread_num_tid(struct ompc_thread *tp)
{
    return tp->num;
}

void _ompc_barrier()
{
    struct ompc_thread *tp;

    tp = _ompc_current_thread();

/*    printf("id[%d] of team[%d]\n", tp->num, tp->num_thds);*/
/*    if ( tp->parent != NULL )
 *   printf("\t parent id[%d] num[%d]\n", tp->parent->num, tp->parent->num_thds);*/
    _ompc_thread_barrier(tp->num, tp->parent);
}

void _ompc_barrier_tid(struct ompc_thread *tp)
{
    _ompc_thread_barrier(tp->num, tp->parent);
}

void _ompc_default_sched(int *lb, int *ub, int *step)
{
    struct ompc_thread *tp = _ompc_current_thread();
#ifdef DEBUG
    if(_ompc_debug_flag)
	printf("default_sched: +id=%d, lb=%d, ub=%d, step=%d\n",
	       tp->num,*lb,*ub,*step);
#endif
    _ompc_static_bsched_tid(tp, lb, ub, step);
#ifdef DEBUG
    if(_ompc_debug_flag)
	printf("default_sched: -id=%d, lb=%d, ub=%d, step=%d\n",
	       tp->num,*lb,*ub,*step); 
#endif
}

/* static scheduling: cyclic */
void _ompc_static_csched(int *lb, int *up, int *step)
{
    struct ompc_thread *tp = _ompc_current_thread();
    _ompc_static_csched_tid(tp, lb, up, step);
}

void _ompc_static_csched_tid(struct ompc_thread *tp, int *lb, int *up, int *step)
{
  int n,s, n_thd, id;

    if ((n_thd = _ompc_get_num_threads (tp)) == 1) { /* not in parallel, do nothing */
      return;
    }

#ifdef USE_LOG
    if(_ompc_log_flag) tlog_loop_init_EVENT(tp->num);
#endif

    id = tp->num;
    tp->is_last = 0;
    s = *step;

    /* how many iteration */
    if(s > 0) n = (*up-*lb+s-1)/s;
    else n = (*up-*lb+s+1)/s;

    *lb += id*s;   /* adjust low bound */
    *step = s*n_thd;
    if(n > 0 && ((n-1)%n_thd) == id) tp->is_last = 1;
}

/* static scheduling: block */
void _ompc_static_bsched(int *lb, int *ub, int *step)
{
    struct ompc_thread *tp = _ompc_current_thread();
    _ompc_static_bsched_tid(tp, lb, ub, step);
}

void _ompc_static_bsched_tid(struct ompc_thread *tp, int *lb, int *ub, int *step)
{
    int n_thd, id;
    int b,e,ee,s,blk_s;

    if ((n_thd = _ompc_get_num_threads (tp)) == 1) { /* not in parallel, do nothing */
      return;
    }

#ifdef USE_LOG
    if(_ompc_log_flag) tlog_loop_init_EVENT(tp->num);
#endif

    s = *step;
    b = *lb;
    ee = e = *ub;
    id = tp->num;
    tp->is_last = 0;
    if(s > 0){
	blk_s = (e-b+n_thd-1)/n_thd;
	blk_s = ((blk_s+s-1)/s)*s;
	b += blk_s*id;
	e = b + blk_s;
	if(e >= ee){
	    e = ee;
	    if(ee > b) tp->is_last = 1;
	}
    } else if(s < 0){
	blk_s = (e-b-n_thd+1)/n_thd;
	blk_s = ((blk_s+s+1)/s)*s;
	b += blk_s*id;
	e = b + blk_s;
	if(e <= ee){
	    e = ee;
	    if(ee < b) tp->is_last = 1;
	}
    } else return;

    *lb = b;
    *ub = e;
}

/*
 * static schedule
 */
void _ompc_static_sched_init(int lb, int up, int step, int chunk_size)
{
    struct ompc_thread *tp = _ompc_current_thread();
    _ompc_static_sched_init_tid(tp, lb, up, step, chunk_size);
}

void _ompc_static_sched_init_tid(struct ompc_thread *tp,
		       int lb, int up, int step, int chunk_size)
{
    int	nthds;


    if ((nthds = _ompc_get_num_threads (tp)) == 1) { /* not in parallel */
      tp->loop_sched_index = lb;
      tp->loop_end = up;
      return;	
    }

#ifdef USE_LOG
    if(_ompc_log_flag) tlog_loop_init_EVENT(tp->num);
#endif

    if(chunk_size <= 0){
	printf("check size is non-positive\n");
	_ompc_fatal("_ompc_static_sched_init");
    }

    chunk_size *= step;
    tp->loop_sched_index = lb+chunk_size*tp->num;
    tp->loop_chunk_size = chunk_size;
    tp->loop_stride = chunk_size * nthds;
    tp->loop_end = up;
    tp->is_last = 0;
/*    printf("begin=%d,end=%d,chunk_size=%d,stride=%d\n",
	   tp->loop_sched_index,tp->loop_end,tp->loop_chunk_size,tp->loop_stride);
*/
}

int _ompc_static_sched_next(int *lb, int *ub)
{
    struct ompc_thread *tp = _ompc_current_thread();
    return _ompc_static_sched_next_tid(tp, lb, ub);
}

int _ompc_static_sched_next_tid(struct ompc_thread *tp,
			    int *lb,int *ub)
{
    int b,e;

    b = tp->loop_sched_index;
    if (_ompc_get_num_threads (tp) == 1) { /* not in parallel */
	e = tp->loop_end;
	if(b == e) return FALSE;
	*lb = b;
	*ub = e;
	tp->loop_sched_index = e;
	return TRUE;
    }

#ifdef USE_LOG
    if(_ompc_log_flag) tlog_loop_next_EVENT(tp->num);
#endif

    tp->loop_sched_index += tp->loop_stride;
    e = b+tp->loop_chunk_size;
    if(tp->loop_chunk_size > 0){
	if(b >= tp->loop_end) return FALSE; 
	if(e >= tp->loop_end){
	    e = tp->loop_end;
	    tp->is_last = 1;
	}
    } else {
	if(b <= tp->loop_end) return FALSE;
	if(e <= tp->loop_end){
	    e = tp->loop_end;
	    tp->is_last = 1;
	}
    }
    *lb = b;
    *ub = e;
    return TRUE;
}

/* 
 * dynamic schedule
 */
void _ompc_dynamic_sched_init(int lb, int up, int step, int chunk_size)
{
    struct ompc_thread *tp = _ompc_current_thread();
    _ompc_dynamic_sched_init_tid(tp, lb, up, step, chunk_size);
}


void _ompc_dynamic_sched_init_tid(struct ompc_thread *tp,
		       int lb, int up, int step, int chunk_size)
{
    struct ompc_thread *tpp;
    int id;

    if (_ompc_get_num_threads (tp) == 1) { /* not in parallel */
	tp->loop_sched_index = lb;
	tp->loop_end = up;
	return;	/* stride is not used */
    }
#ifdef USE_LOG
    if(_ompc_log_flag) tlog_loop_init_EVENT(tp->num);
#endif
    if(chunk_size <= 0){
	printf("check size is non-positive\n");
	_ompc_fatal("_ompc_dynamic_sched_init");
    }
    tp->loop_chunk_size = chunk_size*step;
    tp->loop_end = up;
    tp->loop_sched_index = lb;
    tp->is_last = 0;

    id  = tp->num;
    tpp = tp->parent;
    _OMPC_WAIT((volatile int)tpp->in_flags[id]._v);
    _ompc_lock(&_ompc_thread_lock);
    if(tpp->in_count == 0) tpp->dynamic_index = lb;
    tpp->in_count++;
    _ompc_unlock(&_ompc_thread_lock);
    tpp->in_flags[id]._v = 1;
}

int _ompc_dynamic_sched_next(int *lb, int *ub)
{
    struct ompc_thread *tp = _ompc_current_thread();
    return _ompc_dynamic_sched_next_tid(tp, lb, ub,FALSE);
}

int _ompc_dynamic_sched_next_tid(struct ompc_thread *tp,
			    int *lb,int *ub,int guided)
{
    struct ompc_thread *tpp;
    int id,exit_flag;
    int b,e,l,c;

    if (_ompc_get_num_threads (tp) == 1) { /* not in parallel */
	b = tp->loop_sched_index;
	e = tp->loop_end;
	if(b == e) return FALSE;
	*lb = b;
	*ub = e;
	tp->loop_sched_index = e;
	return TRUE;
    } 
#ifdef USE_LOG
    if(_ompc_log_flag) tlog_loop_next_EVENT(tp->num);
#endif
    c   = tp->loop_chunk_size;
    tpp = tp->parent;

    /* get my chunk, set b and e */
    if(guided){
	_ompc_lock(&_ompc_thread_lock);
	b = tpp->dynamic_index;
	l = (tp->loop_end - b)/tpp->num_thds;
	l = ((l+c)/c)*c;
	if(c > 0){
	    if(c > l) l = c;
	} else {
	    if(c < l) l = c;
	}
	e = b + l;
	tpp->dynamic_index = e;
	_ompc_unlock(&_ompc_thread_lock);
    } else {
	_ompc_lock(&_ompc_thread_lock);
	b = tpp->dynamic_index;
	e = b + c;
	tpp->dynamic_index = e;
	_ompc_unlock(&_ompc_thread_lock);
    }

    exit_flag = FALSE;
    if(c > 0){
	if(tp->loop_sched_index >= tp->loop_end) return FALSE;
	if(e >= tp->loop_end) exit_flag = TRUE;
    } else {
	if(tp->loop_sched_index <= tp->loop_end) return FALSE;
	if(e <= tp->loop_end) exit_flag = TRUE;
    }

    if(exit_flag){
	_ompc_lock(&_ompc_thread_lock);
	tpp->out_count++;
	if(tpp->out_count == tpp->num_thds){   /* all thread exit */
	    tpp->out_count = 0;
	    tpp->in_count = 0;
	    for(id = 0; id < tpp->num_thds; id++)
	      tpp->in_flags[id]._v = 0;
	    MBAR();
	}
	_ompc_unlock(&_ompc_thread_lock);
    }
    tp->loop_sched_index = e;

    /* adjust the last iteration */
    if(c > 0){
	if(b >= tp->loop_end) return FALSE; 
	if(e >= tp->loop_end){
	    e = tp->loop_end;
	    tp->is_last = 1;
	}
    } else {
	if(b <= tp->loop_end) return FALSE;
	if(e <= tp->loop_end){
	    e = tp->loop_end;
	    tp->is_last = 1;
	}
    }
    *lb = b;
    *ub = e;
    return TRUE;
}

/* 
 * guided schedule
 */
void _ompc_guided_sched_init(int lb, int up, int step, int chunk_size)
{
    struct ompc_thread *tp = _ompc_current_thread();
    _ompc_dynamic_sched_init_tid(tp, lb, up, step, chunk_size);
}

int _ompc_guided_sched_next(int *lb, int *ub)
{
    struct ompc_thread *tp = _ompc_current_thread();
    return _ompc_dynamic_sched_next_tid(tp, lb, ub, TRUE);
}


/* 
 * runtime schedule 
 */
static enum { SCHED_NONE = 0, SCHED_STATIC, SCHED_DYNAMIC, SCHED_GUIDED }
_ompc_runtime_sched_kind;
static int _ompc_runtime_chunk_size = 0; /* default */

void _ompc_set_runtime_schedule(char *s)
{
    char *cp;
    cp = s;
    while(isspace((int)*cp)) cp++;
    if(*cp == 0) return;
    if(strncmp(cp,"static",6) == 0){
	cp += 6;
	_ompc_runtime_sched_kind = SCHED_STATIC;
    } else if(strncmp(cp,"dynamic",7) == 0){
	cp += 7;
	_ompc_runtime_sched_kind = SCHED_DYNAMIC;
    } else if(strncmp(cp,"guided",6) == 0){
	cp += 6;
	_ompc_runtime_sched_kind = SCHED_GUIDED;
    }
    while(isspace((int)*cp)) cp++;
    if(*cp == 0) return;
    if(*cp != ',') goto err;
    cp++;
    while(isspace((int)*cp)) cp++;
    if(!isdigit((int)*cp)) goto err;
    sscanf(cp,"%d",&_ompc_runtime_chunk_size);
    if(_ompc_runtime_chunk_size <= 0){
	_ompc_runtime_sched_kind = SCHED_NONE;
	goto err;
    }
    return;
err:
    fprintf(stderr,"OMP_SCHEDULE ='%s'",s);
    _ompc_fatal("bad OMP_SCHEDULE");
}

void _ompc_runtime_sched_init(int lb, int up, int step)
{
    struct ompc_thread *tp = _ompc_current_thread();
    int chunk_size,n_thd;

    chunk_size = _ompc_runtime_chunk_size;
    switch(_ompc_runtime_sched_kind){
    case SCHED_DYNAMIC:
    case SCHED_GUIDED:
	if(chunk_size <= 0) chunk_size = 1;
	_ompc_dynamic_sched_init_tid(tp, lb, up, step, chunk_size);
	break;
    case SCHED_STATIC:
    case SCHED_NONE:
    default:
        n_thd = _ompc_get_num_threads (tp);
	if(chunk_size <= 0){
	    chunk_size = (up - lb)/(step*n_thd) + (((up - lb)%(step*n_thd))?(1):(0));
	    if(chunk_size <= 0) chunk_size = 1;
	}
	_ompc_static_sched_init_tid(tp, lb, up, step, chunk_size);
	break;
    }
}

int _ompc_runtime_sched_next(int *lb, int *ub)
{
    struct ompc_thread *tp = _ompc_current_thread();

    switch(_ompc_runtime_sched_kind){
    case SCHED_DYNAMIC:
	return _ompc_dynamic_sched_next_tid(tp, lb, ub,FALSE);
    case SCHED_GUIDED:
	return _ompc_dynamic_sched_next_tid(tp, lb, ub, TRUE);
    case SCHED_STATIC:
    case SCHED_NONE:
    default:
	return _ompc_static_sched_next_tid(tp, lb, ub);
    }
}

/* 
 * ordered
 */
void _ompc_set_loop_id(int i)
{
    struct ompc_thread *tp = _ompc_current_thread();
    tp->loop_id = i;
}

void _ompc_init_ordered(int lb,int step)
{
    int n,id;
    struct ompc_thread *tpp;
    struct ompc_thread *tp = _ompc_current_thread();

    if ((n = _ompc_get_num_threads (tp)) == 1) { /* not in parallel, do nothing */
      return;
    }
    tpp = tp->parent;
    
    id = tp->num;
    _OMPC_WAIT((volatile int)tpp->in_flags[id]._v);
    _ompc_lock(&_ompc_thread_lock);
    if(tpp->out_count == 0){ /* first visitor execute it */
	tpp->ordered_id = lb;
	tpp->ordered_step = step;
    }
    tpp->out_count++;
    if(tpp->out_count == n){
	/* if all threads comes, clear flags */
	for(id = 0; id < n; id++) tpp->in_flags[id]._v = 0;
	tpp->out_count = 0;
    } else tpp->in_flags[id]._v = 1;
    _ompc_unlock(&_ompc_thread_lock);
}

void _ompc_ordered_begin()
{
    struct ompc_thread *tpp;
    struct ompc_thread *tp = _ompc_current_thread();
    
    if (_ompc_get_num_threads (tp) == 1) { /* serialized */
      return;
    }
    tpp = tp->parent;
    _OMPC_WAIT((volatile int)tp->loop_id != (volatile int)tpp->ordered_id);
}

void _ompc_ordered_end()
{
    struct ompc_thread *tpp;
    struct ompc_thread *tp = _ompc_current_thread();

    if (_ompc_get_num_threads (tp) == 1) { /* serialized */
      return;
    }
    tpp = tp->parent;
    tpp->ordered_id += tpp->ordered_step;
    MBAR();
}

/*
 * sections directives. section_id is allocated in round-robin manner.
 */
void _ompc_section_init(int n_sections)
{
    struct ompc_thread *tp;
    tp = _ompc_current_thread();
    tp->section_id = tp->num;
    tp->last_section_id = n_sections - 1;
    tp->is_last = 0;
}

int _ompc_section_id()
{
    struct ompc_thread *tp;
    int id;

    tp = _ompc_current_thread();
#ifdef USE_LOG
    if(_ompc_log_flag) tlog_section_EVENT(tp->num);
#endif
    id = tp->section_id;
    tp->section_id += _ompc_get_num_threads (tp);

    if (id == tp->last_section_id) {
      tp->is_last = 1;
    }
    return id;
}

int _ompc_is_last()
{
    struct ompc_thread *tp;
    tp = _ompc_current_thread();
    return tp->is_last || (_ompc_get_num_threads (tp) == 1);
}

/*
 * single construct
 */
int _ompc_do_single()
{
    return _ompc_do_single_tid(_ompc_current_thread());
}

int _ompc_do_single_tid(struct ompc_thread *tp)
{
    struct ompc_thread *tpp;
    int n,id;
    int ret = 0;

    if ((n = _ompc_get_num_threads (tp)) == 1) { /* not in parallel */
      return 1;
    }

#ifdef USE_LOG
    if(_ompc_log_flag) tlog_single_EVENT(tp->num);
#endif

    id  = tp->num;
    tpp = tp->parent;
    _OMPC_WAIT((volatile int)tpp->in_flags[id]._v);
    _ompc_lock(&_ompc_thread_lock);
    if(tpp->out_count == 0) ret = 1;	/* first visitor execute it */
    tpp->out_count++;
    if(tpp->out_count == n){
	/* if all threads comes, clear flags */
	for(id = 0; id < n; id++) tpp->in_flags[id]._v = 0;
	tpp->out_count = 0;
    } else tpp->in_flags[id]._v = 1;
    _ompc_unlock(&_ompc_thread_lock);
    return ret;
}

int _ompc_is_master()
{
    struct ompc_thread *tp;
    tp = _ompc_current_thread();
    return tp->num == 0;
    /* return omp_get_thread_num() == 0; */
}

int _ompc_is_master_tid(struct ompc_thread *tp)
{
    return tp->num == 0;
}



static _ompc_lock_t	_critical_lock;

void _ompc_critical_init ()
{
    _ompc_init_lock (&_critical_lock);
}

void _ompc_critical_destroy ()
{
    _ompc_destroy_lock (&_critical_lock);
}

void _ompc_enter_critical(_ompc_lock_t **p)
{
#ifdef USE_LOG
    if(_ompc_log_flag) tlog_critial_IN(_ompc_current_thread()->num);
#endif
    if (*p == NULL) {
	_ompc_lock (&_critical_lock);
	if ((_ompc_lock_t volatile *)*p == NULL) {
	    if((*p = (_ompc_lock_t *)malloc(sizeof(_ompc_lock_t))) == NULL) {
		_ompc_fatal("cannot allocate lock memory");
	    }
	    _ompc_init_lock (*p);
	}
	_ompc_unlock (&_critical_lock);
    }
    _ompc_lock((_ompc_lock_t volatile *)*p);
}

void _ompc_exit_critical(_ompc_lock_t **p)
{
    _ompc_unlock(*p);
#ifdef USE_LOG
    if(_ompc_log_flag) tlog_critial_OUT(_ompc_current_thread()->num);
#endif
}

static _ompc_lock_t _atomic_lock;

void _ompc_atomic_init_lock ()
{
    _ompc_init_lock (&_atomic_lock);
}

void _ompc_atomic_lock()
{
    _ompc_lock(&_atomic_lock);
}

void _ompc_atomic_unlock()
{
    _ompc_unlock(&_atomic_lock);
}

void _ompc_atomic_destroy_lock ()
{
    _ompc_destroy_lock (&_atomic_lock);
}

void _ompc_bcopy(char *dst,char *src,int nbyte)
{
    bcopy(src,dst,nbyte);
}

void _ompc_flush(char *dst,int nbyte)
{
    MBAR();
}

void * _ompc_get_thdprv(void ***thdprv_p,int size,void *datap)
{
    void **pp,*p;
    struct ompc_thread *tp;
    tp = _ompc_current_thread();
    /* printf("_ompc_get_thdprv... %d, size=%d\n",tp->num,size); */
    if((pp = *thdprv_p) == NULL){
	_ompc_lock(&_ompc_thread_lock);
	if((pp = *thdprv_p) == NULL){
	    pp = (void *)malloc(sizeof(void *)*_ompc_max_threads);
	    bzero(pp,sizeof(void *)*_ompc_max_threads);
	    if(pp == NULL) _ompc_fatal("cannot allocate memory");
	    *thdprv_p = pp;
	}
	_ompc_unlock(&_ompc_thread_lock);
    }
    if((p = pp[tp->num]) == NULL){
	if(tp->num == 0) p = datap;
	else {
	    p = (void *)malloc(size);
	}
	pp[tp->num] = p;
    }
    return p;
}

void _ompc_copyin_thdprv(void *datap,void *global_datap,int size)
{
    if(global_datap != datap) bcopy(global_datap,datap,size);
    _ompc_barrier();
}

/* 
 * reduction operation
 */
#define DO_REDUCTION_INTEGRAL(type_t,t) {\
  vals[id].r_v.t = *((type_t *)in_p); \
  if(tpp != NULL) _ompc_thread_barrier(id,tpp); \
  if(id == 0){ \
      any_type v; int i; \
      v.t = *((type_t *)out_p); \
      switch(op){ \
      case OMPC_REDUCTION_PLUS: \
      case OMPC_REDUCTION_MINUS: \
	for(i = 0; i < n_thd; i++) v.t += vals[i].r_v.t;\
	break; \
      case OMPC_REDUCTION_MUL: \
	for(i = 0; i < n_thd; i++) v.t *= vals[i].r_v.t;\
	break; \
      case OMPC_REDUCTION_BITAND: \
	for(i = 0; i < n_thd; i++) v.t &= vals[i].r_v.t;\
	break; \
      case OMPC_REDUCTION_BITOR: \
	for(i = 0; i < n_thd; i++) v.t |= vals[i].r_v.t;\
	break; \
      case OMPC_REDUCTION_BITXOR: \
	for(i = 0; i < n_thd; i++) v.t ^= vals[i].r_v.t;\
	break; \
      case OMPC_REDUCTION_LOGAND: \
	if(!v.t) break; \
	for(i = 0; i < n_thd; i++) \
          if(!vals[i].r_v.t) { v.t = 0; break; } \
	break; \
      case OMPC_REDUCTION_LOGOR: \
	if(v.t) break; \
	for(i = 0; i < n_thd; i++) \
          if(vals[i].r_v.t) { v.t = 1; break; } \
	break; \
      case OMPC_REDUCTION_MIN: \
	for(i = 0; i < n_thd; i++) \
         if(v.t>vals[i].r_v.t) v.t = vals[i].r_v.t;\
	break; \
      case OMPC_REDUCTION_MAX: \
	for(i = 0; i < n_thd; i++) \
         if(v.t<vals[i].r_v.t) v.t = vals[i].r_v.t;\
	break; \
      default: \
	  _ompc_fatal("_ompc_reduction: bad op\n"); \
      } \
      *((type_t *)out_p) = v.t; \
   } \
  if(tpp != NULL) _ompc_thread_barrier(id,tpp); \
  }

#define DO_REDUCTION_FLOAT(type_t,t) { \
  vals[id].r_v.t = *((type_t *)in_p); \
  if(tpp != NULL) _ompc_thread_barrier(id,tpp); \
  if(id == 0){ \
      any_type v; int i; \
      v.t = *((type_t *)out_p); \
      switch(op){ \
      case OMPC_REDUCTION_PLUS: \
      case OMPC_REDUCTION_MINUS: \
	for(i = 0; i < n_thd; i++) v.t += vals[i].r_v.t;\
	break; \
      case OMPC_REDUCTION_MUL: \
	for(i = 0; i < n_thd; i++) v.t *= vals[i].r_v.t;\
	break; \
      case OMPC_REDUCTION_LOGAND: \
	if(!v.t) break; \
	for(i = 0; i < n_thd; i++) \
          if(!vals[i].r_v.t) { v.t = 0; break; } \
	break; \
      case OMPC_REDUCTION_LOGOR: \
	if(v.t) break; \
	for(i = 0; i < n_thd; i++) \
          if(vals[i].r_v.t) { v.t = 1; break; } \
	break; \
      case OMPC_REDUCTION_MIN: \
	for(i = 0; i < n_thd; i++) \
         if(v.t > vals[i].r_v.t) v.t=vals[i].r_v.t;\
	break; \
      case OMPC_REDUCTION_MAX: \
	for(i = 0; i < n_thd; i++) \
         if(v.t < vals[i].r_v.t) v.t=vals[i].r_v.t;\
	break; \
      default: \
	  _ompc_fatal("_ompc_reduction: bad op\n"); \
      } \
      *((type_t *)out_p) = v.t; \
   } \
  if(tpp != NULL) _ompc_thread_barrier(id,tpp); \
 }

#define DO_REDUCTION_COMPLEX(type_t,t) { \
  vals[id].r_v.t = *((type_t *)in_p); \
  if(tpp != NULL) _ompc_thread_barrier(id,tpp); \
  if(id == 0){ \
      any_type v,vv; int i; \
      v.t = *((type_t *)out_p); \
      switch(op){ \
      case OMPC_REDUCTION_PLUS: \
      case OMPC_REDUCTION_MINUS: \
	for(i = 0; i < n_thd; i++)\
	  { v.t.re += vals[i].r_v.t.re; v.t.im += vals[i].r_v.t.im;} \
	break; \
      case OMPC_REDUCTION_MUL: \
	for(i = 0; i < n_thd; i++){ \
 	    vv.t.re = v.t.re*vals[i].r_v.t.re-v.t.im*vals[i].r_v.t.im; \
	    vv.t.im = v.t.re*vals[i].r_v.t.im+v.t.im*vals[i].r_v.t.re;\
            v.t.re = vv.t.re; v.t.im = vv.t.im; }\
	break; \
      default: \
	  _ompc_fatal("_ompc_reduction: bad op\n"); \
      } \
      *((type_t *)out_p) = v.t; \
   } \
  if(tpp != NULL) _ompc_thread_barrier(id,tpp); \
 }

void _ompc_reduction(void *in_p,void *out_p,int type, int op)
{
    struct ompc_thread *tp = _ompc_current_thread();
    struct ompc_thread *tpp = tp->parent;
    struct barrier_flag volatile *vals;
    int id,n_thd;

    if((n_thd = _ompc_get_num_threads (tp)) == 1) {
	id = 0;
	vals = tp->barrier_flags;
    } else {
	id = tp->num;
	vals = tpp->barrier_flags;
    }

    switch(type){
    case OMPC_REDUCTION_CHAR:
	DO_REDUCTION_INTEGRAL(char,c);
	break;
    case OMPC_REDUCTION_UNSIGNED_CHAR:
	DO_REDUCTION_INTEGRAL(unsigned char,uc);
	break;

    case OMPC_REDUCTION_SHORT:
	DO_REDUCTION_INTEGRAL(short,s);
	break;
    case OMPC_REDUCTION_UNSIGNED_SHORT:
	DO_REDUCTION_INTEGRAL(unsigned short,us);
	break;

    case OMPC_REDUCTION_SIGNED:
    case OMPC_REDUCTION_INT:
	DO_REDUCTION_INTEGRAL(int, i);
	break;
    case OMPC_REDUCTION_UNSIGNED_INT:
	DO_REDUCTION_INTEGRAL(unsigned int, ui);
	break;

    case OMPC_REDUCTION_LONG:
	DO_REDUCTION_INTEGRAL(long,l);
	break;
    case OMPC_REDUCTION_UNSIGNED_LONG:
	DO_REDUCTION_INTEGRAL(unsigned long,ul);
	break;

    case OMPC_REDUCTION_LONGLONG:
	DO_REDUCTION_INTEGRAL(long long,ll);
        break;

    case OMPC_REDUCTION_UNSIGNED_LONGLONG:
	DO_REDUCTION_INTEGRAL(unsigned long long,ull);
        break;

    case OMPC_REDUCTION_FLOAT:
	DO_REDUCTION_FLOAT(float,f);
	break;

    case OMPC_REDUCTION_DOUBLE:
	DO_REDUCTION_FLOAT(double,d);
	break;

    case OMPC_REDUCTION_COMPLEX_FLOAT:
	DO_REDUCTION_COMPLEX(ompc_complex_float,cf);
	break;
	
    case OMPC_REDUCTION_COMPLEX_DOUBLE:
	DO_REDUCTION_COMPLEX(ompc_complex_double,cd);
	break;

    case OMPC_REDUCTION_LONG_DOUBLE:
    default:
	_ompc_fatal("_ompc_reduction: bad type");
    }
}

void _ompc_reduction_init(void *in_p, int type, int op)
{
    any_type *p;
    p = (any_type *)in_p;

    switch(type){
    case OMPC_REDUCTION_COMPLEX_FLOAT:
	switch(op){
	case OMPC_REDUCTION_PLUS:
	case OMPC_REDUCTION_MINUS:
	    p->cf.re = 0.0;
	    p->cf.im = 0.0;
	    return;
	case OMPC_REDUCTION_MUL: 
	    p->cf.re = 1.0;
	    p->cf.im = 0.0;
	    return;
	}
	break;
    case OMPC_REDUCTION_COMPLEX_DOUBLE:
	switch(op){
	case OMPC_REDUCTION_PLUS:
	case OMPC_REDUCTION_MINUS:
	    p->cd.re = 0.0;
	    p->cd.im = 0.0;
	    return;
	case OMPC_REDUCTION_MUL: 
	    p->cd.re = 1.0;
	    p->cd.im = 0.0;
	    return;
	}
	break;
    }
    _ompc_fatal("_ompc_reduction_init: bad op\n");
}



