// GetDP - Copyright (C) 1997-2022 P. Dular and C. Geuzaine, University of Liege
//
// See the LICENSE.txt file for license information. Please report all
// issues on https://gitlab.onelab.info/getdp/getdp/issues.

#include <string.h>
#include <math.h>
#include <map>
#include "GetDPConfig.h"
#include "ProData.h"
#include "DofData.h"
#include "F.h"
#include "Cal_Quantity.h"
#include "Cal_Value.h"
#include "MallocUtils.h"
#include "Message.h"
#include "ProParser.h"

#if defined(HAVE_KERNEL)
#include "GeoData.h"
#include "Get_Geometry.h"
#include "Pos_FemInterpolation.h"
#include "Pos_Search.h"
#include "Get_FunctionValue.h"
#include "Pos_Format.h"
#endif

extern struct Problem Problem_S;
extern struct CurrentData Current;

#if defined(HAVE_KERNEL)
extern int TreatmentStatus;
#else
const int TreatmentStatus = 0; // FIXME
#endif

/* ------------------------------------------------------------------------ */
/*  G e t _ V a l u e O f E x p r e s s i o n                               */
/* ------------------------------------------------------------------------ */

void Get_ValueOfExpression(struct Expression *Expression_P,
                           struct QuantityStorage *QuantityStorage_P0, double u,
                           double v, double w, struct Value *Value,
                           int NbrArguments, char *CallingExpressionName)
{
  static char *Flag_WarningUndefined = NULL;

  switch(Expression_P->Type) {
  case CONSTANT:
    if(Current.NbrHar == 1) { Value->Val[0] = Expression_P->Case.Constant; }
    else {
      for(int k = 0; k < Current.NbrHar; k += 2) {
        Value->Val[MAX_DIM * k] = Expression_P->Case.Constant;
        Value->Val[MAX_DIM * (k + 1)] = 0.;
      }
    }
    Value->Type = SCALAR;
    break;

  case WHOLEQUANTITY:
    Cal_WholeQuantity(
      Current.Element, QuantityStorage_P0, Expression_P->Case.WholeQuantity, u,
      v, w, -1, 0, Value, NbrArguments,
      CallingExpressionName ? CallingExpressionName : Expression_P->Name);
    break;

  case PIECEWISEFUNCTION:
    struct Expression *NextExpression_P;
    struct ExpressionPerRegion *ExpressionPerRegion_P;

    if(Current.Region == Expression_P->Case.PieceWiseFunction.NumLastRegion) {
      NextExpression_P =
        Expression_P->Case.PieceWiseFunction.ExpressionForLastRegion;
    }
    else {
      if((ExpressionPerRegion_P = (struct ExpressionPerRegion *)List_PQuery(
            Expression_P->Case.PieceWiseFunction.ExpressionPerRegion,
            &Current.Region, fcmp_int))) {
        Expression_P->Case.PieceWiseFunction.NumLastRegion = Current.Region;
        NextExpression_P =
          Expression_P->Case.PieceWiseFunction.ExpressionForLastRegion =
            (struct Expression *)List_Pointer(
              Problem_S.Expression, ExpressionPerRegion_P->ExpressionIndex);
      }
      else if(Expression_P->Case.PieceWiseFunction.ExpressionIndex_Default >=
              0) {
        NextExpression_P = (struct Expression *)List_Pointer(
          Problem_S.Expression,
          Expression_P->Case.PieceWiseFunction.ExpressionIndex_Default);
      }
      else {
        NextExpression_P = NULL;
        if(Current.Region == NO_REGION)
          Message::Error(
            "Function '%s' undefined in expressions without support",
            Expression_P->Name);
        else
          Message::Error("Function '%s' undefined in Region %d",
                         Expression_P->Name, Current.Region);
      }
    }

    Get_ValueOfExpression(NextExpression_P, QuantityStorage_P0, u, v, w, Value,
                          NbrArguments, Expression_P->Name);
    break;

  case PIECEWISEFUNCTION2:
    struct ExpressionPerRegion2 *ExpressionPerRegion2_P;
    int twoRegions[2];

    if(Current.Region ==
         Expression_P->Case.PieceWiseFunction2.NumLastRegion[0] &&
       Current.SubRegion ==
         Expression_P->Case.PieceWiseFunction2.NumLastRegion[1]) {
      NextExpression_P =
        Expression_P->Case.PieceWiseFunction2.ExpressionForLastRegion;
    }
    else {
      twoRegions[0] = Current.Region;
      twoRegions[1] = Current.SubRegion;
      if((ExpressionPerRegion2_P = (struct ExpressionPerRegion2 *)List_PQuery(
            Expression_P->Case.PieceWiseFunction2.ExpressionPerRegion,
            twoRegions, fcmp_Integer2))) {
        Expression_P->Case.PieceWiseFunction2.NumLastRegion[0] = Current.Region;
        Expression_P->Case.PieceWiseFunction2.NumLastRegion[1] =
          Current.SubRegion;
        NextExpression_P =
          Expression_P->Case.PieceWiseFunction2.ExpressionForLastRegion =
            (struct Expression *)List_Pointer(
              Problem_S.Expression, ExpressionPerRegion2_P->ExpressionIndex);
      }
      else if(Expression_P->Case.PieceWiseFunction.ExpressionIndex_Default >=
              0) {
        NextExpression_P = (struct Expression *)List_Pointer(
          Problem_S.Expression,
          Expression_P->Case.PieceWiseFunction.ExpressionIndex_Default);
      }
      else {
        NextExpression_P = NULL;
        if(Current.Region == NO_REGION)
          Message::Error(
            "Function '%s' undefined in expressions without support",
            Expression_P->Name);
        else
          Message::Error(
            "Function '%s' undefined in [ Region %d, SubRegion %d ]",
            Expression_P->Name, Current.Region, Current.SubRegion);
      }
    }

    Get_ValueOfExpression(NextExpression_P, QuantityStorage_P0, u, v, w, Value,
                          NbrArguments, Expression_P->Name);
    break;

  case UNDEFINED_EXP:
    if(!Flag_WarningUndefined ||
       strcmp(Flag_WarningUndefined, Expression_P->Name)) {
      Message::Warning("Undefined expression '%s' (assuming zero)",
                       Expression_P->Name);
      Flag_WarningUndefined = Expression_P->Name;
    }
    Cal_ZeroValue(Value);
    Value->Type = SCALAR;
    break;

  default:
    Message::Error("Unknown type (%d) of Expression (%s)", Expression_P->Type,
                   Expression_P->Name);
    break;
  }
}

/* ------------------------------------------------------------------------ */
/*  G e t _ V a l u e O f E x p r e s s i o n B y I n d e x                 */
/* ------------------------------------------------------------------------ */

void Get_ValueOfExpressionByIndex(int Index_Expression,
                                  struct QuantityStorage *QuantityStorage_P0,
                                  double u, double v, double w,
                                  struct Value *Value)
{
  Get_ValueOfExpression(
    (struct Expression *)List_Pointer(Problem_S.Expression, Index_Expression),
    QuantityStorage_P0, u, v, w, Value);
}

bool Is_ExpressionConstant(struct Expression *Expression_P)
{
  if(Expression_P->Type == CONSTANT) return true;
  if(Expression_P->Type == WHOLEQUANTITY) {
    for(int i = 0; i < List_Nbr(Expression_P->Case.WholeQuantity); i++) {
      struct WholeQuantity *WholeQuantity_P =
        (struct WholeQuantity *)List_Pointer(Expression_P->Case.WholeQuantity,
                                             i);
      if(WholeQuantity_P->Type != WQ_CONSTANT) return false;
    }
    return true;
  }
  return false;
}

/* ------------------------------------------------------------------------ */
/*  C a l _ S o l i d A n g l e                                             */
/* ------------------------------------------------------------------------ */

void Cal_SolidAngle(int Source, struct Element *Element,
                    struct QuantityStorage *QuantityStorage, int Nbr_Dof,
                    int Index, struct Value **Stack)
{
#if !defined(HAVE_KERNEL)
  Message::Error("Cal_SolidAngle requires Kernel");
#else
  struct Element *Elt;
  struct Geo_Element *GeoElement;
  struct Geo_Node *GeoNode1, *GeoNode2, *GeoNode3;

  double X, Y, Atan;
  int In, TypEnt, NumNode, NbrElements, *NumElements;
  int i, j;

  if(Nbr_Dof != QuantityStorage->NbrElementaryBasisFunction) {
    Message::Error("Uncompatible Quantity (%s) in SolidAngle computation",
                   QuantityStorage->DefineQuantity->Name);
    return;
  }

  if(Source) {
    Elt = Element->ElementSource;
    In = Current.SourceIntegrationSupportIndex;
  }
  else {
    Elt = Element;
    In = Current.IntegrationSupportIndex;
  }

  if(Elt->NumLastElementForSolidAngle == Elt->Num) {
    for(i = 0; i < Nbr_Dof; i++) {
      Stack[i][Index].Type = SCALAR;
      Stack[i][Index].Val[0] = Elt->angle[i];
    }
    return;
  }

  for(i = 0; i < Nbr_Dof; i++) {
    Stack[i][Index].Type = SCALAR;

    TypEnt = ((struct Group *)List_Pointer(
                Problem_S.Group,
                QuantityStorage->BasisFunction[i].BasisFunction->EntityIndex))
               ->FunctionType;

    if(TypEnt != NODESOF) {
      if(Elt->Type == LINE) { Stack[i][Index].Val[0] = M_PI; }
      else {
        Stack[i][Index].Val[0] = 2. * M_PI;
      }
    }

    else {
      NumNode =
        Elt->GeoElement
          ->NumNodes[QuantityStorage->BasisFunction[i].NumEntityInElement];

      Geo_CreateNodesXElements(NumNode, In, &NbrElements, &NumElements);

      if(NbrElements != 2) {
        Message::Error("SolidAngle not done for incidence != 2 (%d)",
                       NbrElements);
        return;
      }

      GeoNode2 = Geo_GetGeoNodeOfNum(NumNode);
      GeoElement = Geo_GetGeoElementOfNum(abs(NumElements[0]));

      if(GeoElement->Type != LINE) {
        Message::Error("SolidAngle not done for Elements other than LINE");
        return;
      }

      if(NumElements[0] > 0) {
        GeoNode1 = Geo_GetGeoNodeOfNum(GeoElement->NumNodes[0]);
        GeoElement = Geo_GetGeoElementOfNum(abs(NumElements[1]));
        GeoNode3 = Geo_GetGeoNodeOfNum(GeoElement->NumNodes[1]);
      }
      else {
        GeoNode3 = Geo_GetGeoNodeOfNum(GeoElement->NumNodes[1]);
        GeoElement = Geo_GetGeoElementOfNum(NumElements[1]);
        GeoNode1 = Geo_GetGeoNodeOfNum(GeoElement->NumNodes[0]);
      }

      Y = (GeoNode1->y - GeoNode2->y) * (GeoNode3->x - GeoNode2->x) -
          (GeoNode1->x - GeoNode2->x) * (GeoNode3->y - GeoNode2->y);
      X = (GeoNode1->x - GeoNode2->x) * (GeoNode3->x - GeoNode2->x) +
          (GeoNode1->y - GeoNode2->y) * (GeoNode3->y - GeoNode2->y);

      Atan = atan2(Y, X);

      Stack[i][Index].Val[0] = (Atan >= 0) ? Atan : (Atan + 2. * M_PI);

      if(Message::GetVerbosity() > 5) {
        printf("Solid angle=%g node=%d, region=%s, elms=",
               Stack[i][Index].Val[0] * 180 / M_PI, NumNode,
               ((struct Group *)List_Pointer(Problem_S.Group, In))->Name);
        for(j = 0; j < NbrElements; j++) printf("%d ", NumElements[j]);
        printf("\n");
      }
    }
  }

  if(Elt->NumLastElementForSolidAngle != Elt->Num) {
    Elt->NumLastElementForSolidAngle = Elt->Num;
    for(i = 0; i < Nbr_Dof; i++) Elt->angle[i] = Stack[i][Index].Val[0];
  }
#endif
}

/* ------------------------------------------------------------------------ */
/*  C a l _ W h o l e Q u a n t i t y                                       */
/* ------------------------------------------------------------------------ */

#define CAST3V void (*)(struct Value *, struct Value *, struct Value *)
#define CAST1V void (*)(struct Value *)
#define CASTF2V void (*)(struct Function *, struct Value *, struct Value *)

// There can be at max one "Dof{op qty}" per WholeQuantity, but as
// many {op qty} as you want.

static std::map<int, struct Value> ValueSaved;
static std::map<std::string, struct Value> NamedValueSaved;

void Cal_WholeQuantity(struct Element *Element,
                       struct QuantityStorage *QuantityStorage_P0,
                       List_T *WholeQuantity_L, double u, double v, double w,
                       int DofIndexInWholeQuantity, int Nbr_Dof,
                       struct Value DofValue[], int NbrArguments,
                       char *ExpressionName)
{
  static int Flag_WarningMissSolForDt = 0;
  static int Flag_WarningMissSolForTime_ntime = 0;
  static int Flag_InfoForTime_ntime = 0;

  int i_WQ, j, k, Flag_True, Index, DofIndex, Multi[MAX_STACK_SIZE];
  int Save_NbrHar, Save_Region, Type_Dimension, ntime;
  double Save_Time, Save_TimeImag, Save_TimeStep, X, Y, Z, Order;
  double Save_x, Save_y, Save_z;

  struct WholeQuantity *WholeQuantity_P0, *WholeQuantity_P;
  struct DofData *Save_DofData;
  struct Solution *Solution_P0, *Solution_PN;

  struct Element *Save_CurrentElement;

  // we could make this dynamic (with std::vector) to reduce stack usage, but
  // the performance hit is important

  // ==> forcing a reduced size of stack for MH case...
  // MAX_STACK_SIZE0 = 8 by default, 2 for MH
  // segmentation violation and out of memory with high number of harmonics
  // MAX_STACK_SIZE at least MAX_HAR_SIZE if harmonic function in formulation
  // term
  struct Value Stack[MAX_STACK_SIZE0][MAX_STACK_SIZE];

  WholeQuantity_P0 = (struct WholeQuantity *)List_Pointer(WholeQuantity_L, 0);

  Index = 0;
  DofIndex = -1;

  for(i_WQ = 0; i_WQ < List_Nbr(WholeQuantity_L); i_WQ++) {
    if(Index >= MAX_STACK_SIZE) {
      Message::Error("Stack size exceeded (%d>%d)", Index, MAX_STACK_SIZE);
      return;
    }

    WholeQuantity_P = WholeQuantity_P0 + i_WQ;

    switch(WholeQuantity_P->Type) {
    case WQ_OPERATORANDQUANTITY: /* {op qty}  Dof{op qty}  BF{op qty} */
      Save_Region = Current.Region;
      Save_CurrentElement = Current.Element;
      if(i_WQ != DofIndexInWholeQuantity) {
#if defined(HAVE_KERNEL)
        Pos_FemInterpolation(
          Element, QuantityStorage_P0,
          QuantityStorage_P0 + WholeQuantity_P->Case.OperatorAndQuantity.Index,
          WholeQuantity_P->Case.OperatorAndQuantity.TypeQuantity,
          WholeQuantity_P->Case.OperatorAndQuantity.TypeOperator, -1, 0, u, v,
          w, 0, 0, 0, Stack[0][Index].Val, &Stack[0][Index].Type, 1);
#else
        Message::Error("TODO Post_FemInterpolation");
#endif
        Multi[Index] = 0;
      }
      else {
        DofIndex = Index;
      }
      Index++;
      Current.Element = Save_CurrentElement;
      Current.Region = Save_Region;
      break;

    case WQ_ORDER: /* Order[{qty}] */
#if defined(HAVE_KERNEL)
      Order = Cal_InterpolationOrder(
        Element,
        QuantityStorage_P0 + WholeQuantity_P->Case.OperatorAndQuantity.Index);
#else
      Message::Error("TODO Cal_InterpolationOrder");
#endif
      for(k = 0; k < Current.NbrHar; k += 2) {
        Stack[0][Index].Val[MAX_DIM * k] = Order;
        Stack[0][Index].Val[MAX_DIM * (k + 1)] = 0.;
      }
      Stack[0][Index].Type = SCALAR;
      Multi[Index] = 0;
      Index++;
      break;

    case WQ_OPERATORANDQUANTITYEVAL:
      Save_Region = Current.Region;
      Save_CurrentElement = Current.Element;
      /* {op qty}[x,y,z], {op qty}[x,y,z,dimension]
         or {op qty}[Vector[x,y,x],dimension]
         or {op qty}[ntime] */
      if(i_WQ != DofIndexInWholeQuantity || TreatmentStatus == STATUS_POS) {
        j = WholeQuantity_P->Case.OperatorAndQuantity.NbrArguments;
        if(j == 2 || j == 3 || j == 4) {
          if(j == 3 || j == 4) {
            Index -= j;
            X = Stack[0][Index].Val[0];
            Y = Stack[0][Index + 1].Val[0];
            Z = Stack[0][Index + 2].Val[0];
            if(j == 4)
              Type_Dimension = (int)Stack[0][Index + 3].Val[0];
            else
              Type_Dimension = -1;
          }
          else { /* j==2 */
            Index -= j;
            X = Stack[0][Index].Val[0];
            Y = Stack[0][Index].Val[1];
            Z = Stack[0][Index].Val[2];
            Type_Dimension = (int)Stack[0][Index + 1].Val[0];
          }
#if defined(HAVE_KERNEL)
          Pos_FemInterpolation(
            Element, QuantityStorage_P0,
            QuantityStorage_P0 +
              WholeQuantity_P->Case.OperatorAndQuantity.Index,
            WholeQuantity_P->Case.OperatorAndQuantity.TypeQuantity,
            WholeQuantity_P->Case.OperatorAndQuantity.TypeOperator,
            Type_Dimension, 1, u, v, w, X, Y, Z, Stack[0][Index].Val,
            &Stack[0][Index].Type, 1);
#else
          Message::Error("TODO Post_FemInterpolation");
#endif
          Multi[Index] = 0;
          Index++;
        }
        else if(j == 1) {
          Index -= j;
          ntime = (int)Stack[0][Index].Val[0];

          for(k = 0; k < Current.NbrSystem; k++) {
            if(!List_Nbr((Current.DofData_P0 + k)->Solutions)) continue;
            Solution_P0 = (struct Solution *)List_Pointer(
              (Current.DofData_P0 + k)->Solutions, 0);
            if(((Current.DofData_P0 + k)->CurrentSolution - Solution_P0) >=
               ntime) {
              ((Current.DofData_P0 + k)->CurrentSolution) -= ntime;
              if(Flag_InfoForTime_ntime !=
                 List_Nbr((Current.DofData_P0 + k)->Solutions)) {
                Message::Debug("Accessing solution from %d time steps ago",
                               ntime);
                Message::Debug(
                  "  -> System %d/%d: TimeStep = %d, Time = %g + i * %g", k + 1,
                  Current.NbrSystem,
                  (Current.DofData_P0 + k)->CurrentSolution->TimeStep,
                  (Current.DofData_P0 + k)->CurrentSolution->Time,
                  (Current.DofData_P0 + k)->CurrentSolution->TimeImag);
                Flag_InfoForTime_ntime =
                  List_Nbr((Current.DofData_P0 + k)->Solutions);
              }
            }
            else {
              if(!Flag_WarningMissSolForTime_ntime) {
                Message::Warning("Missing solution for time step -%d "
                                 "computation (System #%d/%d)",
                                 ntime, k + 1, Current.NbrSystem);
                Flag_WarningMissSolForTime_ntime = 1;
              }
            }
          }

#if defined(HAVE_KERNEL)
          Pos_FemInterpolation(
            Element, QuantityStorage_P0,
            QuantityStorage_P0 +
              WholeQuantity_P->Case.OperatorAndQuantity.Index,
            WholeQuantity_P->Case.OperatorAndQuantity.TypeQuantity,
            WholeQuantity_P->Case.OperatorAndQuantity.TypeOperator, -1, 0, u, v,
            w, 0, 0, 0, Stack[0][Index].Val, &Stack[0][Index].Type, 1);
#else
          Message::Error("TODO Post_FemInterpolation");
#endif

          Multi[Index] = 0;
          Index++;

          for(k = 0; k < Current.NbrSystem; k++) {
            if(!List_Nbr((Current.DofData_P0 + k)->Solutions)) continue;
            Solution_PN = (struct Solution *)List_Pointer(
              (Current.DofData_P0 + k)->Solutions,
              List_Nbr((Current.DofData_P0 + k)->Solutions) - 1);
            if((Solution_PN - (Current.DofData_P0 + k)->CurrentSolution) >=
               ntime)
              ((Current.DofData_P0 + k)->CurrentSolution) += ntime;
          }
        }
        else
          Message::Error("Explicit (x,y,z,time) evaluation not implemented");
      }
      else {
        Message::Error("Explicit Dof{} evaluation out of context");
      }
      Current.Element = Save_CurrentElement;
      Current.Region = Save_Region;
      break;

    case WQ_TRACE: /* Trace[WholeQuantity, Group] */
      Save_Region = Current.Region;

      if(!Element->ElementTrace) {
        Message::Error(
          "Trace must act on discrete quantity (and not in post-processing)");
        break;
      }

      Current.Region = Element->ElementTrace->Region;

      if(WholeQuantity_P->Case.Trace.DofIndexInWholeQuantity >= 0) {
        Cal_WholeQuantity(Element->ElementTrace, QuantityStorage_P0,
                          WholeQuantity_P->Case.Trace.WholeQuantity, Current.ut,
                          Current.vt, Current.wt,
                          WholeQuantity_P->Case.Trace.DofIndexInWholeQuantity,
                          Nbr_Dof, DofValue, NbrArguments, ExpressionName);
        DofIndexInWholeQuantity = DofIndex = Index;
      }
      else {
        Current.x = Current.y = Current.z = 0.;
        for(j = 0; j < Element->GeoElement->NbrNodes; j++) {
          Current.x += Element->x[j] * Element->n[j];
          Current.y += Element->y[j] * Element->n[j];
          Current.z += Element->z[j] * Element->n[j];
        }
#if defined(HAVE_KERNEL)
        xyz2uvwInAnElement(Element->ElementTrace, Current.x, Current.y,
                           Current.z, &Current.ut, &Current.vt, &Current.wt);
#else
        Message::Error("TODO xyz2uvwInAnElement");
#endif
        for(j = 0; j < NbrArguments; j++) {
          Cal_CopyValue(DofValue + j, &Stack[0][Index]);
          Multi[Index] = 0;
          Index++;
        }
        Index -= NbrArguments;
        Cal_WholeQuantity(Element->ElementTrace, QuantityStorage_P0,
                          WholeQuantity_P->Case.Trace.WholeQuantity, Current.ut,
                          Current.vt, Current.wt, -1, 0, &Stack[0][Index],
                          NbrArguments, ExpressionName);
      }

      Current.Region = Save_Region;
      Multi[Index] = 0;
      Index++;
      break;

    case WQ_SOLIDANGLE: /* SolidAngle[{qty}] */
      Cal_SolidAngle(0, Element,
                     QuantityStorage_P0 +
                       WholeQuantity_P->Case.OperatorAndQuantity.Index,
                     Nbr_Dof, Index, (struct Value **)Stack);
      Multi[Index] = 1;
      Index++;
      break;

    case WQ_BINARYOPERATOR: /* + - * x / % ^ < > <= >= == != && || */
      if(Index - 2 != DofIndex && Index - 1 != DofIndex) {
        if(!Multi[Index - 1] && !Multi[Index - 2])
          ((CAST3V)WholeQuantity_P->Case.Operator.Function)(
            &Stack[0][Index - 2], &Stack[0][Index - 1], &Stack[0][Index - 2]);
        else if(Multi[Index - 1] && Multi[Index - 2])
          for(j = 0; j < Nbr_Dof; j++)
            ((CAST3V)WholeQuantity_P->Case.Operator.Function)(
              &Stack[j][Index - 2], &Stack[j][Index - 1], &Stack[j][Index - 2]);
        else if(Multi[Index - 2])
          for(j = 0; j < Nbr_Dof; j++)
            ((CAST3V)WholeQuantity_P->Case.Operator.Function)(
              &Stack[j][Index - 2], &Stack[0][Index - 1], &Stack[j][Index - 2]);
        else {
          for(j = 0; j < Nbr_Dof; j++)
            ((CAST3V)WholeQuantity_P->Case.Operator.Function)(
              &Stack[0][Index - 2], &Stack[j][Index - 1], &Stack[j][Index - 2]);
          Multi[Index - 2] = 1;
        }
      }
      else if(Index - 1 == DofIndex) {
        if(Multi[Index - 2])
          for(j = 0; j < Nbr_Dof; j++)
            ((CAST3V)WholeQuantity_P->Case.Operator.Function)(
              &Stack[j][Index - 2], &DofValue[j], &DofValue[j]);
        else
          for(j = 0; j < Nbr_Dof; j++)
            ((CAST3V)WholeQuantity_P->Case.Operator.Function)(
              &Stack[0][Index - 2], &DofValue[j], &DofValue[j]);
        DofIndex--;
      }
      else { /* Index-2 == DofIndex */
        if(Multi[Index - 1])
          for(j = 0; j < Nbr_Dof; j++)
            ((CAST3V)WholeQuantity_P->Case.Operator.Function)(
              &DofValue[j], &Stack[j][Index - 1], &DofValue[j]);
        else
          for(j = 0; j < Nbr_Dof; j++)
            ((CAST3V)WholeQuantity_P->Case.Operator.Function)(
              &DofValue[j], &Stack[0][Index - 1], &DofValue[j]);
      }
      Index--;
      break;

    case WQ_UNARYOPERATOR: /* + - ! */
      if(Index - 1 == DofIndex)
        for(j = 0; j < Nbr_Dof; j++)
          ((CAST1V)WholeQuantity_P->Case.Operator.Function)(&DofValue[j]);
      else if(Multi[Index - 1])
        for(j = 0; j < Nbr_Dof; j++)
          ((CAST1V)WholeQuantity_P->Case.Operator.Function)(
            &Stack[j][Index - 1]);
      else
        ((CAST1V)WholeQuantity_P->Case.Operator.Function)(&Stack[0][Index - 1]);
      break;

      /* WARNING: all the rest assumes 0 multi status */

    case WQ_TEST:
      Flag_True = (Stack[0][Index - 1].Val[0] != 0.);
      for(j = 0; j < NbrArguments; j++) {
        Cal_CopyValue(DofValue + j, &Stack[0][Index - 1]);
        Multi[Index - 1] = 0;
        Index++;
      }
      Index -= NbrArguments;
      Cal_WholeQuantity(
        Element, QuantityStorage_P0,
        (Flag_True) ? WholeQuantity_P->Case.Test.WholeQuantity_True :
                      WholeQuantity_P->Case.Test.WholeQuantity_False,
        u, v, w, -1, 0, &Stack[0][Index - 1], NbrArguments, ExpressionName);
      break;

    case WQ_EXPRESSION:
      Index -= WholeQuantity_P->Case.Expression.NbrArguments;
      Get_ValueOfExpression(
        (struct Expression *)List_Pointer(
          Problem_S.Expression, WholeQuantity_P->Case.Expression.Index),
        QuantityStorage_P0, u, v, w, &Stack[0][Index],
        WholeQuantity_P->Case.Expression.NbrArguments);
      Multi[Index] = 0;
      Index++;
      break;

    case WQ_BUILTINFUNCTION:
      Index -= WholeQuantity_P->Case.Function.NbrArguments;

      if(Index != DofIndex)
        ((CASTF2V)WholeQuantity_P->Case.Function.Fct)(
          &WholeQuantity_P->Case.Function, &Stack[0][Index], &Stack[0][Index]);
      else /* Dof must be the only argument, only valid with linear functions */
        for(j = 0; j < Nbr_Dof; j++) {
          Current.NumEntity = Current.NumEntities[j]; /* temp */
          ((CASTF2V)WholeQuantity_P->Case.Function.Fct)(
            &WholeQuantity_P->Case.Function, &DofValue[j], &DofValue[j]);
        }

      Multi[Index] = 0;
      Index++;
      break;

    case WQ_EXTERNBUILTINFUNCTION:
      ((CASTF2V)WholeQuantity_P->Case.Function.Fct)(
        &WholeQuantity_P->Case.Function, DofValue, &Stack[0][Index]);
      Multi[Index] = 0;
      Index++;
      break;

    case WQ_CONSTANT:
      if(Current.NbrHar == 1) {
        Stack[0][Index].Val[0] = WholeQuantity_P->Case.Constant;
      }
      else {
        for(k = 0; k < Current.NbrHar; k += 2) {
          Stack[0][Index].Val[MAX_DIM * k] = WholeQuantity_P->Case.Constant;
          Stack[0][Index].Val[MAX_DIM * (k + 1)] = 0.;
        }
      }
      Stack[0][Index].Type = SCALAR;
      Multi[Index] = 0;
      Index++;
      break;

    case WQ_CURRENTVALUE:
      if(Current.NbrHar == 1) {
        Stack[0][Index].Val[0] = *(WholeQuantity_P->Case.CurrentValue.Value);
      }
      else {
        for(k = 0; k < Current.NbrHar; k += 2) {
          Stack[0][Index].Val[MAX_DIM * k] =
            *(WholeQuantity_P->Case.CurrentValue.Value);
          Stack[0][Index].Val[MAX_DIM * (k + 1)] = 0.;
        }
      }
      Stack[0][Index].Type = SCALAR;
      Multi[Index] = 0;
      Index++;
      break;

    case WQ_ARGUMENT:
      if(WholeQuantity_P->Case.Argument.Index > NbrArguments) {
        Message::Error("Function %s called with too few arguments.",
                       ExpressionName);
      }
      Cal_CopyValue(DofValue + WholeQuantity_P->Case.Argument.Index - 1,
                    &Stack[0][Index]);
      Multi[Index] = 0;
      Index++;
      break;

    case WQ_TIMEDERIVATIVE:
      if(Current.TypeTime == TIME_GEAR) {
        for(j = 0; j < NbrArguments; j++) {
          Cal_CopyValue(DofValue + j, &Stack[0][Index]);
          Multi[Index] = 0;
          Index++;
        }
        Index -= NbrArguments;
        Cal_WholeQuantity(Element, QuantityStorage_P0,
                          WholeQuantity_P->Case.TimeDerivative.WholeQuantity, u,
                          v, w, -1, 0, &Stack[0][Index], NbrArguments,
                          ExpressionName);

        for(k = 0; k < Current.NbrSystem; k++)
          (Current.DofData_P0 + k)->Save_CurrentSolution =
            (Current.DofData_P0 + k)->CurrentSolution;
        Save_TimeStep = Current.TimeStep;
        Save_Time = Current.Time;
        Save_TimeImag = Current.TimeImag;

        for(int n = 0; n < Current.CorrOrder; n++) {
          for(k = 0; k < Current.NbrSystem; k++) {
            Solution_P0 = (struct Solution *)List_Pointer(
              (Current.DofData_P0 + k)->Solutions, 0);
            if(((Current.DofData_P0 + k)->CurrentSolution - Solution_P0) > 0)
              ((Current.DofData_P0 + k)->CurrentSolution) -= 1;
            else {
              Message::Error("Too few solutions for Dt with Gear's method");
              break;
            }
          }

          Current.TimeStep = Current.DofData->CurrentSolution->TimeStep;
          Current.Time = Current.DofData->CurrentSolution->Time;
          Current.TimeImag = Current.DofData->CurrentSolution->TimeImag;

          for(j = 0; j < NbrArguments; j++) {
            Cal_CopyValue(DofValue + j, &Stack[0][Index + 1]);
            Multi[Index + 1] = 0;
            Index++;
          }
          Index -= NbrArguments;
          Cal_WholeQuantity(Element, QuantityStorage_P0,
                            WholeQuantity_P->Case.TimeDerivative.WholeQuantity,
                            u, v, w, -1, 0, &Stack[0][Index + 1], NbrArguments,
                            ExpressionName);
          Cal_AddMultValue(&Stack[0][Index], &Stack[0][Index + 1],
                           -Current.aCorrCoeff[n], &Stack[0][Index]);
        }

        Cal_MultValue(&Stack[0][Index],
                      1. / (Current.bCorrCoeff * Current.DTime),
                      &Stack[0][Index]);

        for(k = 0; k < Current.NbrSystem; k++)
          (Current.DofData_P0 + k)->CurrentSolution =
            (Current.DofData_P0 + k)->Save_CurrentSolution;
        Current.TimeStep = Save_TimeStep;
        Current.Time = Save_Time;
        Current.TimeImag = Save_TimeImag;
      }
      else if(Current.NbrHar == 1) {
        for(j = 0; j < NbrArguments; j++) {
          Cal_CopyValue(DofValue + j, &Stack[0][Index]);
          Multi[Index] = 0;
          Index++;
        }
        Index -= NbrArguments;
        Cal_WholeQuantity(Element, QuantityStorage_P0,
                          WholeQuantity_P->Case.TimeDerivative.WholeQuantity, u,
                          v, w, -1, 0, &Stack[0][Index], NbrArguments,
                          ExpressionName);

        for(k = 0; k < Current.NbrSystem; k++) {
          (Current.DofData_P0 + k)->Save_CurrentSolution =
            (Current.DofData_P0 + k)->CurrentSolution;

          if(List_Nbr((Current.DofData_P0 + k)->Solutions) > 1) {
            Solution_P0 = (struct Solution *)List_Pointer(
              (Current.DofData_P0 + k)->Solutions, 0);
            if((Current.DofData_P0 + k)->CurrentSolution != Solution_P0)
              ((Current.DofData_P0 + k)->CurrentSolution)--;
          }
          else {
            if(!Flag_WarningMissSolForDt) {
              Message::Warning(
                "Missing solution for time derivative computation "
                "(Sys#%d/%d)",
                k + 1, Current.NbrSystem);
              Flag_WarningMissSolForDt = 1;
            }
          }
        }

        Save_TimeStep = Current.TimeStep;
        Save_Time = Current.Time;
        Save_TimeImag = Current.TimeImag;

        Current.TimeStep = Current.DofData->CurrentSolution->TimeStep;
        Current.Time = Current.DofData->CurrentSolution->Time;
        Current.TimeImag = Current.DofData->CurrentSolution->TimeImag;
        for(j = 0; j < NbrArguments; j++) {
          Cal_CopyValue(DofValue + j, &Stack[0][Index + 1]);
          Multi[Index + 1] = 0;
          Index++;
        }
        Index -= NbrArguments;
        Cal_WholeQuantity(Element, QuantityStorage_P0,
                          WholeQuantity_P->Case.TimeDerivative.WholeQuantity, u,
                          v, w, -1, 0, &Stack[0][Index + 1], NbrArguments,
                          ExpressionName);
        Cal_SubstractValue(&Stack[0][Index], &Stack[0][Index + 1],
                           &Stack[0][Index]);
        Stack[0][Index + 1].Val[0] = Save_Time - Current.Time;
        Stack[0][Index + 1].Type = SCALAR;
        if(Stack[0][Index + 1].Val[0])
          Cal_DivideValue(&Stack[0][Index], &Stack[0][Index + 1],
                          &Stack[0][Index]);
        else
          Cal_ZeroValue(&Stack[0][Index]);

        for(k = 0; k < Current.NbrSystem; k++)
          (Current.DofData_P0 + k)->CurrentSolution =
            (Current.DofData_P0 + k)->Save_CurrentSolution;
        Current.TimeStep = Save_TimeStep;
        Current.Time = Save_Time;
        Current.TimeImag = Save_TimeImag;
      }
      else {
        for(j = 0; j < NbrArguments; j++) {
          Cal_CopyValue(DofValue + j, &Stack[0][Index]);
          Multi[Index] = 0;
          Index++;
        }
        Index -= NbrArguments;
        Cal_WholeQuantity(Element, QuantityStorage_P0,
                          WholeQuantity_P->Case.TimeDerivative.WholeQuantity, u,
                          v, w, -1, 0, &Stack[0][Index], NbrArguments,
                          ExpressionName);
        for(k = 0; k < Current.NbrHar; k += 2) {
          Stack[0][Index + 1].Val[MAX_DIM * k] = 0.;
          Stack[0][Index + 1].Val[MAX_DIM * (k + 1)] =
            Current.DofData->Val_Pulsation[k / 2];
        }
        Stack[0][Index + 1].Type = SCALAR;
        Cal_ProductValue(&Stack[0][Index], &Stack[0][Index + 1],
                         &Stack[0][Index]);
      }
      Multi[Index] = 0;
      Index++;
      break;

    case WQ_ATANTERIORTIMESTEP:
      ntime = WholeQuantity_P->Case.AtAnteriorTimeStep.TimeStep;

      for(k = 0; k < Current.NbrSystem; k++) {
        Solution_P0 = (struct Solution *)List_Pointer(
          (Current.DofData_P0 + k)->Solutions, 0);
        if(((Current.DofData_P0 + k)->CurrentSolution - Solution_P0) >= ntime) {
          ((Current.DofData_P0 + k)->CurrentSolution) -= ntime;
          if(Flag_InfoForTime_ntime !=
             List_Nbr((Current.DofData_P0 + k)->Solutions)) {
            Message::Info("Accessing solution from %d time steps ago", ntime);
            Message::Info(
              "  -> System %d/%d: TimeStep = %d, Time = %g + i * %g", k + 1,
              Current.NbrSystem,
              (Current.DofData_P0 + k)->CurrentSolution->TimeStep,
              (Current.DofData_P0 + k)->CurrentSolution->Time,
              (Current.DofData_P0 + k)->CurrentSolution->TimeImag);
            Flag_InfoForTime_ntime =
              List_Nbr((Current.DofData_P0 + k)->Solutions);
          }
        }
        else {
          if(!Flag_WarningMissSolForTime_ntime) {
            Message::Warning("Missing solution for time step -%d computation "
                             "(System #%d/%d)",
                             ntime, k + 1, Current.NbrSystem);
            Flag_WarningMissSolForTime_ntime = 1;
          }
        }
      }

      Save_TimeStep = Current.TimeStep;
      Save_Time = Current.Time;
      Save_TimeImag = Current.TimeImag;
      Current.TimeStep = Current.DofData->CurrentSolution->TimeStep;
      Current.Time = Current.DofData->CurrentSolution->Time;
      Current.TimeImag = Current.DofData->CurrentSolution->TimeImag;

      for(j = 0; j < NbrArguments; j++) {
        Cal_CopyValue(DofValue + j, &Stack[0][Index]);
        Multi[Index] = 0;
        Index++;
      }
      Index -= NbrArguments;
      Cal_WholeQuantity(Element, QuantityStorage_P0,
                        WholeQuantity_P->Case.AtAnteriorTimeStep.WholeQuantity,
                        u, v, w, -1, 0, &Stack[0][Index], NbrArguments,
                        ExpressionName);

      Current.TimeStep = Save_TimeStep;
      Current.Time = Save_Time;
      Current.TimeImag = Save_TimeImag;

      for(k = 0; k < Current.NbrSystem; k++) {
        Solution_PN = (struct Solution *)List_Pointer(
          (Current.DofData_P0 + k)->Solutions,
          List_Nbr((Current.DofData_P0 + k)->Solutions) - 1);
        if((Solution_PN - (Current.DofData_P0 + k)->CurrentSolution) >= ntime)
          ((Current.DofData_P0 + k)->CurrentSolution) += ntime;
      }

      Multi[Index] = 0;
      Index++;
      break;

    case WQ_MAXOVERTIME:
      if(Current.NbrHar == 1) {
        double time_init = WholeQuantity_P->Case.MaxOverTime.TimeInit;
        double time_final = WholeQuantity_P->Case.MaxOverTime.TimeFinal;
        /*
        for (k = 0 ; k < Current.NbrSystem ; k++)
          (Current.DofData_P0+k)->Save_CurrentSolution =
           (Current.DofData_P0+k)->CurrentSolution;
        */
        Save_TimeStep = Current.TimeStep;
        Save_Time = Current.Time;
        Save_TimeImag = Current.TimeImag;

        for(j = 0; j < NbrArguments; j++) {
          Cal_CopyValue(DofValue + j, &Stack[0][Index]);
          Multi[Index] = 0;
          Index++;
        }
        Index -= NbrArguments;

        double val_maxInTime = -1.e99;

        for(j = 1; j < List_Nbr((Current.DofData)->Solutions); j++) {
          Current.DofData->CurrentSolution =
            (struct Solution *)List_Pointer((Current.DofData)->Solutions, j);

          //++++ Add: also for other systems!

          Current.TimeStep = Current.DofData->CurrentSolution->TimeStep;
          Current.Time = Current.DofData->CurrentSolution->Time;
          Current.TimeImag = Current.DofData->CurrentSolution->TimeImag;

          //++++ test to do more accurately!
          if(Current.Time >= time_init && Current.Time <= time_final) {
            Cal_WholeQuantity(Element, QuantityStorage_P0,
                              WholeQuantity_P->Case.MaxOverTime.WholeQuantity,
                              u, v, w, -1, 0, &Stack[0][Index], NbrArguments,
                              ExpressionName);

            if(Stack[0][Index].Type == SCALAR) {
              if(Stack[0][Index].Val[0] > val_maxInTime) {
                val_maxInTime = Stack[0][Index].Val[0];
              }
            }
            else {
              Message::Error(
                "MaxOverTime can only be applied on scalar values");
            }
          }
        }
        Stack[0][Index].Val[0] = val_maxInTime;
        /*
        for (k = 0 ; k < Current.NbrSystem ; k++)
          (Current.DofData_P0+k)->CurrentSolution =
            (Current.DofData_P0+k)->Save_CurrentSolution;
        */
        Current.TimeStep = Save_TimeStep;
        Current.Time = Save_Time;
        Current.TimeImag = Save_TimeImag;

        Multi[Index] = 0;
        Index++;
      }
      else {
        Message::Error("MaxOverTime can only be used in time domain");
        break;
      }
      break;

    case WQ_FOURIERSTEINMETZ:
      if(Current.NbrHar == 1) {
        double time_init = WholeQuantity_P->Case.FourierSteinmetz.TimeInit;
        double time_final = WholeQuantity_P->Case.FourierSteinmetz.TimeFinal;
        int nbrFrequencyInFormula =
          WholeQuantity_P->Case.FourierSteinmetz.NbrFrequency;
        double exponent_f = WholeQuantity_P->Case.FourierSteinmetz.Exponent_f;
        double exponent_b = WholeQuantity_P->Case.FourierSteinmetz.Exponent_b;

        /*
        for (k = 0 ; k < Current.NbrSystem ; k++)
          (Current.DofData_P0+k)->Save_CurrentSolution =
           (Current.DofData_P0+k)->CurrentSolution;
        */
        Save_TimeStep = Current.TimeStep;
        Save_Time = Current.Time;
        Save_TimeImag = Current.TimeImag;

        for(j = 0; j < NbrArguments; j++) {
          Cal_CopyValue(DofValue + j, &Stack[0][Index]);
          Multi[Index] = 0;
          Index++;
        }
        Index -= NbrArguments;

        int i_Solution_init = -1, i_Solution_final = -1;
        int NbrTimeStep, Size = -1;

        for(j = 0; j < List_Nbr((Current.DofData)->Solutions); j++) {
          Current.DofData->CurrentSolution =
            (struct Solution *)List_Pointer((Current.DofData)->Solutions, j);
          Current.Time = Current.DofData->CurrentSolution->Time;
          if(Current.Time >= time_init && i_Solution_init < 0)
            i_Solution_init = j;
          if(Current.Time <= time_final) { i_Solution_final = j; }
          if(Current.Time > time_final) { break; }
        }
        NbrTimeStep = i_Solution_final - i_Solution_init + 1;
        if(NbrTimeStep < 2)
          Message::Error(
            "Wrong time interval in Function FourierSteinmetz (%d,%d)",
            i_Solution_init, i_Solution_final);

        double *Times = (double *)Malloc(NbrTimeStep * sizeof(double));
        struct Value *TmpValues =
          (struct Value *)Malloc(NbrTimeStep * sizeof(struct Value));

        for(j = 0; j < NbrTimeStep; j++) {
          Current.DofData->CurrentSolution = (struct Solution *)List_Pointer(
            (Current.DofData)->Solutions, i_Solution_init + j);

          //++++ Add: also for other systems!

          Current.TimeStep = Current.DofData->CurrentSolution->TimeStep;
          Current.Time = Current.DofData->CurrentSolution->Time;
          Current.TimeImag = Current.DofData->CurrentSolution->TimeImag;

          Cal_WholeQuantity(Element, QuantityStorage_P0,
                            WholeQuantity_P->Case.MaxOverTime.WholeQuantity, u,
                            v, w, -1, 0, &Stack[0][Index], NbrArguments,
                            ExpressionName);
          Times[j] = Current.Time;
          Cal_CopyValue(&Stack[0][Index], &TmpValues[j]);
          if(Stack[0][Index].Type == SCALAR)
            Size = 1;
          else if(Stack[0][Index].Type == VECTOR)
            Size = 3;
          else
            Message::Error("FourierSteinmetz can only be applied on scalar or "
                           "vector values");
        }

        // FourierTransform
        int NbrFreq;
        double *Frequencies;
        struct Value *FourierValues;

#if defined(HAVE_KERNEL)
        Pos_FourierTransform(NbrTimeStep, 1, Times, TmpValues, Size, 2,
                             nbrFrequencyInFormula, &NbrFreq, &Frequencies,
                             &FourierValues);
#else
        Message::Error("TODO Pos_FourierTransform");
#endif
        /*
          we calculate the Sum for all frequencies of frequency_i^exponent_f *
          b_i^exponent_b
        */

        if(nbrFrequencyInFormula > NbrFreq - 1)
          Message::Error("FourierSteinmetz: too many frequencies asked "
                         "(%d asked and only %d available)",
                         nbrFrequencyInFormula, NbrFreq - 1);

        double val = 0.;
        for(j = 0; j < nbrFrequencyInFormula; j++) {
          if(Size == 1) {
            val += pow(Frequencies[j + 1], exponent_f) *
                   pow(FourierValues[j + 1].Val[0], exponent_b);
          }
          else {
            val +=
              pow(Frequencies[j + 1], exponent_f) *
              pow(
                sqrt(FourierValues[j + 1].Val[0] * FourierValues[j + 1].Val[0] +
                     FourierValues[j + 1].Val[1] * FourierValues[j + 1].Val[1] +
                     FourierValues[j + 1].Val[2] * FourierValues[j + 1].Val[2]),
                exponent_b);
          }
        }
        Stack[0][Index].Type = SCALAR;
        Stack[0][Index].Val[0] = val;

        Free(Frequencies);
        Free(FourierValues);

        Free(Times);
        Free(TmpValues);

        /*
        for (k = 0 ; k < Current.NbrSystem ; k++)
          (Current.DofData_P0+k)->CurrentSolution =
            (Current.DofData_P0+k)->Save_CurrentSolution;
        */
        Current.TimeStep = Save_TimeStep;
        Current.Time = Save_Time;
        Current.TimeImag = Save_TimeImag;

        Multi[Index] = 0;
        Index++;
      }
      else {
        Message::Error("FourierSteinmetz can only be used in time domain");
        break;
      }
      break;

    case WQ_MHTRANSFORM:
      if(Current.NbrHar == 1) {
        Message::Error(
          "MHTransform can only be used in complex (multi-harmonic)"
          " calculations");
        break;
      }

      for(j = 0; j < NbrArguments; j++) {
        Cal_CopyValue(DofValue + j, &Stack[0][Index]);
        Multi[Index] = 0;
        Index++;
      }
      Index -= NbrArguments;
      {
        int N = List_Nbr(WholeQuantity_P->Case.MHTransform.WholeQuantity_L);
        std::vector<struct Value> MH_Values(N);
        for(int j = 0; j < N; j++) {
          List_T *WQ;
          List_Read(WholeQuantity_P->Case.MHTransform.WholeQuantity_L, j, &WQ);
          Cal_WholeQuantity(Element, QuantityStorage_P0, WQ, u, v, w, -1, 0,
                            &MH_Values[j], NbrArguments, ExpressionName);
        }
        MHTransform(
          Element, QuantityStorage_P0, u, v, w, MH_Values,
          (struct Expression *)List_Pointer(
            Problem_S.Expression, WholeQuantity_P->Case.MHTransform.Index),
          WholeQuantity_P->Case.MHTransform.NbrPoints, Stack[0][Index]);
      }
      Multi[Index] = 0;
      Index++;
      break;

    case WQ_CAST:
      /* This should be changed... */
      Save_NbrHar = Current.NbrHar;
      Save_DofData = Current.DofData;

      if(!WholeQuantity_P->Case.Cast.NbrHar) {
        Current.DofData =
          ((struct FunctionSpace *)List_Pointer(
             Problem_S.FunctionSpace,
             WholeQuantity_P->Case.Cast.FunctionSpaceIndexForType))
            ->DofData;

        Current.NbrHar = Current.DofData->NbrHar;
      }
      else {
        Current.NbrHar = WholeQuantity_P->Case.Cast.NbrHar;
      }

      for(j = 0; j < NbrArguments; j++) {
        Cal_CopyValue(DofValue + j, &Stack[0][Index]);
        Multi[Index] = 0;
        Index++;
      }
      Index -= NbrArguments;
      Cal_WholeQuantity(Element, QuantityStorage_P0,
                        WholeQuantity_P->Case.Cast.WholeQuantity, u, v, w, -1,
                        0, &Stack[0][Index], NbrArguments, ExpressionName);

      if(Current.NbrHar < Save_NbrHar) /* ne plus a completer ...?? */
        Cal_SetZeroHarmonicValue(&Stack[0][Index], Save_NbrHar);

      Current.NbrHar = Save_NbrHar;
      Current.DofData = Save_DofData;
      Multi[Index] = 0;
      Index++;
      break;

    case WQ_CHANGECURRENTPOSITION:
      Save_x = Current.x;
      Save_y = Current.y;
      Save_z = Current.z;

      Current.x = Stack[0][Index - 1].Val[0];
      Current.y = Stack[0][Index - 1].Val[1];
      Current.z = Stack[0][Index - 1].Val[2];

      for(j = 0; j < NbrArguments; j++) {
        Cal_CopyValue(DofValue + j, &Stack[0][Index - 1]);
        Multi[Index - 1] = 0;
        Index++;
      }
      Index -= NbrArguments;
      Cal_WholeQuantity(
        Element, QuantityStorage_P0,
        WholeQuantity_P->Case.ChangeCurrentPosition.WholeQuantity, u, v, w, -1,
        0, &Stack[0][Index - 1], NbrArguments, ExpressionName);

      Current.x = Save_x;
      Current.y = Save_y;
      Current.z = Save_z;
      break;

    case WQ_SAVEVALUE:
      Cal_CopyValue(&Stack[0][Index - 1],
                    &ValueSaved[WholeQuantity_P->Case.SaveValue.Index]);
      break;

    case WQ_VALUESAVED:
      if(ValueSaved.count(WholeQuantity_P->Case.ValueSaved.Index))
        Cal_CopyValue(&ValueSaved[WholeQuantity_P->Case.ValueSaved.Index],
                      &Stack[0][Index]);
      else {
        if(TreatmentStatus != STATUS_PRE)
          Message::Warning("Empty register %d: assuming zero value",
                           WholeQuantity_P->Case.ValueSaved.Index + 1);
        Cal_ZeroValue(&Stack[0][Index]);
        Stack[0][Index].Type = SCALAR;
      }
      Multi[Index] = 0;
      Index++;
      break;

    case WQ_SAVENAMEDVALUE:
      Cal_CopyValue(&Stack[0][Index - 1],
                    &NamedValueSaved[WholeQuantity_P->Case.NamedValue.Name]);
      break;

    case WQ_NAMEDVALUESAVED:
      if(NamedValueSaved.count(WholeQuantity_P->Case.NamedValue.Name))
        Cal_CopyValue(&NamedValueSaved[WholeQuantity_P->Case.NamedValue.Name],
                      &Stack[0][Index]);
      else {
        if(TreatmentStatus != STATUS_PRE)
          Message::Warning("Unknown current value '$%s': assuming zero value",
                           WholeQuantity_P->Case.NamedValue.Name);
        Cal_ZeroValue(&Stack[0][Index]);
        Stack[0][Index].Type = SCALAR;
      }
      Multi[Index] = 0;
      Index++;
      break;

    case WQ_SHOWVALUE:
      if(Index - 1 == DofIndex) {
        for(j = 0; j < Nbr_Dof; j++) {
          fprintf(stderr, "##%d Dof %d ", WholeQuantity_P->Case.ShowValue.Index,
                  j + 1);
          Show_Value(&DofValue[j]);
        }
      }
      else {
        fprintf(stderr, "##%d ", WholeQuantity_P->Case.ShowValue.Index);
        Show_Value(&Stack[0][Index - 1]);
      }
      break;

    default:
      Message::Error("Unknown type of WholeQuantity (%d)",
                     WholeQuantity_P->Type);
      break;
    }
  }

  if(DofIndexInWholeQuantity < 0) Cal_CopyValue(&Stack[0][0], &DofValue[0]);
}

/* ------------------------------------------------------------------------ */
/*  C a l _ S t o r e I n R e g i s t e r                                   */
/* ------------------------------------------------------------------------ */

void Cal_StoreInRegister(struct Value *Value, int RegisterIndex)
{
  Cal_CopyValue(Value, &ValueSaved[RegisterIndex]);
}

/* ------------------------------------------------------------------------ */
/*  C a l _ S t o r e I n V a r i a b l e                                   */
/* ------------------------------------------------------------------------ */

void Cal_StoreInVariable(struct Value *Value, const char *name)
{
  Cal_CopyValue(Value, &NamedValueSaved[name]);
  Export_Value(Value, GetDPNumbers[name], 0, false); // don't append
}

void Cal_GetValueSaved(struct Value *Value, const char *name)
{
  if(NamedValueSaved.count(name))
    Cal_CopyValue(&NamedValueSaved[name], Value);
  else {
    Message::Warning("Unknown current value '$%s': assuming zero value", name);
    Cal_ZeroValue(Value);
    Value->Type = SCALAR;
  }
}

std::map<std::string, struct Value> &Get_AllValueSaved()
{
  return NamedValueSaved;
}
