/*

 \brief This file is for testing all pieces and all steps

 \authors Boyana Norris, Beata Winnicka, Paul Hovland
 \version $Id: ad_grad.h 286 2006-06-23 15:44:31Z norris $

 Copyright (c) 2006, Argonne National Laboratory <br>
 All rights reserved. <br>
 See $ADIC_DIR/share/ADIC_Copyright.txt for details. <br>

 */
#ifndef __AD_GRAD_H
#define __AD_GRAD_H

//Ensure C linkage
#ifdef __cplusplus
extern "C" {
#endif

/**
 * --------------------------------------------------------------
 * AD utility and preaccumulation macros and function prototypes
 * --------------------------------------------------------------
 * The macros and functions in this header can be implemented in
 * different ways, e.g., to provide dynamic memory allocation for
 * the gradient vectors, as long as all the macro/function
 * prototypes are implemented.
 */

/* ADIC initialization and cleanup; prototypes:
 * void ADIC_Init();
 * void ADIC_Finalize();
 *
 * These macros or functions are used to initialize and cleanup,
 * respectively, internal global variables used by ADIC, e.g.,
 * the number of independent variables. The maximum number of
 * independent variables is specified with the macro
 * ADIC_GRADVEC_LENGTH (see ad_types.h)
 */
void
ADIC_Init(void);

void
ADIC_Finalize(void);

void
ADIC_SetForwardMode(void);

void
ADIC_SetReverseMode(void);

int
ADIC_GetMode(void);

/**
 * Internal ADIC macros and functions, do not use in directly.
 */
int
__ADIC_IncrShadowVar(void);
void
__ADIC_CommitShadowVar(void);
void
__ADIC_ResetShadowVar(void);
int
__ADIC_GradSize(void);



#define __ADIC_Validate(g)

/* Setting independent and dependent variables
 * Prototype: void ADIC_SetIndep(DERIV_TYPE var);
 */
#define ADIC_SetIndep(var)                                              \
  {                                                                     \
    int __adic_pos = __ADIC_IncrShadowVar();                            \
    if (__adic_pos > ADIC_GRADVEC_LENGTH) {                             \
      fprintf(stderr, "ADIC_SetIndep() Error: the number of independent " \
              "variables %d exceeds the max (ADIC_GRADVEC_LENGTH=%d)!\n", \
              __adic_pos, ADIC_GRADVEC_LENGTH);                         \
      fflush(stderr);                                                   \
      abort();                                                          \
    }                                                                   \
   int mode = ADIC_GetMode();                                          \
    if(mode==0) {                                                  \
      __ADIC_SetGradVal(&var, 1.0, __adic_pos);                         \
    } else if(mode==1) {                                             \
      __ADIC_SetGradVal(&var, 0.0, __adic_pos);                         \
    } else                                                                \
      fprintf(stderr, "ADIC_SetIndep() Error: Differentiation Mode not set.\n");\
  }

/* Setting independent and dependent 1-D array variables
 * Prototype: void ADIC_SetIndepArray(DERIV_TYPE arrayvar[], arraysize);
 */
#define ADIC_SetIndepArray(arrayvar, arraysize)                         \
  {                                                                     \
    int __adic_iCtr;                                                    \
    for (__adic_iCtr = 0; __adic_iCtr < arraysize; __adic_iCtr++) {     \
      ADIC_SetIndep((arrayvar)[__adic_iCtr]);                           \
    }                                                                   \
  }

/* Setting independent and dependent variables
 * Prototype: void ADIC_SetIndep(DERIV_TYPE var);
 */

#define ADIC_SetDep(var)                                              \
  {                                                                     \
    int __adic_pos = -1;                            \
    int mode = ADIC_GetMode();                                          \
   if(mode==0) {                                                  \
     __ADIC_SetGradVal(&var, 0.0, __adic_pos);                         \
   } else if(mode==1) {                                             \
     __ADIC_SetGradVal(&var, 1.0, __adic_pos);                         \
   } else                                                                \
     fprintf(stderr, "ADIC_SetDep() Error: Differentiation Mode not set.\n");\
  }

/* Setting independent and dependent variables
 * Prototype: void ADIC_SetIndep(DERIV_TYPE var);
 */
/*
#define ADIC_SetDep(var)                                              \
  {                                                                     \
    int __adic_pos = __ADIC_IncrShadowVar();                            \
    if (__adic_pos > ADIC_GRADVEC_LENGTH) {                             \
      fprintf(stderr, "ADIC_SetIndep() Error: the number of independent " \
              "variables %d exceeds the max (ADIC_GRADVEC_LENGTH=%d)!\n", \
              __adic_pos, ADIC_GRADVEC_LENGTH);                         \
      fflush(stderr);                                                   \
      abort();                                                          \
    }                                                                   \
   int mode = ADIC_GetMode();                                          \
   if(mode==0) {                                                  \
     __ADIC_SetGradVal(&var, 0.0, __adic_pos);                         \
   } else if(mode==1) {                                             \
     __ADIC_SetGradVal(&var, 1.0, __adic_pos);                         \
   } else                                                                \
     fprintf(stderr, "ADIC_SetDep() Error: Differentiation Mode not set.\n");\
  }
*/
/* Setting independent and dependent 1-D array variables
 * Prototype: void ADIC_SetIndepArray(DERIV_TYPE arrayvar[], arraysize);
 */
#define ADIC_SetDepArray(arrayvar, arraysize)                         \
  {                                                                     \
    int __adic_iCtr;                                                    \
    for (__adic_iCtr = 0; __adic_iCtr < arraysize; __adic_iCtr++) {     \
      ADIC_SetDep((arrayvar)[__adic_iCtr]);                           \
    }                                                                   \
  }

#define ADIC_SetIndepDone() __ADIC_CommitShadowVar()
#define ADIC_ResetIndep()   __ADIC_ResetShadowVar()

/* Zero the value of the gradient g.
 * Prototype: void ADIC_ZeroDeriv(DERIV_TYPE *__adic_tgt);
 */
#define ADIC_ZeroDeriv(__adic_tgt)                                      \
  { int __adic_iCtr; double *__adic_gradz = DERIV_grad(__adic_tgt);  \
    for (__adic_iCtr = 0 ; __adic_iCtr < __ADIC_GradSize() ; __adic_iCtr++) { \
      __adic_gradz[__adic_iCtr] = 0.0;                                  \
    }                                                                   \
  }

/* One the value of the gradient g.
 * Prototype: void ADIC_OneDeriv(DERIV_TYPE *__adic_tgt);
 */
#define ADIC_OneDeriv(__adic_tgt)                                      \
  { int __adic_iCtr; double *__adic_gradz = DERIV_grad(__adic_tgt);  \
    for (__adic_iCtr = 0 ; __adic_iCtr < ADIC_GRADVEC_LENGTH ; __adic_iCtr++) { \
      __adic_gradz[__adic_iCtr] = 1.0;                                  \
    }                                                                   \
  }


/* Set the value of the gradient g
 * Prototype: void ADIC_SetDeriv(DERIV_TYPE *tgt, DERIV_TYPE *src);
 */
#define ADIC_SetDeriv(__adic_src, __adic_tgt)                           \
  { int __adic_iCtr; double *__adic_gradz = DERIV_grad(__adic_tgt),  \
                       *__adic_grads=DERIV_grad(__adic_src);         \
    for (__adic_iCtr = 0 ; __adic_iCtr < ADIC_GRADVEC_LENGTH ; __adic_iCtr++) { \
      __adic_gradz[__adic_iCtr] = __adic_grads[__adic_iCtr];            \
    }                                                                   \
  }

#define ADIC_SetNegDeriv(__adic_src, __adic_tgt)                           \
  { int __adic_iCtr; double *__adic_gradz = DERIV_grad(__adic_tgt),  \
                       *__adic_grads=DERIV_grad(__adic_src);         \
    for (__adic_iCtr = 0 ; __adic_iCtr < ADIC_GRADVEC_LENGTH ; __adic_iCtr++) { \
      __adic_gradz[__adic_iCtr] = -(__adic_grads[__adic_iCtr]);            \
    }                                                                   \
  }




  
#define ADIC_IncDeriv(__adic_src, __adic_tgt)                           \
  { int __adic_iCtr; double *__adic_gradz = DERIV_grad(__adic_tgt),  \
                       *__adic_grads=DERIV_grad(__adic_src);         \
    for (__adic_iCtr = 0 ; __adic_iCtr < ADIC_GRADVEC_LENGTH ; __adic_iCtr++) { \
      __adic_gradz[__adic_iCtr] = __adic_gradz[__adic_iCtr] + __adic_grads[__adic_iCtr];\
    }                                                                   \
  }

#define ADIC_DecDeriv(__adic_src, __adic_tgt)                           \
  { int __adic_iCtr; double *__adic_gradz = DERIV_grad(__adic_tgt),  \
                       *__adic_grads=DERIV_grad(__adic_src);         \
    for (__adic_iCtr = 0 ; __adic_iCtr < ADIC_GRADVEC_LENGTH ; __adic_iCtr++) { \
      __adic_gradz[__adic_iCtr] = __adic_gradz[__adic_iCtr] - __adic_grads[__adic_iCtr];\
    }                                                                   \
  }

#define ADIC_Sax(__adic_ca, __adic_src, __adic_tgt)                   \
  { int __adic_iCtr; double *__adic_gradz = DERIV_grad(__adic_tgt) , \
                       *__adic_grada = DERIV_grad(__adic_src);       \
    for (__adic_iCtr = 0; __adic_iCtr < ADIC_GRADVEC_LENGTH; __adic_iCtr++) { \
      __adic_gradz[__adic_iCtr] =  (__adic_ca)*__adic_grada[__adic_iCtr]; \
    }                                                                   \
  }

#define ADIC_Saxpy(__adic_ca, __adic_src, __adic_tgt)                   \
  { int __adic_iCtr; double *__adic_gradz = DERIV_grad(__adic_tgt) , \
                       *__adic_grada = DERIV_grad(__adic_src);       \
    for (__adic_iCtr = 0; __adic_iCtr < ADIC_GRADVEC_LENGTH; __adic_iCtr++) { \
      __adic_gradz[__adic_iCtr] +=  (__adic_ca)*__adic_grada[__adic_iCtr]; \
    }                                                                   \
  }

/* Extract the gradient array
 * Prototype: void ADIC_ExtractGrad(double *tgt, DERIV_TYPE src)
 */
#define ADIC_ExtractGrad(tgt, src)                                      \
  {                                                                     \
    int __adic_iCtr;                                                    \
    for (__adic_iCtr = 0; __adic_iCtr < __ADIC_GradSize(); __adic_iCtr++) { \
      tgt[__adic_iCtr] = DERIV_grad(src)[__adic_iCtr];                  \
    }                                                                   \
  }

/* Extract the value of a DERIV_TYPE variable (same as DERIV_val(src))
 * Prototype: void ADIC_ExtractVal(double *tgt, DERIV_TYPE src)
 */
#define ADIC_ExtractVal(tgt, src)                                      \
  {                                                                    \
    tgt = DERIV_val(src)                                               \
  }

/* Set the value of a DERIV_TYPE variable (same as DERIV_val(src))
 * Prototype: void ADIC_SetValue(DERIV_TYPE tgt, double val)
 */
#define ADIC_SetValue(tgt, val)                                      \
  {                                                                    \
    DERIV_val(tgt) = val;                                               \
  }

/* Set the value of one element of the gradient vector and set the
 * rest of the vector elements to zero.
 * Prototype: void ADIC_SetGradVal(DERIV_TYPE *var, double a, int position);
 */
#define __ADIC_SetGradVal(var, a, i)                                    \
  {                                                                     \
    int __adic_pos2;                                                    \
    __ADIC_Validate(var);                                               \
    for (__adic_pos2 = 0; __adic_pos2 < ADIC_GRADVEC_LENGTH; __adic_pos2++) { \
	    if (__adic_pos2 == i) {                                           \
        DERIV_grad(*var)[__adic_pos2] = a;                              \
      }                                                                 \
	    else {                                                            \
        DERIV_grad(*var)[__adic_pos2] = 0.0;                            \
      }                                                                 \
    }                                                                   \
  }

/* Set the value of one element of the gradient vector and set the
 * rest of the vector elements to zero.
 * Prototype: void ADIC_SetGradVal(DERIV_TYPE *var, double a, int position);
 */
#define ADIC_Initialize(var)                                    \
  {                                                        \
  }
  
#ifdef __cplusplus
} /* closing brace for extern "C" */
#endif
#endif /* #ifndef __AD_GRAD_H */
