// GetDP - Copyright (C) 1997-2021 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 <math.h>
#include "GetDPConfig.h"
#include "ProData.h"
#include "ProDefine.h"
#include "F.h"
#include "NumericUtils.h"
#include "MallocUtils.h"
#include "Message.h"

#if defined(HAVE_KERNEL)
#include "GeoData.h"
#include "DofData.h"
#include "Get_Geometry.h"
#endif

#define SQU(a)     ((a)*(a))

extern struct CurrentData Current ;

void F_Normal(F_ARG)
{
#if !defined(HAVE_KERNEL)
  Message::Error("F_Normal requires Kernel");
#else
  int  k ;

  if(!Current.Element || Current.Element->Num == NO_ELEMENT)
    Message::Error("No element on which to compute 'F_Normal'");

  Geo_CreateNormal(Current.Element->Type,
		   Current.Element->x,
		   Current.Element->y,
		   Current.Element->z,
		   V->Val);

  if (Current.NbrHar != 1) {
    V->Val[MAX_DIM] = 0. ;
    V->Val[MAX_DIM+1] = 0. ;
    V->Val[MAX_DIM+2] = 0. ;
    for (k = 2 ; k < std::min(NBR_MAX_HARMONIC, Current.NbrHar) ; k += 2) {
      V->Val[MAX_DIM* k   ] = V->Val[0] ;
      V->Val[MAX_DIM* k +1] = V->Val[1] ;
      V->Val[MAX_DIM* k +2] = V->Val[2] ;
      V->Val[MAX_DIM*(k+1)  ] = 0. ;
      V->Val[MAX_DIM*(k+1)+1] = 0. ;
      V->Val[MAX_DIM*(k+1)+2] = 0. ;
    }
  }
  V->Type = VECTOR ;
#endif
}

void F_NormalSource(F_ARG)
{
#if !defined(HAVE_KERNEL)
  Message::Error("F_NormalSource requires Kernel");
#else
  int  k ;

  if(!Current.ElementSource || Current.ElementSource->Num == NO_ELEMENT)
    Message::Error("No element on which to compute 'F_NormalSource'");

  Geo_CreateNormal(Current.ElementSource->Type,
		   Current.ElementSource->x,
		   Current.ElementSource->y,
		   Current.ElementSource->z,
		   V->Val);

  if (Current.NbrHar != 1) {
    V->Val[MAX_DIM] = 0. ;
    V->Val[MAX_DIM+1] = 0. ;
    V->Val[MAX_DIM+2] = 0. ;
    for (k = 2 ; k < std::min(NBR_MAX_HARMONIC, Current.NbrHar) ; k += 2) {
      V->Val[MAX_DIM* k   ] = V->Val[0] ;
      V->Val[MAX_DIM* k +1] = V->Val[1] ;
      V->Val[MAX_DIM* k +2] = V->Val[2] ;
      V->Val[MAX_DIM*(k+1)  ] = 0. ;
      V->Val[MAX_DIM*(k+1)+1] = 0. ;
      V->Val[MAX_DIM*(k+1)+2] = 0. ;
    }
  }
  V->Type = VECTOR ;
#endif
}

void F_Tangent(F_ARG)
{
  int  k ;
  double  tx, ty, tz, norm ;

  if(!Current.Element || Current.Element->Num == NO_ELEMENT)
    Message::Error("No element on which to compute 'F_Tangent'");

  switch (Current.Element->Type) {

  case LINE :
    tx = Current.Element->x[1] - Current.Element->x[0] ;
    ty = Current.Element->y[1] - Current.Element->y[0] ;
    tz = Current.Element->z[1] - Current.Element->z[0] ;
    norm = sqrt(SQU(tx)+SQU(ty)+SQU(tz)) ;
    V->Val[0] = tx/norm ;
    V->Val[1] = ty/norm ;
    V->Val[2] = tz/norm ;
    break ;

  default :
    Message::Error("Function 'Tangent' only valid for Line Elements");
  }

  if (Current.NbrHar != 1) {
    V->Val[MAX_DIM] = 0. ;
    V->Val[MAX_DIM+1] = 0. ;
    V->Val[MAX_DIM+2] = 0. ;
    for (k = 2 ; k < std::min(NBR_MAX_HARMONIC, Current.NbrHar) ; k += 2) {
      V->Val[MAX_DIM* k   ] = V->Val[0] ;
      V->Val[MAX_DIM* k +1] = V->Val[1] ;
      V->Val[MAX_DIM* k +2] = V->Val[2] ;
      V->Val[MAX_DIM*(k+1)  ] = 0. ;
      V->Val[MAX_DIM*(k+1)+1] = 0. ;
      V->Val[MAX_DIM*(k+1)+2] = 0. ;
    }
  }
  V->Type = VECTOR ;
}

void F_TangentSource(F_ARG)
{
  int  k ;
  double  tx, ty, tz, norm ;

  if(!Current.ElementSource || Current.ElementSource->Num == NO_ELEMENT)
    Message::Error("No element on which to compute 'F_TangentSource'");

  switch (Current.ElementSource->Type) {

  case LINE :
    tx = Current.ElementSource->x[1] - Current.ElementSource->x[0] ;
    ty = Current.ElementSource->y[1] - Current.ElementSource->y[0] ;
    tz = Current.ElementSource->z[1] - Current.ElementSource->z[0] ;
    norm = sqrt(SQU(tx)+SQU(ty)+SQU(tz)) ;
    V->Val[0] = tx/norm ;
    V->Val[1] = ty/norm ;
    V->Val[2] = tz/norm ;
    break ;

  default :
    Message::Error("Function 'TangentSource' only valid for Line Elements");
  }

  if (Current.NbrHar != 1) {
    V->Val[MAX_DIM] = 0. ;
    V->Val[MAX_DIM+1] = 0. ;
    V->Val[MAX_DIM+2] = 0. ;
    for (k = 2 ; k < std::min(NBR_MAX_HARMONIC, Current.NbrHar) ; k += 2) {
      V->Val[MAX_DIM* k   ] = V->Val[0] ;
      V->Val[MAX_DIM* k +1] = V->Val[1] ;
      V->Val[MAX_DIM* k +2] = V->Val[2] ;
      V->Val[MAX_DIM*(k+1)  ] = 0. ;
      V->Val[MAX_DIM*(k+1)+1] = 0. ;
      V->Val[MAX_DIM*(k+1)+2] = 0. ;
    }
  }
  V->Type = VECTOR ;
}

void F_ElementVol(F_ARG)
{
#if !defined(HAVE_KERNEL)
  Message::Error("F_ElementVol requires Kernel");
#else
  int k;
  double Vol = 0.;
  MATRIX3x3 Jac;

  if(!Current.Element || Current.Element->Num == NO_ELEMENT)
    Message::Error("No element on which to compute 'F_ElementVol'");

  /* It would be more efficient to compute the volumes directly from
     the node coordinates, but I'm lazy... */

  Get_NodesCoordinatesOfElement(Current.Element) ;
  Get_BFGeoElement(Current.Element, Current.u, Current.v, Current.w) ;

  /* we use the most general case (3D embedding) */

  switch(Current.Element->Type){
  case LINE:
    Vol = 2. * JacobianLin3D(Current.Element,&Jac);
    break;
  case TRIANGLE:
    Vol = 0.5 * JacobianSur3D(Current.Element,&Jac) ;
    break;
  case QUADRANGLE:
    Vol = 4. * JacobianSur3D(Current.Element,&Jac) ;
    break;
  case TETRAHEDRON:
    Vol = 1./6. * JacobianVol3D(Current.Element,&Jac) ;
    break;
  case HEXAHEDRON:
    Vol = 8. * JacobianVol3D(Current.Element,&Jac) ;
    break;
  case PRISM:
    Vol = JacobianVol3D(Current.Element,&Jac) ;
    break;
  case PYRAMID:
    Vol = 4./3. * JacobianVol3D(Current.Element,&Jac) ;
    break;
  default :
    Message::Error("F_ElementVol not implemented for %s",
                   Get_StringForDefine(Element_Type, Current.Element->Type));
  }

  V->Type = SCALAR ;
  V->Val[0] = fabs(Vol);
  V->Val[MAX_DIM] = 0.;

  for (k = 2 ; k < std::min(NBR_MAX_HARMONIC, Current.NbrHar) ; k += 2) {
    V->Val[MAX_DIM* k] = V->Val[0] ;
    V->Val[MAX_DIM* (k+1)] = 0. ;
  }
#endif
}

void F_SurfaceArea(F_ARG)
{
#if !defined(HAVE_KERNEL)
  Message::Error("F_SurfaceArea requires Kernel");
#else
  struct Element  Element ;

  // check if the cache can be reused
  if (Fct->Active) {
    bool recompute = false;
    if(Fct->NbrParameters != List_Nbr(Fct->Active->Case.SurfaceArea.RegionList)) {
      recompute = true;
    }
    else if(Fct->NbrParameters >= 1) {
      for(int i = 0; i < Fct->NbrParameters; i++) {
        int num ; List_Read(Fct->Active->Case.SurfaceArea.RegionList, i, &num);
        if(num != (int)(Fct->Para[i])) {
          recompute = true;
          break;
        }
      }
    }
    else if(Current.Region != Fct->Active->Case.SurfaceArea.RegionCurrent) {
      recompute = true;
    }
    if(recompute) {
      List_Delete(Fct->Active->Case.SurfaceArea.RegionList);
      Free(Fct->Active);
      Fct->Active = NULL;
    }
  }

  if (!Fct->Active) {
    Fct->Active = (struct FunctionActive *)Malloc(sizeof(struct FunctionActive)) ;

    List_T *InitialList_L = NULL;
    if (Fct->NbrParameters >= 1) {
      InitialList_L = List_Create(Fct->NbrParameters,1,sizeof(int));
      for (int i = 0; i < Fct->NbrParameters; i++) {
        int Index_Region = (int)(Fct->Para[i]) ;
        List_Add(InitialList_L, &Index_Region);
      }
    }

    double Val_Surface = 0. ;
    int Nbr_Element = Geo_GetNbrGeoElements() ;
    int Nbr_Found_Element = 0;
    for (int i_Element = 0 ; i_Element < Nbr_Element; i_Element++) {
      Element.GeoElement = Geo_GetGeoElement(i_Element) ;
      if ((InitialList_L &&
	   List_Search(InitialList_L, &(Element.GeoElement->Region), fcmp_int)) ||
	  (!InitialList_L && Element.GeoElement->Region == Current.Region)) {

        Nbr_Found_Element++;

        Element.Num    = Element.GeoElement->Num ;
	Element.Type   = Element.GeoElement->Type ;

	if (Element.Type == TRIANGLE || Element.Type == QUADRANGLE ||
            Element.Type == TRIANGLE_2) {

	  Get_NodesCoordinatesOfElement(&Element) ;
	  Get_BFGeoElement(&Element, 0., 0., 0.) ;
          double  c11 = 0., c21 = 0., c12 = 0., c22 = 0., c13 = 0., c23 = 0.;
	  for (int i = 0 ; i < Element.GeoElement->NbrNodes ; i++ ) {
	    c11 += Element.x[i] * Element.dndu[i][0] ;
	    c21 += Element.x[i] * Element.dndu[i][1] ;
	    c12 += Element.y[i] * Element.dndu[i][0] ;
	    c22 += Element.y[i] * Element.dndu[i][1] ;
	    c13 += Element.z[i] * Element.dndu[i][0] ;
	    c23 += Element.z[i] * Element.dndu[i][1] ;
	  }
          double DetJac = sqrt(SQU(c11 * c22 - c12 * c21) +
                               SQU(c13 * c21 - c11 * c23) +
                               SQU(c12 * c23 - c13 * c22));

	  if (Element.Type == TRIANGLE || Element.Type == TRIANGLE_2)
	    Val_Surface += fabs(DetJac) * 0.5 ;
	  else if (Element.Type == QUADRANGLE)
	    Val_Surface += fabs(DetJac) * 4. ;

	}
	else if (Element.Type == LINE || Element.Type == LINE_2) {
	  Get_NodesCoordinatesOfElement(&Element) ;
	  Get_BFGeoElement(&Element, 0., 0., 0.) ;

          double  c11 = 0., c12 = 0., c13 = 0.;
	  for (int i = 0; i < Element.GeoElement->NbrNodes; i++) {
	    c11 += Element.x[i] * Element.dndu[i][0] ;
	    c12 += Element.y[i] * Element.dndu[i][0] ;
	    c13 += Element.z[i] * Element.dndu[i][0] ;
	  }
          double DetJac = sqrt(SQU(c11)+SQU(c12)+SQU(c13));

          Val_Surface += fabs(DetJac) * 2 ; // SurfaceArea of LINE x 1m
        }
	else {
	  Message::Error("Function 'SurfaceArea' only valid for line, triangle or "
                         "quandrangle elements");
	}
      }
    }
    Fct->Active->Case.SurfaceArea.RegionList = InitialList_L ;
    Fct->Active->Case.SurfaceArea.RegionCurrent = Current.Region ;
    Fct->Active->Case.SurfaceArea.Value = Val_Surface ;

    if(!Nbr_Found_Element)
      Message::Info("No element found in SurfaceArea[]");
  }

  V->Type = SCALAR ;
  V->Val[0] = Fct->Active->Case.SurfaceArea.Value ;
  V->Val[MAX_DIM] = 0.;

  for (int k = 2; k < std::min(NBR_MAX_HARMONIC, Current.NbrHar); k += 2) {
    V->Val[MAX_DIM* k] = V->Val[0] ;
    V->Val[MAX_DIM* (k+1)] = 0. ;
  }
#endif
}

void F_GetVolume(F_ARG)
{
#if !defined(HAVE_KERNEL)
  Message::Error("F_GetVolume requires Kernel");
#else
  struct Element  Element ;
  List_T  * InitialList_L;

  int     Nbr_Element, i_Element ;
  double  Val_Volume ;
  double  c11, c21, c31, c12, c22, c32, c13, c23, c33 ;
  double  DetJac ;
  int     i, k ;

  if (!Fct->Active) {
    Fct->Active = (struct FunctionActive *)Malloc(sizeof(struct FunctionActive)) ;

    if (Fct->NbrParameters == 1) {
      int Index_Region = (int)(Fct->Para[0]) ;
      InitialList_L = List_Create(1, 1, sizeof(int));
      List_Add(InitialList_L,&Index_Region);
    }
    else {
      InitialList_L = NULL ;
    }

    Val_Volume = 0. ;
    Nbr_Element = Geo_GetNbrGeoElements() ;
    for (i_Element = 0 ; i_Element < Nbr_Element; i_Element++) {
      Element.GeoElement = Geo_GetGeoElement(i_Element) ;
      if ((InitialList_L &&
	   List_Search(InitialList_L, &(Element.GeoElement->Region), fcmp_int)) ||
	  (!InitialList_L && Element.GeoElement->Region == Current.Region)) {
	Element.Num    = Element.GeoElement->Num ;
	Element.Type   = Element.GeoElement->Type ;

	if (Element.Type == TETRAHEDRON || Element.Type == TETRAHEDRON_2 ||
            Element.Type == HEXAHEDRON ||
            Element.Type == PRISM) {

	  Get_NodesCoordinatesOfElement(&Element) ;
	  Get_BFGeoElement(&Element, 0., 0., 0.) ;

	  c11 = c21 = c31 = c12 = c22 = c32 = c13 = c23 = c33 = 0;
	  for ( i = 0 ; i < Element.GeoElement->NbrNodes ; i++ ) {
            c11 += Element.x[i] * Element.dndu[i][0] ;
            c21 += Element.x[i] * Element.dndu[i][1] ;
            c31 += Element.x[i] * Element.dndu[i][2] ;

            c12 += Element.y[i] * Element.dndu[i][0] ;
            c22 += Element.y[i] * Element.dndu[i][1] ;
            c32 += Element.y[i] * Element.dndu[i][2] ;

            c13 += Element.z[i] * Element.dndu[i][0] ;
            c23 += Element.z[i] * Element.dndu[i][1] ;
            c33 += Element.z[i] * Element.dndu[i][2] ;
	  }

          DetJac = c11 * c22 * c33 + c13 * c21 * c32
            + c12 * c23 * c31 - c13 * c22 * c31
            - c11 * c23 * c32 - c12 * c21 * c33 ;

          switch(Element.Type){
          case TETRAHEDRON:
          case TETRAHEDRON_2:
            Val_Volume += 1./6. * fabs(DetJac);
            break;
          case HEXAHEDRON:
            Val_Volume += 8. * fabs(DetJac);
            break;
          case PRISM:
            Val_Volume += fabs(DetJac);
            break;
          }
	}
	else {
	  Message::Error("Function 'GetVolume' not valid for %s",
                         Get_StringForDefine(Element_Type, Element.Type));
	}
      }
    }
    Fct->Active->Case.GetVolume.Value = Val_Volume ;
  }

  V->Type = SCALAR ;
  V->Val[0] = Fct->Active->Case.GetVolume.Value ;
  V->Val[MAX_DIM] = 0.;

  for (k = 2 ; k < std::min(NBR_MAX_HARMONIC, Current.NbrHar) ; k += 2) {
    V->Val[MAX_DIM* k] = V->Val[0] ;
    V->Val[MAX_DIM* (k+1)] = 0. ;
  }
#endif
}

void F_GetNumElement(F_ARG)
{
  int k;

  V->Type = SCALAR ;
  V->Val[0] = (double)Current.Element->Num;
  V->Val[MAX_DIM] = 0.;

  for (k = 2 ; k < std::min(NBR_MAX_HARMONIC, Current.NbrHar) ; k += 2) {
    V->Val[MAX_DIM* k] = V->Val[0] ;
    V->Val[MAX_DIM* (k+1)] = 0 ;
  }
}

void F_GetNumElements(F_ARG)
{
#if !defined(HAVE_KERNEL)
  Message::Error("F_GetNumElements requires Kernel");
#else
  struct Element  Element ;

  if (!Fct->Active) {
    Fct->Active = (struct FunctionActive *)Malloc(sizeof(struct FunctionActive)) ;
    List_T  * InitialList_L;
    if (Fct->NbrParameters == 1) {
      int Index_Region = (int)(Fct->Para[0]) ;
      InitialList_L = List_Create(1, 1, sizeof(int));
      List_Add(InitialList_L, &Index_Region);
    }
    else if (Fct->NbrParameters > 1) {
      InitialList_L = List_Create(Fct->NbrParameters,1,sizeof(int));
      List_Reset(InitialList_L);
      for (int i=0; i<Fct->NbrParameters; i++) {
        int Index_Region = (int)(Fct->Para[i]) ;
        List_Add(InitialList_L, &Index_Region);
      }
    }
    else {
      InitialList_L = NULL ;
    }

    int Count = 0. ;
    int Nbr_Element = Geo_GetNbrGeoElements() ;
    if(!InitialList_L){
      Count = Nbr_Element ;
    }
    else{
      for (int i_Element = 0 ; i_Element < Nbr_Element; i_Element++) {
        Element.GeoElement = Geo_GetGeoElement(i_Element) ;
        if ((InitialList_L &&
             List_Search(InitialList_L, &(Element.GeoElement->Region), fcmp_int)) ||
            (!InitialList_L && Element.GeoElement->Region == Current.Region)) {
          Count++;
        }
      }
    }
    Fct->Active->Case.GetNumElements.Value = Count ;
  }

  V->Type = SCALAR ;
  V->Val[0] = Fct->Active->Case.GetNumElements.Value ;
  V->Val[MAX_DIM] = 0.;

  for (int k = 2 ; k < std::min(NBR_MAX_HARMONIC, Current.NbrHar) ; k += 2) {
    V->Val[MAX_DIM* k] = V->Val[0] ;
    V->Val[MAX_DIM* (k+1)] = 0. ;
  }
#endif
}

void F_GetNumNodes(F_ARG)
{
#if !defined(HAVE_KERNEL)
  Message::Error("F_GetNumNodes requires Kernel");
#else
  // TODO: accept arguments to limit to some regions
  V->Type = SCALAR ;
  V->Val[0] = Geo_GetNbrGeoNodes() ;
  V->Val[MAX_DIM] = 0.;
  for (int k = 2 ; k < std::min(NBR_MAX_HARMONIC, Current.NbrHar) ; k += 2) {
    V->Val[MAX_DIM* k] = V->Val[0] ;
    V->Val[MAX_DIM* (k+1)] = 0. ;
  }
#endif
}

void F_CellSize(F_ARG)
{
#if !defined(HAVE_KERNEL)
  Message::Error("F_CellSize requires Kernel");
#else
  double cellSize, Vol;
  MATRIX3x3 Jac;
  double  c11, c21, c12, c22, DetJac;
  int     i, k ;

  if(!Current.Element || Current.Element->Num == NO_ELEMENT)
    Message::Error("No element on which to compute 'CellSize'");

  Get_NodesCoordinatesOfElement(Current.Element) ;
  Get_BFGeoElement(Current.Element, 0., 0., 0.) ;

  switch(Current.Element->Type){
  case LINE:
    cellSize = 2. * JacobianLin3D(Current.Element,&Jac);
    break;
  case TRIANGLE:
    c11 = c21 = c12 = c22 = 0. ;

    for ( i = 0 ; i < Current.Element->GeoElement->NbrNodes ; i++ ) {
      c11 += Current.Element->x[i] * Current.Element->dndu[i][0] ;
      c21 += Current.Element->x[i] * Current.Element->dndu[i][1] ;
      c12 += Current.Element->y[i] * Current.Element->dndu[i][0] ;
      c22 += Current.Element->y[i] * Current.Element->dndu[i][1] ;
    }
    DetJac = c11 * c22 - c12 * c21 ;
    cellSize =
      sqrt(SQU(Current.Element->x[1]-Current.Element->x[0])
           +SQU(Current.Element->y[1]-Current.Element->y[0])
           +SQU(Current.Element->z[1]-Current.Element->z[0]))
      *
      sqrt(SQU(Current.Element->x[2]-Current.Element->x[1])
           +SQU(Current.Element->y[2]-Current.Element->y[1])
           +SQU(Current.Element->z[2]-Current.Element->z[1]))
      *
      sqrt(SQU(Current.Element->x[0]-Current.Element->x[2])
           +SQU(Current.Element->y[0]-Current.Element->y[2])
           +SQU(Current.Element->z[0]-Current.Element->z[2]))
      / fabs(DetJac) ;
    break;
  case QUADRANGLE:
    //    Message::Warning("Function CellSize not ready for QUADRANGLE") ;
    Vol = 4. * JacobianSur3D(Current.Element,&Jac) ;
    cellSize = sqrt(Vol);
    break;
  case TETRAHEDRON:
    cellSize = 0.;
    Message::Warning("Function CellSize not ready for TETRAHEDRON") ;
    break;
  case HEXAHEDRON:
    cellSize = 0.;
    Message::Warning("Function CellSize not ready for HEXAHEDRON") ;
    break;
  case PRISM:
    cellSize = 0.;
    Message::Warning("Function CellSize not ready for PRISM") ;
    break;
  default :
    cellSize = 0.;
    Message::Error("Function 'CellSize' not valid for %s",
                   Get_StringForDefine(Element_Type, Current.Element->Type));
  }

  V->Type = SCALAR ;
  V->Val[0] = cellSize ;
  V->Val[MAX_DIM] = 0. ;
  for (k = 2 ; k < std::min(NBR_MAX_HARMONIC, Current.NbrHar) ; k += 2) {
    V->Val[MAX_DIM* k] = V->Val[0] ;
    V->Val[MAX_DIM* (k+1)] = 0. ;
  }
#endif
}

void F_SquNormEdgeValues(F_ARG)
{
#if !defined(HAVE_KERNEL)
  Message::Error("F_SquNormEdgeValues requires Kernel");
#else
  struct Geo_Element  *GeoElement;
  int                  i, *NumNodes;
  double               x [NBR_MAX_NODES_IN_ELEMENT] ;
  double               y [NBR_MAX_NODES_IN_ELEMENT] ;
  double               z [NBR_MAX_NODES_IN_ELEMENT] ;
  double               xe[2], ye[2], ze[2];

  int                  numDofData, Code_BasisFunction, CodeExist = 0;
  struct Dof  * Dof_P = NULL;
  double               Val_Dof, Val_Dof_i, valSum, sizeEdge ;
  int k;

  if(!Current.Element || Current.Element->Num == NO_ELEMENT)
    Message::Error("No element on which to compute 'SquNormEdgeValues'");

  numDofData = (int)Fct->Para[0];
  Code_BasisFunction = (int)Fct->Para[1];

  GeoElement = Current.Element->GeoElement;
  Geo_GetNodesCoordinates
    (GeoElement->NbrNodes, GeoElement->NumNodes, x, y, z) ;

  valSum = 0.;

  if(!GeoElement->NbrEdges) Geo_CreateEdgesOfElement(GeoElement) ;
  for(i=0 ; i<GeoElement->NbrEdges ; i++){
    NumNodes = Geo_GetNodesOfEdgeInElement(GeoElement, i) ;
    xe[0] = x[abs(NumNodes[0])-1]; xe[1] = x[abs(NumNodes[1])-1];
    ye[0] = y[abs(NumNodes[0])-1]; ye[1] = y[abs(NumNodes[1])-1];
    ze[0] = z[abs(NumNodes[0])-1]; ze[1] = z[abs(NumNodes[1])-1];

    CodeExist =
      ((Dof_P =
        Dof_GetDofStruct(Current.DofData_P0+ numDofData,
                         Code_BasisFunction, abs(GeoElement->NumEdges[i]), 0))
       != NULL) ;

    if (CodeExist) {
      sizeEdge = sqrt( SQU(xe[1]-xe[0]) + SQU(ye[1]-ye[0]) + SQU(ze[1]-ze[0]) );

      if(Current.NbrHar==1){
        Dof_GetRealDofValue(Current.DofData_P0+ numDofData, Dof_P, &Val_Dof) ;
        Val_Dof = Val_Dof/sizeEdge;
        valSum += SQU(Val_Dof) * sizeEdge;
      }
      else{
        for (k = 0 ; k < Current.NbrHar ; k+=2) {
          Dof_GetComplexDofValue
            (Current.DofData_P0+ numDofData,
             Dof_P + k/2*gCOMPLEX_INCREMENT,
             &Val_Dof, &Val_Dof_i) ;
          Val_Dof   = Val_Dof  /sizeEdge;
          Val_Dof_i = Val_Dof_i/sizeEdge;
          valSum += (SQU(Val_Dof)+SQU(Val_Dof_i)) * sizeEdge;
        }
      }
    }

  }

  V->Type = SCALAR ;
  V->Val[0] = valSum ;
  V->Val[MAX_DIM] = 0. ;

  for (k = 2 ; k < std::min(NBR_MAX_HARMONIC, Current.NbrHar) ; k += 2) {
    V->Val[MAX_DIM* k] = V->Val[0] ;
    V->Val[MAX_DIM* (k+1)] = 0. ;
  }
#endif
}

static double POINT_TO_PROJECT[3], ELLIPSE_PARAMETERS[2];

static double dist_ellipse(double t)
{
  double x, y;
  x = ELLIPSE_PARAMETERS[0] * cos(t);
  y = ELLIPSE_PARAMETERS[1] * sin(t);
  return sqrt(SQU(x - POINT_TO_PROJECT[0]) + SQU(y - POINT_TO_PROJECT[1]));
}

void F_ProjectPointOnEllipse(F_ARG)
{
  int k;
  double t1 = 0., t2 = 1.e-6, t3, f1, f2, f3, tol = 1.e-4;
  double t, x, y;

  POINT_TO_PROJECT[0] = A->Val[0];
  POINT_TO_PROJECT[1] = A->Val[1];
  POINT_TO_PROJECT[2] = A->Val[2];

  ELLIPSE_PARAMETERS[0] = Fct->Para[0] ;
  ELLIPSE_PARAMETERS[1] = Fct->Para[1] ;

  mnbrak(&t1, &t2, &t3, &f1, &f2, &f3, dist_ellipse);

  if(t1 > t2){
    t = t1;
    t1 = t3;
    t3 = t;
  }

  brent(t1, t2, t3, dist_ellipse, tol, &t);

  x = ELLIPSE_PARAMETERS[0] * cos(t);
  y = ELLIPSE_PARAMETERS[1] * sin(t);

  /* printf("SL(%g,%g,0,%g,%g,0){1,1};\n", A->Val[0], A->Val[1], x, y); */

  for (k = 0 ; k < Current.NbrHar ; k++) {
    V->Val[MAX_DIM*k  ] = 0. ;
    V->Val[MAX_DIM*k+1] = 0. ;
    V->Val[MAX_DIM*k+2] = 0. ;
  }
  V->Val[0] = x;
  V->Val[1] = y;
  V->Type = VECTOR ;
}
