// -*- Mode : c++ -*-
//
// SUMMARY  :
// USAGE    :
// ORG      :
// AUTHOR   : Frederic Hecht
// E-MAIL   : hecht@ann.jussieu.fr
//

/*

 This file is part of Freefem++

 Freefem++ is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License as published by
 the Free Software Foundation; either version 2.1 of the License, or
 (at your option) any later version.

 Freefem++  is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public License
 along with Freefem++; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <cstdio>
#include "Meshio.h"
#include "Mesh2.h"

namespace bamg {

  inline Real8 det3x3(Real8 A[3], Real8 B[3], Real8 C[3]) {
    return A[0] * (B[1] * C[2] - B[2] * C[1]) - A[1] * (B[0] * C[2] - B[2] * C[0]) +
           A[2] * (B[0] * C[1] - B[1] * C[0]);
  }

  SaveMetricInterpole LastMetricInterpole;

  void ReductionSimultanee(MetricAnIso M1, MetricAnIso M2, double &l1, double &l2, D2xD2 &V) {
    double a11 = M1.a11, a21 = M1.a21, a22 = M1.a22;
    double b11 = M2.a11, b21 = M2.a21, b22 = M2.a22;
    //  M1 v = l M2 v
    // (M1 - l M2) v =0
    // det (M1 - l M2) =0
    // det (M1 - l M2) = a l^2 + b l + c;
    // = (a11 - l * b11) * (a22 - l * b22) - (a21 - l * b21 ) ^2
    //  const double eps = 1.e-5;
    const double /*c11 = a11*a11,*/ c21 = a21 * a21;
    const double /*d11 = b11*b11,*/ d21 = b21 * b21;
    const double a = b11 * b22 - d21;
    const double b = -a11 * b22 - a22 * b11 + 2 * a21 * b21;
    const double c = -c21 + a11 * a22;
    const double bb = b * b, ac = a * c;
    const double delta = bb - 4 * ac;
    //  const double kk=c11+c22+c21+d11+d21+d22;
    // modif F Hecht feb 1998
    // cerr.precision(14);
    // cerr  <<  bb << " " << ac << " " <<  bb <<  " " <<a << endl;
    // cerr << a11 << " " << a21 << " " << a22 << endl;
    // cerr << b11 << " " << b21 << " " << b22 << endl;
    if (bb + Abs(ac) < 1.0e-20 || (delta < 1.0E-4 * bb)) {
      // racine double;
      // cerr << "double " << endl ;
      if (Abs(a) < 1.e-30)
        l1 = l2 = 0;
      else
        l1 = l2 = -b / (2 * a);
      V = D2xD2(1, 0, 0, 1);
    } else {
      // cerr << "  -- " << a << endl ;
      const double delta2 = sqrt(delta);
      l1 = (-b - delta2) / (2 * a);
      l2 = (-b + delta2) / (2 * a);
      // M1 v = l M2 v
      //  ( (M1 - I M2) x,y)  = (x,(M1 - I M2) y) \forall y
      // so Ker((M1 - I M2)) = Im((M1 - I M2))^\perp
      double v0 = a11 - l1 * b11, v1 = a21 - l1 * b21, v2 = a22 - l1 * b22;
      double s0 = v0 * v0 + v1 * v1, s1 = v1 * v1 + v2 * v2;
      double vp1x, vp1y, vp2x, vp2y;

      if (s1 < s0)
        s0 = sqrt(s0), vp1x = v1 / s0, vp1y = -v0 / s0;
      else
        s1 = sqrt(s1), vp1x = v2 / s1, vp1y = -v1 / s1;

      v0 = a11 - l2 * b11, v1 = a21 - l2 * b21, v2 = a22 - l2 * b22;
      s0 = v0 * v0 + v1 * v1, s1 = v1 * v1 + v2 * v2;
      if (s1 < s0)
        s0 = sqrt(s0), vp2x = v1 / s0, vp2y = -v0 / s0;
      else
        s1 = sqrt(s1), vp2x = v2 / s1, vp2y = -v1 / s1;
#ifdef DEBUG
      assert(Abs(vp1y) + Abs(vp2y) > 0);
#endif
      V = D2xD2(vp1x, vp2x, vp1y, vp2y);
    }
    return;
  }

  MetricAnIso Intersection(const MetricAnIso M1, const MetricAnIso M2);
  MetricAnIso Intersection(const MetricAnIso M1, const MetricAnIso M2) {
    D2xD2 M;
    double l1, l2;
    ReductionSimultanee(M1, M2, l1, l2, M);
    R2 v0(M.x.x, M.y.x);
    R2 v1(M.x.y, M.y.y);
    D2xD2 M_1(M.inv( ));
    D2xD2 D(Max(M1(v0, v0), M2(v0, v0)), 0, 0, Max(M1(v1, v1), M2(v1, v1)));
    D2xD2 Mi(M_1.t( ) * D * M_1);
    return MetricAnIso(Mi.x.x, 0.5 * (Mi.x.y + Mi.y.x), Mi.y.y);
  }

  MetricAnIso::MetricAnIso(const Real8 a[3], const MetricAnIso m0, const MetricAnIso m1,
                           const MetricAnIso m2) {
    MetricAnIso mab(a[0] * m0.a11 + a[1] * m1.a11 + a[2] * m2.a11,
                    a[0] * m0.a21 + a[1] * m1.a21 + a[2] * m2.a21,
                    a[0] * m0.a22 + a[1] * m1.a22 + a[2] * m2.a22);

    MatVVP2x2 vab(mab);

    R2 v1(vab.v.x, vab.v.y);
    R2 v2(-v1.y, v1.x);

    Real8 h1 = a[0] / m0(v1) + a[1] / m1(v1) + a[2] / m2(v1);
    Real8 h2 = a[0] / m0(v2) + a[1] / m1(v2) + a[2] / m2(v2);

    vab.lambda1 = 1 / (h1 * h1);
    vab.lambda2 = 1 / (h2 * h2);
    *this = vab;
  }

  MetricAnIso::MetricAnIso(Real8 a, const MetricAnIso ma, Real8 b, const MetricAnIso mb) {
    MetricAnIso mab(a * ma.a11 + b * mb.a11, a * ma.a21 + b * mb.a21, a * ma.a22 + b * mb.a22);
    MatVVP2x2 vab(mab);

    R2 v1(vab.v.x, vab.v.y);
    R2 v2(-v1.y, v1.x);

    Real8 h1 = a / ma(v1) + b / mb(v1);
    Real8 h2 = a / ma(v2) + b / mb(v2);
    vab.lambda1 = 1 / (h1 * h1);
    vab.lambda2 = 1 / (h2 * h2);
    *this = vab;
  }

  MatVVP2x2::MatVVP2x2(const MetricAnIso M) {
    double a11 = M.a11, a21 = M.a21, a22 = M.a22;
    const double eps = 1.e-5;
    double c11 = a11 * a11, c22 = a22 * a22, c21 = a21 * a21;
    double b = -a11 - a22, c = -c21 + a11 * a22;
    double delta = b * b - 4 * c;
    double n2 = (c11 + c22 + c21);
    if (n2 < 1e-30)
      lambda1 = lambda2 = 0, v.x = 1, v.y = 0;
    else if (delta < eps * n2) {
      lambda1 = lambda2 = -b / 2, v.x = 1, v.y = 0;
    } else {    //    ---  construction  de 2 vecteur dans (Im ( A - D(i) Id) ortogonal
      delta = sqrt(delta);
      lambda1 = (-b - delta) / 2.0, lambda2 = (-b + delta) / 2.0;
      double v0 = a11 - lambda1, v1 = a21, v2 = a22 - lambda1;
      double s0 = v0 * v0 + v1 * v1, s1 = v1 * v1 + v2 * v2;

      if (s1 < s0)
        s0 = sqrt(s0), v.x = v1 / s0, v.y = -v0 / s0;
      else
        s1 = sqrt(s1), v.x = v2 / s1, v.y = -v1 / s1;
    };
  }

  int MetricAnIso::IntersectWith(const MetricAnIso M2) {
    // cerr << " - " << *this << M2 <<  endl;
    int r = 0;
    MetricAnIso &M1 = *this;
    D2xD2 M;
    double l1, l2;

    ReductionSimultanee(*this, M2, l1, l2, M);
    // cerr << M << endl;
    R2 v1(M.x.x, M.y.x);
    R2 v2(M.x.y, M.y.y);
    double l11 = M1(v1, v1);
    double l12 = M1(v2, v2);
    double l21 = M2(v1, v1);
    double l22 = M2(v2, v2);
    if (l11 < l21) r = 1, l11 = l21;
    if (l12 < l22) r = 1, l12 = l22;
    // cerr << r << endl;
    if (r) {    // change
      D2xD2 M_1(M.inv( ));
      D2xD2 D(l11, 0, 0, l12);
      D2xD2 Mi(M_1.t( ) * D * M_1);
      a11 = Mi.x.x;
      a21 = 0.5 * (Mi.x.y + Mi.y.x);
      a22 = Mi.y.y;
    }
    return r;
  }
  void Triangles::IntersectGeomMetric(const Real8 err = 1, const int iso = 0)

  {
    if (verbosity > 1)
      cout << "  -- IntersectGeomMetric geometric err=" << err << (iso ? " iso " : " aniso ")
           << endl;
    Real8 ss[2] = {0.00001, 0.99999};
    Real8 errC = 2 * sqrt(2 * err);
    Real8 hmax = Gh.MaximalHmax( );
    Real8 hmin = Gh.MinimalHmin( );
    Real8 maxaniso = 1e6;
    assert(hmax > 0);
    SetVertexFieldOn( );
    if (errC > 1) errC = 1;
    for (Int4 i = 0; i < nbe; i++)
      for (int j = 0; j < 2; j++) {

        Vertex V;
        VertexOnGeom GV;
        // cerr << Number(edges[i]) << " " << ss[j] << endl;
        Gh.ProjectOnCurve(edges[i], ss[j], V, GV);
        {
          GeometricalEdge *eg = GV;
          Real8 s = GV;
          R2 tg;
          //	   cerr << i << " " << j << " " << Number(V) << " on = "
          //	<< Gh.Number(eg) << " at s = " << s << " " << endl;
          Real8 R1 = eg->R1tg(s, tg);
          // cerr << " R = " << 1/Max(R1,1e-20) << tg << " on x "
          //    << V.r << errC/ Max(R1,1e-20) <<  " hold=" <<V.m(tg) << " "  << endl;
          Real8 ht = hmax;
          if (R1 > 1.0e-20) {    // err relative to the length of the edge
            ht = Min(Max(errC / R1, hmin), hmax);
          }
          Real8 hn = iso ? ht : Min(hmax, ht * maxaniso);
          // cerr << ht << " " << hn << "m=" << edges[i][j].m <<  endl;
          assert(ht > 0 && hn > 0);
          MatVVP2x2 Vp(1 / (ht * ht), 1 / (hn * hn), tg);
          // cerr << " : " ;
          Metric MVp(Vp);
          // cerr << " : "  << MVp  << endl;
          edges[i][j].m.IntersectWith(MVp);
          // cerr << " . " << endl;
        }
      }
    // the problem is for the vertex on vertex
  }
  /*
  void  Triangles::BoundAnisotropy(Real8 anisomax)
  {
    if (verbosity > 1)
      cout << "  -- BoundAnisotropy by  " << anisomax << endl;
    Real8 h1=1.e30,h2=1e-30,rx=0;
    Real8 coef = 1./(anisomax*anisomax);
    Real8 hn1=1.e30,hn2=1e-30,rnx =1.e-30;
    for (Int4 i=0;i<nbv;i++)
      {

        MatVVP2x2 Vp(vertices[i]);

        h1=Min(h1,Vp.lmin());
        h2=Max(h2,Vp.lmax());
        rx = Max(rx,Vp.Aniso2());

        Vp.BoundAniso2(coef);

        hn1=Min(hn1,Vp.lmin());
        hn2=Max(hn2,Vp.lmax());
        rnx = Max(rnx,Vp.Aniso2());


        vertices[i].m = Vp;

      }

    if (verbosity>2)
      {
        cout << "     input :  Hmin = " << sqrt(1/h2)  << " Hmax = " << sqrt(1/h1)
             << " factor of anisotropy max  = " << sqrt(rx) << endl;
        cout << "     output:  Hmin = " << sqrt(1/hn2) << " Hmax = " << sqrt(1/hn1)
             << " factor of anisotropy max  = " << sqrt(rnx) << endl;
      }
  }
  */
  void Triangles::BoundAnisotropy(Real8 anisomax, Real8 hminaniso) {
    double lminaniso = 1 / (Max(hminaniso * hminaniso, 1e-100));
    if (verbosity > 1) cout << "  -- BoundAnisotropy by  " << anisomax << endl;
    Real8 h1 = 1.e30, h2 = 1e-30, rx = 0;
    Real8 coef = 1. / (anisomax * anisomax);
    Real8 hn1 = 1.e30, hn2 = 1e-30, rnx = 1.e-30;
    for (Int4 i = 0; i < nbv; i++) {

      MatVVP2x2 Vp(vertices[i]);
      double lmax = Vp.lmax( );
      h1 = Min(h1, Vp.lmin( ));
      h2 = Max(h2, Vp.lmax( ));
      rx = Max(rx, Vp.Aniso2( ));

      Vp *= Min(lminaniso, lmax) / lmax;

      Vp.BoundAniso2(coef);

      hn1 = Min(hn1, Vp.lmin( ));
      hn2 = Max(hn2, Vp.lmax( ));
      rnx = Max(rnx, Vp.Aniso2( ));

      vertices[i].m = Vp;
    }

    if (verbosity > 2) {
      cout << "     input :  Hmin = " << sqrt(1 / h2) << " Hmax = " << sqrt(1 / h1)
           << " factor of anisotropy max  = " << sqrt(rx) << endl;
      cout << "     output:  Hmin = " << sqrt(1 / hn2) << " Hmax = " << sqrt(1 / hn1)
           << " factor of anisotropy max  = " << sqrt(rnx) << endl;
    }
  }
  void Triangles::IntersectConsMetric(const double *s, const Int4 nbsol, const int *typsols,
                                      const Real8 hmin1, const Real8 hmax1, const Real8 coef,
                                      const Real8 anisomax, const Real8 CutOff, const int NbJacobi,
                                      const int DoNormalisation, const double power,
                                      const int choice) {    //  the array of solution s is store
    // sol0,sol1,...,soln    on vertex 0
    //  sol0,sol1,...,soln   on vertex 1
    //  etc.
    //  choise = 0 =>  H is computed with green formule
    //   otherwise  => H is computed from P2 on 4T
    const int dim = 2;

    int sizeoftype[] = {1, dim, dim * (dim + 1) / 2, dim * dim};

    // computation of the nb of field
    Int4 ntmp = 0;
    if (typsols) {
      for (Int4 i = 0; i < nbsol; i++)
        if (typsols[i] < 4)
          ntmp += sizeoftype[typsols[i]];
        else
          ntmp += typsols[i] + 1;
    } else
      ntmp = nbsol;

    // n is the total number of fields

    const Int4 n = ntmp;

    Int4 i, k, iA, iB, iC, iv;
    R2 O(0, 0);
    int RelativeMetric = CutOff > 1e-30;
    Real8 hmin = Max(hmin1, MinimalHmin( ));
    Real8 hmax = Min(hmax1, MaximalHmax( ));
    Real8 coef2 = 1 / (coef * coef);

    if (verbosity > 1) {
      cout << "  -- Construction of Metric: Nb of field. " << n << " nbt = " << nbt
           << " nbv= " << nbv << " coef = " << coef << endl
           << "     hmin = " << hmin << " hmax=" << hmax << " anisomax = " << anisomax
           << " Nb Jacobi " << NbJacobi << " Power = " << power;
      if (RelativeMetric)
        cout << " RelativeErr with CutOff= " << CutOff << endl;
      else
        cout << " Absolute Err" << endl;
    }
    double *ss = (double *)s;    //, *ssiii = ss;

    double sA, sB, sC;

    Real8 *detT = new Real8[nbt];
    Real8 *Mmass = new Real8[nbv];
    Real8 *Mmassxx = new Real8[nbv];
    Real8 *dxdx = new Real8[nbv];
    Real8 *dxdy = new Real8[nbv];
    Real8 *dydy = new Real8[nbv];
    Real8 *workT = new Real8[nbt];
    Real8 *workV = new Real8[nbv];
    int *OnBoundary = new int[nbv];
    for (iv = 0; iv < nbv; iv++) {
      Mmass[iv] = 0;
      OnBoundary[iv] = 0;
      Mmassxx[iv] = 0;
    }

    for (i = 0; i < nbt; i++)
      if (triangles[i].link)    // the real triangles
      {
        const Triangle &t = triangles[i];
        // coor of 3 vertices
        R2 A = t[0];
        R2 B = t[1];
        R2 C = t[2];

        // number of the 3 vertices
        iA = Number(t[0]);
        iB = Number(t[1]);
        iC = Number(t[2]);

        Real8 dett = bamg::Area2(A, B, C);
        detT[i] = dett;
        dett /= 6;

        // construction of on boundary
        int nbb = 0;
        for (int j = 0; j < 3; j++) {
          Triangle *ta = t.Adj(j);
          if (!ta || !ta->link)    // no adj triangle => edge on boundary
            OnBoundary[Number(t[VerticesOfTriangularEdge[j][0]])] = 1,
            OnBoundary[Number(t[VerticesOfTriangularEdge[j][1]])] = 1, nbb++;
        }

        workT[i] = nbb;
        Mmass[iA] += dett;
        Mmass[iB] += dett;
        Mmass[iC] += dett;

        if ((nbb == 0) || !choice) {
          Mmassxx[iA] += dett;
          Mmassxx[iB] += dett;
          Mmassxx[iC] += dett;
        }
      } else
        workT[i] = -1;

    //  for (Int4 kcount=0;kcount<n;kcount++,ss++)
    for (Int4 nusol = 0; nusol < nbsol; nusol++) {    // for all Solution

      Real8 smin = ss[0], smax = ss[0];

      Real8 h1 = 1.e30, h2 = 1e-30, rx = 0;
      Real8 coef = 1. / (anisomax * anisomax);
      Real8 hn1 = 1.e30, hn2 = 1e-30, rnx = 1.e-30;
      int nbfield =
        typsols ? (typsols[nusol] < 4 ? sizeoftype[typsols[nusol]] : typsols[nusol] + 1) : 1;
      if (nbfield == 1)
        for (iv = 0, k = 0; iv < nbv; iv++, k += n) {
          dxdx[iv] = dxdy[iv] = dydy[iv] = 0;
          smin = Min(smin, ss[k]);
          smax = Max(smax, ss[k]);
        }
      else {
        //  cas vectoriel
        for (iv = 0, k = 0; iv < nbv; iv++, k += n) {
          double v = 0;
          for (int i = 0; i < nbfield; i++) v += ss[k + i] * ss[k + i];
          v = sqrt(v);
          smin = Min(smin, v);
          smax = Max(smax, v);
        }
      }
      Real8 sdelta = smax - smin;
      Real8 absmax = Max(Abs(smin), Abs(smax));
      Real8 cnorm = DoNormalisation ? coef2 / sdelta : coef2;

      if (verbosity > 2)
        cout << "    Solution " << nusol << " Min = " << smin << " Max = " << smax
             << " Delta =" << sdelta << " cnorm = " << cnorm << " Nb of fields =" << nbfield
             << endl;

      if (sdelta < 1.0e-10 * Max(absmax, 1e-20) && (nbfield == 1)) {
        if (verbosity > 2)
          cout << "      Solution " << nusol << " is constant. We skip. "
               << " Min = " << smin << " Max = " << smax << endl;
        continue;
      }

      double *sf = ss;
      for (Int4 nufield = 0; nufield < nbfield; nufield++, ss++) {
        for (iv = 0, k = 0; iv < nbv; iv++, k += n) dxdx[iv] = dxdy[iv] = dydy[iv] = 0;
        for (i = 0; i < nbt; i++)
          if (triangles[i].link) {    // for real all triangles
            // coor of 3 vertices
            R2 A = triangles[i][0];
            R2 B = triangles[i][1];
            R2 C = triangles[i][2];

            // warning the normal is internal and the
            //   size is the length of the edge
            R2 nAB = Orthogonal(B - A);
            R2 nBC = Orthogonal(C - B);
            R2 nCA = Orthogonal(A - C);
            // remark :  nAB + nBC + nCA == 0

            // number of the 3 vertices
            iA = Number(triangles[i][0]);
            iB = Number(triangles[i][1]);
            iC = Number(triangles[i][2]);

            // for the test of  boundary edge
            // the 3 adj triangles
            Triangle *tBC = triangles[i].TriangleAdj(OppositeEdge[0]);
            Triangle *tCA = triangles[i].TriangleAdj(OppositeEdge[1]);
            Triangle *tAB = triangles[i].TriangleAdj(OppositeEdge[2]);

            // value of the P1 fonction on 3 vertices
            sA = ss[iA * n];
            sB = ss[iB * n];
            sC = ss[iC * n];

            R2 Grads = (nAB * sC + nBC * sA + nCA * sB) / detT[i];
            if (choice) {
              int nbb = 0;
              Real8 dd = detT[i];
              Real8 lla, llb, llc, llf;
              Real8 taa[3][3], bb[3];
              // construction of the trans of lin system
              for (int j = 0; j < 3; j++) {
                int ie = OppositeEdge[j];
                TriangleAdjacent ta = triangles[i].Adj(ie);
                Triangle *tt = ta;
                if (tt && tt->link) {
                  Vertex &v = *ta.OppositeVertex( );
                  R2 V = v;
                  Int4 iV = Number(v);
                  Real8 lA = bamg::Area2(V, B, C) / dd;
                  Real8 lB = bamg::Area2(A, V, C) / dd;
                  Real8 lC = bamg::Area2(A, B, V) / dd;
                  taa[0][j] = lB * lC;
                  taa[1][j] = lC * lA;
                  taa[2][j] = lA * lB;
                  // Real8 xx = V.x-V.y;
                  // Real8 yy = V.x + V.y;
                  // cout << " iv " << ss[iV*n] << " == " << (8*xx*xx+yy*yy)
                  //     << " l = " << lA << " " << lB << " " << lC
                  //     << " = " << lA+lB+lC << " " <<  V << " == " << A*lA+B*lB+C*lC << endl;

                  lla = lA, llb = lB, llc = lC, llf = ss[iV * n];

                  bb[j] = ss[iV * n] - (sA * lA + sB * lB + sC * lC);
                } else {
                  nbb++;
                  taa[0][j] = 0;
                  taa[1][j] = 0;
                  taa[2][j] = 0;
                  taa[j][j] = 1;
                  bb[j] = 0;
                }
              }

              // resolution of 3x3 lineaire system transpose
              Real8 det33 = det3x3(taa[0], taa[1], taa[2]);
              Real8 cBC = det3x3(bb, taa[1], taa[2]);
              Real8 cCA = det3x3(taa[0], bb, taa[2]);
              Real8 cAB = det3x3(taa[0], taa[1], bb);

              assert(det33);
              //	det33=1;
              // verif
              //	cout << " " << (taa[0][0]*cBC +  taa[1][0]*cCA + taa[2][0] * cAB)/det33 << "
              //== " << bb[0] ; 	cout << " " << (taa[0][1]*cBC +  taa[1][1]*cCA + taa[2][1] *
              //cAB)/det33 << " == " << bb[1]; 	cout << " " << (taa[0][2]*cBC +  taa[1][2]*cCA +
              //taa[2][2] * cAB)/det33 << " == " << bb[2]
              //	     << "  -- " ;
              // cout << lla*sA + llb*sB+llc*sC+ (lla*llb* cAB +  llb*llc* cBC + llc*lla*cCA)/det33
              //   << " == " << llf <<  endl;
              // computation of the gradient in the element

              // H( li*lj) = grad li grad lj + grad lj grad lj
              // grad li = njk  / detT ; with i j k ={A,B,C)
              Real8 Hxx = cAB * (nBC.x * nCA.x) + cBC * (nCA.x * nAB.x) + cCA * (nAB.x * nBC.x);
              Real8 Hyy = cAB * (nBC.y * nCA.y) + cBC * (nCA.y * nAB.y) + cCA * (nAB.y * nBC.y);
              Real8 Hxy = cAB * (nBC.y * nCA.x) + cBC * (nCA.y * nAB.x) + cCA * (nAB.y * nBC.x) +
                          cAB * (nBC.x * nCA.y) + cBC * (nCA.x * nAB.y) + cCA * (nAB.x * nBC.y);
              Real8 coef = 1.0 / (3 * dd * det33);
              Real8 coef2 = 2 * coef;
              //	cout << " H = " << Hxx << " " << Hyy << " " <<  Hxy/2 << " coef2 = " <<
              //coef2 << endl;
              Hxx *= coef2;
              Hyy *= coef2;
              Hxy *= coef2;
              // cout << i  << " H = " << 3*Hxx/dd << " " << 3*Hyy/dd << " " <<  3*Hxy/(dd*2) << "
              // nbb = " << nbb << endl;
              if (nbb == 0) {
                dxdx[iA] += Hxx;
                dydy[iA] += Hyy;
                dxdy[iA] += Hxy;

                dxdx[iB] += Hxx;
                dydy[iB] += Hyy;
                dxdy[iB] += Hxy;

                dxdx[iC] += Hxx;
                dydy[iC] += Hyy;
                dxdy[iC] += Hxy;
              }

            } else {

              // if edge on boundary no contribution  => normal = 0
              if (!tBC || !tBC->link) nBC = O;
              if (!tCA || !tCA->link) nCA = O;
              if (!tAB || !tAB->link) nAB = O;

              // remark we forgot a 1/2 because
              //       $\\int_{edge} w_i = 1/2 $ if $i$ is in edge
              //                          0  if not
              // if we don't take the  boundary
              // dxdx[iA] += ( nCA.x + nAB.x ) *Grads.x;

              dxdx[iA] += (nCA.x + nAB.x) * Grads.x;
              dxdx[iB] += (nAB.x + nBC.x) * Grads.x;
              dxdx[iC] += (nBC.x + nCA.x) * Grads.x;

              // warning optimization (1) the divide by 2 is done on the metrix construction
              dxdy[iA] += ((nCA.y + nAB.y) * Grads.x + (nCA.x + nAB.x) * Grads.y);
              dxdy[iB] += ((nAB.y + nBC.y) * Grads.x + (nAB.x + nBC.x) * Grads.y);
              dxdy[iC] += ((nBC.y + nCA.y) * Grads.x + (nBC.x + nCA.x) * Grads.y);

              dydy[iA] += (nCA.y + nAB.y) * Grads.y;
              dydy[iB] += (nAB.y + nBC.y) * Grads.y;
              dydy[iC] += (nBC.y + nCA.y) * Grads.y;
            }

          }    // for real all triangles
        Int4 kk = 0;
        for (iv = 0, k = 0; iv < nbv; iv++, k += n)
          if (Mmassxx[iv] > 0) {
            dxdx[iv] /= 2 * Mmassxx[iv];
            // warning optimization (1) on term dxdy[iv]*ci/2
            dxdy[iv] /= 4 * Mmassxx[iv];
            dydy[iv] /= 2 * Mmassxx[iv];
            // Compute the matrix with abs(eigen value)
            Metric M(dxdx[iv], dxdy[iv], dydy[iv]);
            MatVVP2x2 Vp(M);
            // cout <<iv <<  "  M  = " <<  M <<  " aniso= " << Vp.Aniso() ;
            Vp.Abs( );
            M = Vp;
            dxdx[iv] = M.a11;
            dxdy[iv] = M.a21;
            dydy[iv] = M.a22;
            //  cout << " (abs)  iv M  = " <<  M <<  " aniso= " << Vp.Aniso() <<endl;
          } else
            kk++;

        // correction of second derivate
        // by a laplacien

        Real8 *d2[3] = {dxdx, dxdy, dydy};
        Real8 *dd;
        for (int xy = 0; xy < 3; xy++) {
          dd = d2[xy];
          // do leat 2 iteration for boundary problem
          for (int ijacobi = 0; ijacobi < Max(NbJacobi, 2); ijacobi++) {
            for (i = 0; i < nbt; i++)
              if (triangles[i].link)    // the real triangles
              {
                // number of the 3 vertices
                iA = Number(triangles[i][0]);
                iB = Number(triangles[i][1]);
                iC = Number(triangles[i][2]);
                Real8 cc = 3;
                if (ijacobi == 0)
                  cc = Max((Real8)((Mmassxx[iA] > 0) + (Mmassxx[iB] > 0) + (Mmassxx[iC] > 0)), 1.);
                workT[i] = (dd[iA] + dd[iB] + dd[iC]) / cc;
              }
            for (iv = 0; iv < nbv; iv++) workV[iv] = 0;

            for (i = 0; i < nbt; i++)
              if (triangles[i].link)    // the real triangles
              {
                // number of the 3 vertices
                iA = Number(triangles[i][0]);
                iB = Number(triangles[i][1]);
                iC = Number(triangles[i][2]);
                Real8 cc = workT[i] * detT[i];
                workV[iA] += cc;
                workV[iB] += cc;
                workV[iC] += cc;
              }

            for (iv = 0; iv < nbv; iv++)
              if (ijacobi < NbJacobi || OnBoundary[iv]) dd[iv] = workV[iv] / (Mmass[iv] * 6);
          }
        }

        // constuction  of the metrix from the Hessian dxdx. dxdy,dydy

        Real8 rCutOff = CutOff * absmax;    // relative cut off

        for (iv = 0, k = 0; iv < nbv; iv++, k += n) {    // for all vertices
          //{
          // Metric M(dxdx[iv], dxdy[iv], dydy[iv]);
          // MatVVP2x2 Vp(M);
          // cout << " iv M="<<  M << "  Vp = " << Vp << " aniso  " << Vp.Aniso() << endl;
          //}
          MetricIso Miso;
          // new code to compute ci ---
          Real8 ci;
          if (RelativeMetric) {    //   compute the norm of the solution
            double xx = 0, *sfk = sf + k;
            for (int ifield = 0; ifield < nbfield; ifield++, sfk++) xx += *sfk * *sfk;
            xx = sqrt(xx);
            ci = coef2 / Max(xx, rCutOff);
          } else
            ci = cnorm;

          // old
          //	  Real8 ci = RelativeMetric ? coef2/(Max(Abs(ss[k]),rCutOff)) : cnorm ;
          //   modif F Hecht 101099
          Metric Miv(dxdx[iv] * ci, dxdy[iv] * ci, dydy[iv] * ci);
          MatVVP2x2 Vp(Miv);

          Vp.Abs( );
          if (power != 1.0) Vp.pow(power);

          h1 = Min(h1, Vp.lmin( ));
          h2 = Max(h2, Vp.lmax( ));

          Vp.Maxh(hmin);
          Vp.Minh(hmax);

          rx = Max(rx, Vp.Aniso2( ));

          Vp.BoundAniso2(coef);

          hn1 = Min(hn1, Vp.lmin( ));
          hn2 = Max(hn2, Vp.lmax( ));
          rnx = Max(rnx, Vp.Aniso2( ));

          Metric MVp(Vp);
          vertices[iv].m.IntersectWith(MVp);
        }    // for all vertices
        if (verbosity > 2) {
          cout << "              Field " << nufield << " of solution " << nusol << endl;
          cout << "              before bounding :  Hmin = " << sqrt(1 / h2)
               << " Hmax = " << sqrt(1 / h1) << " factor of anisotropy max  = " << sqrt(rx) << endl;
          cout << "              after  bounding :  Hmin = " << sqrt(1 / hn2)
               << " Hmax = " << sqrt(1 / hn1) << " factor of anisotropy max  = " << sqrt(rnx)
               << endl;
        }
      }    //  end of for all field
    }      // end for all solution

    delete[] detT;
    delete[] Mmass;
    delete[] dxdx;
    delete[] dxdy;
    delete[] dydy;
    delete[] workT;
    delete[] workV;
    delete[] Mmassxx;
    delete[] OnBoundary;
  }

  void Triangles::ReadMetric(const char *fmetrix, const Real8 hmin1 = 1.0e-30,
                             const Real8 hmax1 = 1.0e30, const Real8 coef = 1) {
    Real8 hmin = Max(hmin1, MinimalHmin( ));
    Real8 hmax = Min(hmax1, MaximalHmax( ));
    MeshIstream f_metrix(fmetrix);
    Int4 k, j;
    f_metrix >> k >> j;
    if (verbosity > 1)
      cout << " metrix: open " << fmetrix << ", le coef = " << coef << ", hmin = " << hmin
           << ", hmax = " << hmax << ((j == 1) ? " Iso " : " AnIso ") << endl;

    if (k != nbv || !(j == 1 || j == 3)) {
      cerr << " Error Pb metrix " << k << " <> " << nbv << " or  1 or 3 <> " << j << endl;
      MeshError(1002);
    }

    cout << " j = " << j << endl;
    //  Int4 nberr = 0;
    for (Int4 iv = 0; iv < nbv; iv++) {
      Real8 h;
      if (j == 1) {
        f_metrix >> h;
        vertices[iv].m = Metric(Max(hmin, Min(hmax, h * coef)));
      } else if (j == 3) {
        Real8 a, b, c;
        f_metrix >> a >> b >> c;
        MetricAnIso M(a, b, c);
        MatVVP2x2 Vp(M / coef);

        Vp.Maxh(hmin);
        Vp.Minh(hmax);
        vertices[iv].m = Vp;
      }
    }
  }

  void Triangles::WriteMetric(ostream &f, int iso) {
    if (iso) {
      f << nbv << " " << 1 << endl;
      for (Int4 iv = 0; iv < nbv; iv++) {
        MatVVP2x2 V = vertices[iv].m;
        f << V.hmin( ) << endl;
      }
    } else {
      f << nbv << " " << 3 << endl;
      for (Int4 iv = 0; iv < nbv; iv++)
        f << vertices[iv].m.a11 << " " << vertices[iv].m.a21 << " " << vertices[iv].m.a22 << endl;
    }
  }
  void Triangles::MaxSubDivision(Real8 maxsubdiv) {
    const Real8 maxsubdiv2 = maxsubdiv * maxsubdiv;
#ifdef DRAWING2
    inquire( );
#endif
    if (verbosity > 1)
      cout << "  -- Limit the subdivision of a edges in the new mesh by " << maxsubdiv << endl;
    // for all the edges
    // if the len of the edge is to long
    Int4 it, nbchange = 0;
    Real8 lmax = 0;
    for (it = 0; it < nbt; it++) {
      Triangle &t = triangles[it];
      for (int j = 0; j < 3; j++) {
        Triangle *ptt = t.TriangleAdj(j);
        Triangle &tt = *ptt;
        if (!ptt || ((it < Number(tt) && (tt.link || t.link)))) {
          Vertex &v0 = t[VerticesOfTriangularEdge[j][0]];
          Vertex &v1 = t[VerticesOfTriangularEdge[j][1]];
          R2 AB = (R2)v1 - (R2)v0;
          Metric M = v0;
          Real8 l = M(AB, AB);
          lmax = Max(lmax, l);
          if (l > maxsubdiv2) {
            R2 AC = M.Orthogonal(AB);    // the ortogonal vector of AB in M
            Real8 lc = M(AC, AC);
            D2xD2 Rt(AB, AC);    // Rt.x = AB , Rt.y = AC;
            D2xD2 Rt1(Rt.inv( ));
            D2xD2 D(maxsubdiv2, 0, 0, lc);
            D2xD2 MM = Rt1 * D * Rt1.t( );
#ifdef DRAWING1
            v0.m.Draw(v0);
#endif
            v0.m = M = MetricAnIso(MM.x.x, MM.y.x, MM.y.y);
#ifdef DRAWING1
            v0.m.Draw(v0);
#endif
            //		    cout << " M(AB,AB) = " << M(AB,AB) << " == " << maxsubdiv
            //	 << " M(AC,AC) = " << M(AC,AC) << " == " << lc << endl;
            nbchange++;
          }
          M = v1;
          l = M(AB, AB);
          lmax = Max(lmax, l);
          if (l > maxsubdiv2) {
            R2 AC = M.Orthogonal(AB);    // the ortogonal vector of AB in M
            Real8 lc = M(AC, AC);
            D2xD2 Rt(AB, AC);    // Rt.x = AB , Rt.y = AC;
            D2xD2 Rt1(Rt.inv( ));
            D2xD2 D(maxsubdiv2, 0, 0, lc);
            D2xD2 MM = Rt1 * D * Rt1.t( );
#ifdef DRAWING1
            v1.m.Draw(v1);
#endif
            v1.m = M = MetricAnIso(MM.x.x, MM.y.x, MM.y.y);
#ifdef DRAWING1
            v1.m.Draw(v1);
            inquire( );
#endif
            // cout << " M(AB,AB) = " << M(AB,AB) << " == " << maxsubdiv
            //	 << " M(AC,AC) = " << M(AC,AC) << " == " << lc << endl;
            nbchange++;
          }
        }
      }
    }
    if (verbosity > 3)
      cout << "    Nb of metric change = " << nbchange
           << " Max  of the subdivision of a edges before change  = " << sqrt(lmax) << endl;
#ifdef DRAWING2
    inquire( );
#endif
  }

  void Triangles::SmoothMetric(Real8 raisonmax) {
    if (raisonmax < 1.1) return;
    if (verbosity > 1)
      cout << "  -- Triangles::SmoothMetric raisonmax = " << raisonmax << " " << nbv << endl;
    ReMakeTriangleContainingTheVertex( );
    Int4 i, j, kch, kk, ip;
    Int4 *first_np_or_next_t0 = new Int4[nbv];
    Int4 *first_np_or_next_t1 = new Int4[nbv];
    Int4 Head0 = 0, Head1 = -1;
    Real8 logseuil = log(raisonmax);

    for (i = 0; i < nbv - 1; i++) first_np_or_next_t0[i] = i + 1;
    first_np_or_next_t0[nbv - 1] = -1;    // end;
    for (i = 0; i < nbv; i++) first_np_or_next_t1[i] = -1;
    kk = 0;
    while (Head0 >= 0 && kk++ < 100) {
      kch = 0;
      for (i = Head0; i >= 0; i = first_np_or_next_t0[ip = i],
          first_np_or_next_t0[ip] =
                                -1) {    //  pour tous les triangles autour du sommet s
                                         // 	cout << kk << " i = " << i << " " << ip << endl;
        Triangle *t = vertices[i].t;
        assert(t);
        Vertex &vi = vertices[i];
        TriangleAdjacent ta(t, EdgesVertexTriangle[vertices[i].vint][0]);
        Vertex *pvj0 = ta.EdgeVertex(0);
        while (1) {
          //	  cout << i << " " <<  Number(ta.EdgeVertex(0)) << " "
          //      << Number(ta.EdgeVertex(1)) << "  ---> " ;
          ta = Previous(Adj(ta));
          // cout <<  Number(ta.EdgeVertex(0)) << " " << Number(ta.EdgeVertex(1)) << endl;
          assert(vertices + i == ta.EdgeVertex(1));
          Vertex *pvj = (ta.EdgeVertex(0));
          Vertex &vj = *pvj;
          if (pvj) {
            j = &vj - vertices;
            assert(j >= 0 && j < nbv);
            R2 Aij = (R2)vj - (R2)vi;
            Real8 ll = Norme2(Aij);
            if (0) {
              Real8 hi = ll / vi.m(Aij);
              Real8 hj = ll / vj.m(Aij);
              if (hi < hj) {
                Real8 dh = (hj - hi) / ll;
                // cout << " dh = " << dh << endl;
                if (dh > logseuil) {
                  vj.m.IntersectWith(vi.m / (1 + logseuil * ll / hi));
                  if (first_np_or_next_t1[j] < 0) kch++, first_np_or_next_t1[j] = Head1, Head1 = j;
                }
              }
            } else {
              Real8 li = vi.m(Aij);
              // Real8 lj = vj.m(Aij);
              //		if ( i == 2 || j == 2)
              //  cout << " inter " << i << " " << j << " " << ((1 +logseuil*li)) <<  endl;
              if (vj.m.IntersectWith(vi.m / (1 + logseuil * li)))
                // if( vj.m.IntersectWith(vi.m*(lj/li/(1 +logseuil*lj))) )
                if (first_np_or_next_t1[j] < 0)    // if the metrix change
                  kch++, first_np_or_next_t1[j] = Head1, Head1 = j;
            }
          }
          if (&vj == pvj0) break;
        }
      }
      Head0 = Head1;
      Head1 = -1;
      Exchange(first_np_or_next_t0, first_np_or_next_t1);
      if (verbosity > 5)
        cout << "     Iteration " << kk << " Nb de  vertices with change  " << kch << endl;
    }
    if (verbosity > 2 && verbosity < 5) cout << "    Nb of Loop " << kch << endl;
    delete[] first_np_or_next_t0;
    delete[] first_np_or_next_t1;
  }

  void Geometry::ReadMetric(const char *fmetrix, Real8 hmin = 1.0e-30, Real8 hmax = 1.0e30,
                            Real8 coef = 1) {
    hmin = Max(hmin, MinimalHmin( ));
    MeshIstream f_metrix(fmetrix);
    Int4 k, j;
    f_metrix >> k >> j;
    if (verbosity > 1)
      cout << "  -- ReadMetric  " << fmetrix << ",  coef = " << coef << ", hmin = " << hmin
           << ", hmax = " << hmax << ((j == 1) ? " Iso " : " AnIso ") << endl;

    if (k != nbv || !(j == 1 || j == 3)) {
      cerr << " Error Pb metrix " << k << " <> " << nbv << " or  1 or 3  <> " << j << endl;
      MeshError(1003);
    }

    //  Int4 nberr = 0;
    for (Int4 iv = 0; iv < nbv; iv++) {
      Real8 h;
      if (j == 1) {
        f_metrix >> h;
        vertices[iv].m = Metric(Max(hmin, Min(hmax, h * coef)));
      } else if (j == 3) {
        Real8 a, b, c;
        f_metrix >> a >> b >> c;
        MetricAnIso M(a, b, c);
        MatVVP2x2 Vp(M / coef);
        Vp.Maxh(hmin);
        Vp.Minh(hmax);
        vertices[iv].m = Vp;
      }
    }
  }

  Real8 LengthInterpole(const MetricAnIso Ma, const MetricAnIso Mb, R2 AB) {
    Real8 k = 1. / 2.;
    int level = 0;
    static int kkk = 0;
    static Metric Ms1[32], Ms2[32];
    static Real8 lMs1[32], lMs2[32];
    static double K[32];
    Real8 l = 0, sss = 0;
    Ms1[level] = Ma;
    Ms2[level] = Mb;
    Real8 sa = Ma(AB);
    Real8 sb = Mb(AB);
    lMs1[level] = sa;
    lMs2[level] = sb;
    K[level] = k;
    level++;
    int i = 0;
    Real8 *L = LastMetricInterpole.L, *S = LastMetricInterpole.S;
    Real8 sstop = 0.1;    // Max(0.6,(sa+sb)/5000);
    while (level) {
      level--;
      Metric M1 = Ms1[level];
      Metric M2 = Ms2[level];
      k = K[level];
      Real8 s1 = lMs1[level];
      Real8 s2 = lMs2[level];

      Real8 s = (s1 + s2) * k;
      //    if (level >20  && i < 2030-level)
      //    cout << "                  level " << level << " " << i << " " << s << " " << k <<endl;
      if (s > sstop && level < 30 && i < 500 - level) {
        Metric Mi(0.5, M1, 0.5, M2);
        Real8 si = Mi(AB);
        if (Abs((s1 + s2) - (si + si)) > s1 * 0.001) {
          k = k / 2;
          // we begin by the end to walk in the correct sens from a to b
          // due to the stack
          Ms1[level] = Mi;
          Ms2[level] = M2;
          lMs1[level] = si;
          lMs2[level] = s2;
          K[level] = k;
          level++;
          Ms1[level] = M1;
          Ms2[level] = Mi;
          lMs1[level] = s1;
          lMs2[level] = si;
          K[level] = k;
          level++;
        } else
          L[i] = l += s, S[i] = sss += k, i++;
      } else
        L[i] = l += s, S[i] = sss += k,
        i++;    // cout << i << " l = " << l << " sss = " << sss << endl;
    }
    // warning for optimisation S is in [0:0.5] not in [0:1]
    assert(i < 512);
    LastMetricInterpole.lab = l;
    LastMetricInterpole.opt = i;
    if (i > 200 && kkk++ < 10)
      cout << "Warning LengthInterpole: ( i = " << i << " l = " << l << " sss " << sss << " ) "
           << sstop << endl;
    return l;
  }

  Real8 abscisseInterpole(const MetricAnIso Ma, const MetricAnIso Mb, R2 AB, Real8 s, int optim) {
    if (!optim) LengthInterpole(Ma, Mb, AB);
    Real8 l = s * LastMetricInterpole.lab, r;
    int j = LastMetricInterpole.opt - 1, i = 0, k;

    Real8 *L = LastMetricInterpole.L, *S = LastMetricInterpole.S;
    // warning for optimisation S is the abcisse in [0:0.5]
    // and L is le lenght
    if (l <= L[0])
      r = 2 * S[0] * l / L[0];
    else if (l >= L[j])
      r = 1;
    else {
      while (j - i > 1) {
        k = (i + j) / 2;
        if (l <= L[k])
          j = k;    // l<=L[j]
        else
          i = k;    //  L[i]<l
      };
      //   cout  << i << " " << j  <<" " << L[i] << " " << L[j] << " " << S[i] << " " <<  S[j]  << "
      //   l=" << l << endl;
      if (i == j)
        r = 2 * S[i];
      else
        r = 2 * (S[i] * (L[j] - l) + S[j] * (l - L[i])) / (L[j] - L[i]);
    }
    assert(r <= 1 && r >= 0);
    return r;
  }

#ifdef DRAWING

  void MetricAnIso::Draw(R2 c) const {
    float x = c.x, y = c.y;
    if (InPtScreen(x, y)) {
      R2 X(cos(0.0), sin(0.0));
      X = X / operator( )(X);
      rmoveto(x + X.x, y + X.y);

      for (int i = 1; i <= 100; i++) {
        double t = 2 * Pi * i / 100.0;
        R2 X(cos(t), sin(t));
        X = X / Max(operator( )(X), 1.0e-5);
        rlineto(x + X.x, y + X.y);
      }
    }
  }

  void MetricIso::Draw(R2 c) const {
    float x = c.x, y = c.y;
    if (InPtScreen(x, y)) {
      rmoveto(x + h, y);
      for (int i = 1; i <= 40; i++) {
        double t = Pi * i / 20.0;
        rlineto(x + h * cos(t), y + h * sin(t));
      }
    }
  }

#endif
}    // end of namespace bamg
