#include "DelaunayTriangulation.h"

#include <Eigen/Dense>

#include "SIMPLib/Math/MatrixMath.h"

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
DelaunayTriangulation::DelaunayTriangulation(TriMesh::VertexCoordList vertices, double offset, double tolerance, double alpha, Observable* observable)
: m_Vertices(vertices)
, m_Offset(offset)
, m_Tolerance(tolerance)
, m_Alpha(alpha)
, m_Observer(observable)
{
  initialize();
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
DelaunayTriangulation::~DelaunayTriangulation()
{
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void DelaunayTriangulation::initialize()
{
  m_PointBounds[0] = std::numeric_limits<double>::max();
  m_PointBounds[1] = std::numeric_limits<double>::lowest();
  m_PointBounds[2] = std::numeric_limits<double>::max();
  m_PointBounds[3] = std::numeric_limits<double>::lowest();
  m_PointBounds[4] = std::numeric_limits<double>::max();
  m_PointBounds[5] = std::numeric_limits<double>::lowest();

  m_NumDuplicatePoints = 0;
  m_NumDegeneracies = 0;

  m_Delaunay = TriMesh::NullPointer();
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
TriangleGeom::Pointer DelaunayTriangulation::triangulate()
{
  initialize();

  if(m_Observer)
  {
    connect(this, SIGNAL(filterGeneratedMessage(const PipelineMessage&)), m_Observer, SLOT(broadcastPipelineMessage(const PipelineMessage&)));
  }

  Eigen::Transform<double, 3, Eigen::Affine> transform = findProjectionPlane();

  TriMesh::VertexCoordList projectedVertices(m_Vertices);

  auto numVerts = m_Vertices.size();

  for(size_t v = 0; v < numVerts; v++)
  {
    Eigen::Vector3d point(m_Vertices[v][0], m_Vertices[v][1], m_Vertices[v][2]);
    // Eigen::Vector3d transformedPoint = transform * point;
    // projectedVertices[v][0] = transformedPoint(0);
    // projectedVertices[v][1] = transformedPoint(1);
    // projectedVertices[v][2] = transformedPoint(2);
    projectedVertices[v][0] = point(0);
    projectedVertices[v][1] = point(1);
    projectedVertices[v][2] = point(2);
  }

  findPointBounds(projectedVertices);

  double center[3];
  center[0] = (m_PointBounds[0] + m_PointBounds[1]) / 2.0;
  center[1] = (m_PointBounds[2] + m_PointBounds[3]) / 2.0;
  center[2] = (m_PointBounds[4] + m_PointBounds[5]) / 2.0;

  double diff = 0.0;
  double l = 0.0;

  for(size_t i = 0; i < 3; i++)
  {
    diff = static_cast<double>(m_PointBounds[2 * i + 1]) - static_cast<double>(m_PointBounds[2 * i]);
    l += diff * diff;
  }
  double tol = sqrt(l);
  double radius = m_Offset * tol;
  tol *= m_Tolerance;

  double x[3];

  for(size_t ptId = 0; ptId < 8; ptId++)
  {
    x[0] = center[0] + radius * cos(ptId * 45.0 * SIMPLib::Constants::k_PiOver180);
    x[1] = center[1] + radius * sin(ptId * 45.0 * SIMPLib::Constants::k_PiOver180);
    x[2] = center[2];
    projectedVertices.emplace_back(std::initializer_list<float>{float(x[0]), float(x[1]), float(x[2])});
  }

  m_Delaunay = TriMesh::New(projectedVertices);
  m_Delaunay->addTriangle(numVerts + 0, numVerts + 1, numVerts + 2);
  m_Delaunay->addTriangle(numVerts + 2, numVerts + 3, numVerts + 4);
  m_Delaunay->addTriangle(numVerts + 4, numVerts + 5, numVerts + 6);
  m_Delaunay->addTriangle(numVerts + 6, numVerts + 7, numVerts + 0);
  m_Delaunay->addTriangle(numVerts + 0, numVerts + 2, numVerts + 6);
  m_Delaunay->addTriangle(numVerts + 2, numVerts + 4, numVerts + 6);

  int64_t nei[3];
  int64_t neiPts[3];
  int64_t tri[4];
  int64_t pts[3];
  int64_t nodes[4][3];
  int64_t p1;
  int64_t p2;

  tri[0] = 0;

  int64_t progIncrement = static_cast<int64_t>(numVerts / 100);
  int64_t prog = 1;
  int64_t progressInt = 0;
  int64_t counter = 0;

  for(int64_t ptId = 0; ptId < int64_t(numVerts); ptId++)
  {
    x[0] = projectedVertices[ptId][0];
    x[1] = projectedVertices[ptId][1];
    x[2] = projectedVertices[ptId][2];

    nei[0] = (-1); // where we are coming from...nowhere initially

    if((tri[0] = findTriangle(x, tri[0], tol, nei, pts)) >= 0)
    {
      if(nei[0] < 0) // in triangle
      {
        // delete this triangle; create three new triangles
        // first triangle is replaced with one of the new ones

        nodes[0][0] = ptId;
        nodes[0][1] = pts[0];
        nodes[0][2] = pts[1];
        m_Delaunay->removeLinkFromTriangle(pts[2], tri[0]);
        m_Delaunay->replaceTriangleVertices(nodes[0][0], nodes[0][1], nodes[0][2], tri[0]);
        m_Delaunay->addLinkToTriangle(ptId, tri[0]);

        nodes[1][0] = ptId;
        nodes[1][1] = pts[1];
        nodes[1][2] = pts[2];
        tri[1] = m_Delaunay->addTriangle(nodes[1][0], nodes[1][1], nodes[1][2]);

        nodes[2][0] = ptId;
        nodes[2][1] = pts[2];
        nodes[2][2] = pts[0];
        tri[2] = m_Delaunay->addTriangle(nodes[2][0], nodes[2][1], nodes[2][2]);

        // Check edge neighbors for Delaunay criterion. If not satisfied, flip
        // edge diagonal. (This is done recursively.)
        checkEdge(ptId, x, pts[0], pts[1], tri[0], true);
        checkEdge(ptId, x, pts[1], pts[2], tri[1], true);
        checkEdge(ptId, x, pts[2], pts[0], tri[2], true);
      }

      else // on triangle edge
      {
        // update cell list
        m_Delaunay->getTriangleVertices(nei[0], neiPts);
        for(size_t i = 0; i < 3; i++)
        {
          if(neiPts[i] != nei[1] && neiPts[i] != nei[2])
          {
            p1 = neiPts[i];
          }
          if(pts[i] != nei[1] && pts[i] != nei[2])
          {
            p2 = pts[i];
          }
        }

        m_Delaunay->removeLinkFromTriangle(nei[2], tri[0]);
        m_Delaunay->removeLinkFromTriangle(nei[2], nei[0]);

        nodes[0][0] = ptId;
        nodes[0][1] = p2;
        nodes[0][2] = nei[1];
        m_Delaunay->replaceTriangleVertices(nodes[0][0], nodes[0][1], nodes[0][2], tri[0]);

        nodes[1][0] = ptId;
        nodes[1][1] = p1;
        nodes[1][2] = nei[1];
        m_Delaunay->replaceTriangleVertices(nodes[1][0], nodes[1][1], nodes[1][2], nei[0]);

        m_Delaunay->addLinkToTriangle(ptId, tri[0]);
        m_Delaunay->addLinkToTriangle(ptId, nei[0]);

        tri[1] = nei[0];

        nodes[2][0] = ptId;
        nodes[2][1] = p2;
        nodes[2][2] = nei[2];
        tri[2] = m_Delaunay->addTriangle(nodes[2][0], nodes[2][1], nodes[2][2]);

        nodes[3][0] = ptId;
        nodes[3][1] = p1;
        nodes[3][2] = nei[2];
        tri[3] = m_Delaunay->addTriangle(nodes[3][0], nodes[3][1], nodes[3][2]);

        // Check edge neighbors for Delaunay criterion.
        for(size_t i = 0; i < 4; i++)
        {
          checkEdge(ptId, x, nodes[i][1], nodes[i][2], tri[i], true);
        }
      }
    } // if triangle found

    else
    {
      tri[0] = 0; // no triangle found
    }

    if(counter > prog)
    {
      progressInt = static_cast<int64_t>((static_cast<float>(counter) / numVerts) * 100.0f);
      QString ss = m_MessageTitle + QObject::tr(" || %1% Complete").arg(progressInt);
      (m_MessagePrefix, "", ss);
      prog = prog + progIncrement;
    }
    counter++;
  } // for all points

  std::vector<int64_t> triUse(m_Delaunay->getNumberOfTriangles(), 1);

  for(int64_t ptId = numVerts; ptId < int64_t((numVerts + 8)); ptId++)
  {
    std::vector<int64_t> neighborTris = m_Delaunay->getTrianglesToVertex(ptId);
    for(auto&& neighbor : neighborTris)
    {
      triUse[neighbor] = 0;
    }
  }

  ////alpha begin
  // if(m_Alpha > 0.0)
  //{
  //  double alpha2 = m_Alpha * m_Alpha;
  //  double x1[3], x2[3], x3[3];
  //  double xx1[3], xx2[3], xx3[3];
  //  int64_t cellId, numNei, ap1, ap2, neighbor;

  //  TriMesh::Pointer alphaVert = TriMesh::New(projectedVertices);

  //  vtkCellArray *alphaVerts = vtkCellArray::New();
  //  alphaVerts->Allocate(numPoints);
  //  vtkCellArray *alphaLines = vtkCellArray::New();
  //  alphaLines->Allocate(numPoints);

  //  char *pointUse = new char[numPoints + 8];
  //  for(ptId = 0; ptId < (numPoints + 8); ptId++)
  //  {
  //    pointUse[ptId] = 0;
  //  }

  //  //traverse all triangles; evaluating Delaunay criterion
  //  for(i = 0; i < numTriangles; i++)
  //  {
  //    if(triUse[i] == 1)
  //    {
  //      this->Mesh->GetCellPoints(i, npts, triPts);

  //      // if any point is one of the bounding points that was added
  //      // at the beginning of the algorithm, then grab the points
  //      // from the variable "points" (this list has the boundary
  //      // points and the original points have been transformed by the
  //      // input transform).  if none of the points are bounding points,
  //      // then grab the points from the variable "inPoints" so the alpha
  //      // criterion is applied in the nontransformed space.
  //      if(triPts[0]<numPoints && triPts[1]<numPoints && triPts[2]<numPoints)
  //      {
  //        inPoints->GetPoint(triPts[0], x1);
  //        inPoints->GetPoint(triPts[1], x2);
  //        inPoints->GetPoint(triPts[2], x3);
  //      }
  //      else
  //      {
  //        points->GetPoint(triPts[0], x1);
  //        points->GetPoint(triPts[1], x2);
  //        points->GetPoint(triPts[2], x3);
  //      }

  //      // evaluate the alpha criterion in 3D
  //      vtkTriangle::ProjectTo2D(x1, x2, x3, xx1, xx2, xx3);
  //      if(vtkTriangle::Circumcircle(xx1, xx2, xx3, center) > alpha2)
  //      {
  //        triUse[i] = 0;
  //      }
  //      else
  //      {
  //        for(int j = 0; j<3; j++)
  //        {
  //          pointUse[triPts[j]] = 1;
  //        }
  //      }
  //    }//if non-deleted triangle
  //  }//for all triangles

  //  //traverse all edges see whether we need to create some
  //  for(cellId = 0, triangles->InitTraversal();
  //      triangles->GetNextCell(npts, triPts); cellId++)
  //  {
  //    if(!triUse[cellId])
  //    {
  //      for(i = 0; i < npts; i++)
  //      {
  //        ap1 = triPts[i];
  //        ap2 = triPts[(i + 1) % npts];

  //        if(this->BoundingTriangulation || (ap1<numPoints && ap2<numPoints))
  //        {
  //          this->Mesh->GetCellEdgeNeighbors(cellId, ap1, ap2, neighbors);
  //          numNei = neighbors->GetNumberOfIds();

  //          if(numNei < 1 || ((neighbor = neighbors->GetId(0)) > cellId
  //            && !triUse[neighbor]))
  //          {//see whether edge is shorter than Alpha

  //            // same argument as above, if one is a boundary point, get
  //            // it using this->GetPoint() which are transformed points. if
  //            // neither of the points are boundary points, get the from
  //            // inPoints (untransformed points) so alpha comparison done
  //            // untransformed space
  //            if(ap1 < numPoints && ap2 < numPoints)
  //            {
  //              inPoints->GetPoint(ap1, x1);
  //              inPoints->GetPoint(ap2, x2);
  //            }
  //            else
  //            {
  //              this->GetPoint(ap1, x1);
  //              this->GetPoint(ap2, x2);
  //            }
  //            if((vtkMath::Distance2BetweenPoints(x1, x2)*0.25) <= alpha2)
  //            {
  //              pointUse[ap1] = 1; pointUse[ap2] = 1;
  //              pts[0] = ap1;
  //              pts[1] = ap2;
  //              alphaLines->InsertNextCell(2, pts);
  //            }//if passed test
  //          }//test edge
  //        }//if valid edge
  //      }//for all edges of this triangle
  //    }//if triangle not output
  //  }//for all triangles

  //  //traverse all points, create vertices if none used
  //  for(ptId = 0; ptId<(numPoints + 8); ptId++)
  //  {
  //    if((ptId < numPoints || this->BoundingTriangulation)
  //       && !pointUse[ptId])
  //    {
  //      pts[0] = ptId;
  //      alphaVerts->InsertNextCell(1, pts);
  //    }
  //  }

  //  // update output
  //  delete[] pointUse;
  //  output->SetVerts(alphaVerts);
  //  alphaVerts->Delete();
  //  output->SetLines(alphaLines);
  //  alphaLines->Delete();
  //}
  ////alpha end

  fixupBoundaryTriangles(numVerts, projectedVertices, triUse);

  // Remove the 8 bounding triangle vertices
  TriMesh::VertexList delVerts = m_Delaunay->getVertices();
  for(size_t i = numVerts; i < 8; i++)
  {
    delVerts.pop_back();
  }

  // std::vector<int64_t> vertsToRemove(8);
  // std::iota(vertsToRemove.begin(), vertsToRemove.end(), numVerts);
  // m_Delaunay->removeVertices(vertsToRemove);

  size_t numGoodTris = 0;

  for(size_t i = 0; i < triUse.size(); i++)
  {
    if(triUse[i])
    {
      numGoodTris++;
    }
  }

  std::vector<std::vector<float>> untransformedVerts;
  for(auto i = 0; i < numVerts; i++)
  {
    untransformedVerts.push_back(delVerts[i].vert);
  }

  SharedVertexList::Pointer vertices = TriangleGeom::CreateSharedVertexList(untransformedVerts.size());
  TriangleGeom::Pointer triangles = TriangleGeom::CreateGeometry(numGoodTris, vertices, SIMPL::Geometry::TriangleGeometry);
  float* vertPtr = triangles->getVertexPointer(0);
  int64_t* triPtr = triangles->getTriPointer(0);

  TriMesh::TriList triList = m_Delaunay->getTriangles();

  size_t triIter = 0;
  int64_t triVerts[3];

  for(int64_t i = 0; i < int64_t(triList.size()); i++)
  {
    if(triUse[i])
    {
      m_Delaunay->getTriangleVertices(i, triVerts);
      triPtr[3 * triIter + 0] = triVerts[0];
      triPtr[3 * triIter + 1] = triVerts[1];
      triPtr[3 * triIter + 2] = triVerts[2];
      triIter++;
    }
  }

  for(auto i = 0; i < untransformedVerts.size(); i++)
  {
    Eigen::Vector3d point(untransformedVerts[i][0], untransformedVerts[i][1], untransformedVerts[i][2]);
    // Eigen::Vector3d transformedPoint = transform.inverse() * point;
    // vertPtr[3 * i + 0] = transformedPoint(0);
    // vertPtr[3 * i + 1] = transformedPoint(1);
    // vertPtr[3 * i + 2] = transformedPoint(2);
    vertPtr[3 * i + 0] = point(0);
    vertPtr[3 * i + 1] = point(1);
    vertPtr[3 * i + 2] = point(2);
  }

  return triangles;
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
int64_t DelaunayTriangulation::findTriangle(double x[3], int64_t tri, double tol, int64_t nei[3], int64_t pts[3])
{
  int i, j, ir, ic, inside, i2, i3;
  int64_t newNei;
  double p[3][3], n[2], vp[2], vx[2], dp, minProj;

  m_Delaunay->getTriangleVertices(tri, pts);
  m_Delaunay->getVertexCoordinates(pts[0], p[0]);
  m_Delaunay->getVertexCoordinates(pts[1], p[1]);
  m_Delaunay->getVertexCoordinates(pts[2], p[2]);

  srand(tri);
  ir = rand() % 3;
  const double del2D_tolerance = 1.0e-014;

  for(inside = 1, minProj = del2D_tolerance, ic = 0; ic < 3; ic++)
  {
    i = (ir + ic) % 3;
    i2 = (i + 1) % 3;
    i3 = (i + 2) % 3;

    // create a 2D edge normal to define a "half-space"; evaluate points (i.e.,
    // candidate point and other triangle vertex not on this edge).
    n[0] = -(p[i2][1] - p[i][1]);
    n[1] = p[i2][0] - p[i][0];
    Normalize2x1(n);

    // compute local vectors
    for(j = 0; j < 2; j++)
    {
      vp[j] = p[i3][j] - p[i][j];
      vx[j] = x[j] - p[i][j];
    }

    // check for duplicate point
    Normalize2x1(vp);
    if(Normalize2x1(vx) <= tol)
    {
      m_NumDuplicatePoints++;
      return -1;
    }

    // see if two points are in opposite half spaces
    dp = Dot2D(n, vx) * (Dot2D(n, vp) < 0 ? -1.0 : 1.0);
    if(dp < del2D_tolerance)
    {
      if(dp < minProj) // track edge most orthogonal to point direction
      {
        inside = 0;
        nei[1] = pts[i];
        nei[2] = pts[i2];
        minProj = dp;
      }
    } // outside this edge
  }   // for each edge

  if(inside) // all edges have tested positive
  {
    nei[0] = (-1);
    return tri;
  }

  else if(!inside && (fabs(minProj) < del2D_tolerance)) // on edge
  {
    nei[0] = m_Delaunay->getTriangleEdgeNeighbor(nei[1], nei[2], tri);
    return tri;
  }

  else // walk towards point
  {
    newNei = m_Delaunay->getTriangleEdgeNeighbor(nei[1], nei[2], tri);
    if(newNei == nei[0])
    {
      m_NumDegeneracies++;
      return -1;
    }
    else
    {
      nei[0] = tri;
      return findTriangle(x, newNei, tol, nei, pts);
    }
  }
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void DelaunayTriangulation::checkEdge(int64_t point, double x[3], int64_t p1, int64_t p2, int64_t tri, bool recursive)
{
  double x1[3], x2[3], x3[3];
  int64_t neighbor;

  m_Delaunay->getVertexCoordinates(p1, x1);
  m_Delaunay->getVertexCoordinates(p2, x2);

  neighbor = m_Delaunay->getTriangleEdgeNeighbor(p1, p2, tri);

  if(neighbor > 0) // i.e., not a boundary edge
  {
    // get neighbor info including opposite point
    int64_t oppositeVert = m_Delaunay->getOppositeVertex(p1, p2, neighbor);
    if(oppositeVert < 0)
    {
      return;
    }
    m_Delaunay->getVertexCoordinates(oppositeVert, x3);

    // see whether point is in circumcircle
    if(inCircumcircle(x3, x, x1, x2))
    { // swap diagonal
      m_Delaunay->removeLinkFromTriangle(p1, tri);
      m_Delaunay->removeLinkFromTriangle(p2, neighbor);
      m_Delaunay->addLinkToTriangle(point, neighbor);
      m_Delaunay->addLinkToTriangle(oppositeVert, tri);

      m_Delaunay->replaceTriangleVertices(point, oppositeVert, p2, tri);
      m_Delaunay->replaceTriangleVertices(point, p1, oppositeVert, neighbor);

      if(recursive)
      {
        // two new edges become suspect
        checkEdge(point, x, oppositeVert, p2, tri, true);
        checkEdge(point, x, p1, oppositeVert, neighbor, true);
      }
    } // in circle
  }   // interior edge
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void DelaunayTriangulation::findPointBounds(TriMesh::VertexCoordList& vertices)
{
  for(auto&& vert : vertices)
  {
    if(vert[0] < m_PointBounds[0])
    {
      m_PointBounds[0] = static_cast<double>(vert[0]);
    }
    if(vert[0] > m_PointBounds[1])
    {
      m_PointBounds[1] = static_cast<double>(vert[0]);
    }
    if(vert[1] < m_PointBounds[2])
    {
      m_PointBounds[2] = static_cast<double>(vert[1]);
    }
    if(vert[1] > m_PointBounds[3])
    {
      m_PointBounds[3] = static_cast<double>(vert[1]);
    }
    if(vert[2] < m_PointBounds[4])
    {
      m_PointBounds[4] = static_cast<double>(vert[2]);
    }
    if(vert[2] > m_PointBounds[5])
    {
      m_PointBounds[5] = static_cast<double>(vert[2]);
    }
  }
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void DelaunayTriangulation::fixupBoundaryTriangles(int64_t numVerts, TriMesh::VertexCoordList& points, std::vector<int64_t>& triUse)
{
  bool isConnected;
  int64_t numSwaps = 0;
  int64_t p1;
  int64_t p2;
  int64_t p3;
  int64_t triPts[3];
  int64_t neiPts[3];
  int64_t pts[3];
  int64_t swapPts[3];
  double n1[3];
  double n2[3];

  for(int64_t ptId = 0; ptId < numVerts; ptId++)
  {
    // check if point is only connected to triangles scheduled for
    // removal
    std::vector<int64_t> neighbors = m_Delaunay->getTrianglesToVertex(ptId);
    auto ncells = neighbors.size();

    isConnected = false;

    for(size_t i = 0; i < ncells; i++)
    {
      if(triUse[neighbors[i]])
      {
        isConnected = true;
        break;
      }
    }

    // this point will be connected in the output
    if(isConnected)
    {
      // point is connected: continue
      continue;
    }

    // This point is only connected to triangles scheduled for removal.
    // Therefore it will not be connected in the output triangulation.
    // Let's swap edges to create a triangle with 3 inner points.
    // - inner points have an id < numPoints
    // - boundary point ids are, numPoints <= id < numPoints+8.

    // visit every edge connected to that point.
    // check the 2 triangles touching at that edge.
    // if one triangle is connected to 2 non-boundary points

    for(auto i = 0; i < ncells; i++)
    {
      int64_t tri1 = neighbors[i];
      m_Delaunay->getTriangleVertices(tri1, triPts);

      if(triPts[0] == ptId)
      {
        p1 = triPts[1];
        p2 = triPts[2];
      }
      else if(triPts[1] == ptId)
      {
        p1 = triPts[2];
        p2 = triPts[0];
      }
      else
      {
        p1 = triPts[0];
        p2 = triPts[1];
      }

      // if both p1 & p2 are boundary points,
      // we skip them.
      if(p1 >= numVerts && p2 >= numVerts)
      {
        continue;
      }

      int64_t tri2 = m_Delaunay->getTriangleEdgeNeighbor(p1, p2, tri1);

      // get the 3 points of the neighbor triangle
      m_Delaunay->getTriangleVertices(tri2, neiPts);

      // locate the point different from p1 and p2
      if(neiPts[0] != p1 && neiPts[0] != p2)
      {
        p3 = neiPts[0];
      }
      else if(neiPts[1] != p1 && neiPts[1] != p2)
      {
        p3 = neiPts[1];
      }
      else
      {
        p3 = neiPts[2];
      }

      // create the two new triangles.
      // we just need to replace their pt ids.
      pts[0] = ptId;
      pts[1] = p1;
      pts[2] = p3;

      swapPts[0] = ptId;
      swapPts[1] = p3;
      swapPts[2] = p2;

      findTriangleNormal(pts, n1);
      findTriangleNormal(swapPts, n2);

      // the normals must be along the same direction,
      // or one triangle is upside down.
      if(MatrixMath::DotProduct3x1(n1, n2) < 0.0)
      {
        // do not swap diagonal
        continue;
      }

      // swap edge [p1 p2] and diagonal [ptId p3]

      // it's ok to swap the diagonal
      m_Delaunay->removeLinkFromTriangle(p1, tri2);
      m_Delaunay->removeLinkFromTriangle(p2, tri1);
      m_Delaunay->addLinkToTriangle(ptId, tri2);
      m_Delaunay->addLinkToTriangle(p3, tri1);

      m_Delaunay->replaceTriangleVertices(pts[0], pts[1], pts[2], tri1);
      m_Delaunay->replaceTriangleVertices(swapPts[0], swapPts[1], swapPts[2], tri2);

      triUse[tri1] = (p1 < numVerts && p3 < numVerts);
      triUse[tri2] = (p3 < numVerts && p2 < numVerts);

      // update the 'scheduled for removal' flag of the first triangle.
      // The second triangle was not scheduled for removal anyway.
      numSwaps++;
    }
  }
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
double DelaunayTriangulation::circumcircle(double a[2], double b[2], double c[2], double center[2])
{
  double n12[2] = {0.0, 0.0};
  double n13[2] = {0.0, 0.0};
  double x12[2] = {0.0, 0.0};
  double x13[2] = {0.0, 0.0};
  double sum = 0.0;
  double diff = 0.0;
  size_t i;

  for(i = 0; i < 2; i++)
  {
    n12[i] = b[i] - a[i];
    n13[i] = c[i] - a[i];
    x12[i] = (b[i] + a[i]) / 2.0;
    x13[i] = (c[i] + a[i]) / 2.0;
  }

  Eigen::Matrix2d A;
  A << n12[0], n12[1], n13[0], n13[1];
  Eigen::Vector2d rhs;
  rhs << Dot2D(n12, x12), Dot2D(n13, x13);
  Eigen::Vector2d solution = A.colPivHouseholderQr().solve(rhs);

  center[0] = solution(0);
  center[1] = solution(1);

  for(sum = 0, i = 0; i < 2; i++)
  {
    diff = a[i] - center[i];
    sum += diff * diff;
    diff = b[i] - center[i];
    sum += diff * diff;
    diff = c[i] - center[i];
    sum += diff * diff;
  }

  if((sum /= 3.0) > std::numeric_limits<double>::max())
  {
    return std::numeric_limits<double>::max();
  }
  else
  {
    return sum;
  }
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
int32_t DelaunayTriangulation::inCircumcircle(double p[3], double a[3], double b[3], double c[3])
{
  double radius2 = 0.0;
  double center[2] = {0.0, 0.0};
  double dist2 = 0.0;

  radius2 = circumcircle(a, b, c, center);

  dist2 = (p[0] - center[0]) * (p[0] - center[0]) + (p[1] - center[1]) * (p[1] - center[1]);

  if(dist2 < (0.999999999999 * radius2))
  {
    return 1;
  }
  else
  {
    return 0;
  }
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
double DelaunayTriangulation::Determinant3x3(double g[3][3])
{
  return (g[0][0] * (g[1][1] * g[2][2] - g[1][2] * g[2][1])) - (g[0][1] * (g[1][0] * g[2][2] - g[1][2] * g[2][0])) + (g[0][2] * (g[1][0] * g[2][1] - g[1][1] * g[2][0]));
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
double DelaunayTriangulation::Dot2D(double a[2], double b[2])
{
  return (a[0] * b[0] + a[1] * b[1]);
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
double DelaunayTriangulation::Normalize2x1(double n[2])
{
  double denom;
  denom = sqrt(((n[0] * n[0]) + (n[1] * n[1])));
  if(denom != 0)
  {
    n[0] /= denom;
    n[1] /= denom;
  }
  return denom;
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void DelaunayTriangulation::findTriangleNormal(int64_t triPoints[3], double n[3])
{
  double length;

  double ax, ay, az, bx, by, bz;

  double a[3];
  double b[3];
  double c[3];

  m_Delaunay->getVertexCoordinates(triPoints[0], a);
  m_Delaunay->getVertexCoordinates(triPoints[1], b);
  m_Delaunay->getVertexCoordinates(triPoints[2], c);

  // order is important!!! maintain consistency with triangle vertex order
  ax = c[0] - b[0];
  ay = c[1] - b[1];
  az = c[2] - b[2];
  bx = a[0] - b[0];
  by = a[1] - b[1];
  bz = a[2] - b[2];

  n[0] = (ay * bz - az * by);
  n[1] = (az * bx - ax * bz);
  n[2] = (ax * by - ay * bx);

  if((length = sqrt((n[0] * n[0] + n[1] * n[1] + n[2] * n[2]))) != 0.0)
  {
    n[0] /= length;
    n[1] /= length;
    n[2] /= length;
  }
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
Eigen::Transform<double, 3, Eigen::Affine> DelaunayTriangulation::findProjectionPlane()
{
  auto numVertices = m_Vertices.size();
  double matrix[3][3] = {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}};
  double normal[3] = {0.0, 0.0, 0.0};
  double origin[3] = {0.0, 0.0, 0.0};
  double v[3] = {0.0, 0.0, 0.0};

  double m[9];
  double *c1, *c2, *c3, det;

  const double tolerance = 1.0e-03;

  for(auto&& vert : m_Vertices)
  {
    v[0] += vert[0] * vert[2];
    v[1] += vert[1] * vert[2];
    v[2] += vert[2];

    matrix[0][0] += vert[0] * vert[0];
    matrix[0][1] += vert[0] * vert[1];
    matrix[0][2] += vert[0];

    matrix[1][0] += vert[0] * vert[1];
    matrix[1][1] += vert[1] * vert[1];
    matrix[1][2] += vert[1];

    matrix[2][0] += vert[0];
    matrix[2][1] += vert[1];
  }

  matrix[2][2] = numVertices;

  origin[0] = matrix[0][2] / numVertices;
  origin[1] = matrix[1][2] / numVertices;
  origin[2] = v[2] / numVertices;

  c1 = m;
  c2 = m + 3;
  c3 = m + 6;

  double tmpMatrixInv[3][3] = {{matrix[0][0], matrix[1][0], matrix[2][0]}, {matrix[0][1], matrix[1][1], matrix[2][1]}, {matrix[0][2], matrix[1][2], matrix[2][2]}};

  double tmpMatrix0[3][3] = {{v[0], matrix[1][0], matrix[2][0]}, {v[1], matrix[1][1], matrix[2][1]}, {v[2], matrix[1][2], matrix[2][2]}};

  double tmpMatrix1[3][3] = {{matrix[0][0], v[0], matrix[2][0]}, {matrix[0][1], v[1], matrix[2][1]}, {matrix[0][2], v[2], matrix[2][2]}};

  if((det = Determinant3x3(tmpMatrixInv)) > tolerance)
  {
    normal[0] = Determinant3x3(tmpMatrix0) / det;
    normal[1] = Determinant3x3(tmpMatrix1) / det;
    normal[2] = -1.0;
  }

  double zAxis[3] = {0.0, 0.0, 1.0};
  double rotationAxis[3] = {0.0, 0.0, 0.0};

  MatrixMath::Normalize3x1(normal);
  MatrixMath::CrossProduct(normal, zAxis, rotationAxis);
  MatrixMath::Normalize3x1(rotationAxis);

  double rotationAngle = acos(MatrixMath::DotProduct3x1(zAxis, normal));

  Eigen::Transform<double, 3, Eigen::Affine> transform =
      Eigen::AngleAxisd(rotationAngle, Eigen::Vector3d(rotationAxis[0], rotationAxis[1], rotationAxis[2])) * Eigen::Translation3d(-origin[0], -origin[1], -origin[2]);

  return transform;
}


/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*
 Copyright (C) 2017 Klaus Spanderen

 This file is part of QuantLib, a free-software/open-source library
 for financial quantitative analysts and developers - http://quantlib.org/

 QuantLib is free software: you can redistribute it and/or modify it
 under the terms of the QuantLib license.  You should have received a
 copy of the license along with this program; if not, please email
 <quantlib-dev@lists.sf.net>. The license is also available online at
 <http://quantlib.org/license.shtml>.

 This program 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 license for more details.
*/

#include "utilities.hpp"
#include "squarerootclvmodel.hpp"
#include <ql/quotes/simplequote.hpp>
#include <ql/time/daycounters/actualactual.hpp>
#include <ql/time/daycounters/actual365fixed.hpp>
#include <ql/time/calendars/nullcalendar.hpp>
#include <ql/instruments/impliedvolatility.hpp>
#include <ql/instruments/forwardvanillaoption.hpp>
#include <ql/math/statistics/statistics.hpp>
#include <ql/math/integrals/gausslobattointegral.hpp>
#include <ql/math/randomnumbers/rngtraits.hpp>
#include <ql/math/randomnumbers/sobolbrownianbridgersg.hpp>
#include <ql/math/optimization/constraint.hpp>
#include <ql/math/optimization/simplex.hpp>
#include <ql/processes/squarerootprocess.hpp>
#include <ql/methods/montecarlo/multipathgenerator.hpp>
#include <ql/pricingengines/blackcalculator.hpp>
#include <ql/pricingengines/vanilla/analytichestonengine.hpp>
#include <ql/pricingengines/vanilla/analyticeuropeanengine.hpp>
#include <ql/pricingengines/forward/forwardengine.hpp>
#include <ql/methods/montecarlo/pathgenerator.hpp>
#include <ql/termstructures/volatility/equityfx/hestonblackvolsurface.hpp>
#include <ql/termstructures/volatility/equityfx/noexceptlocalvolsurface.hpp>
#include <ql/experimental/models/squarerootclvmodel.hpp>
#include <ql/experimental/models/hestonslvfdmmodel.hpp>
#include <ql/experimental/processes/hestonslvprocess.hpp>
#include <ql/experimental/finitedifferences/fdhestondoublebarrierengine.hpp>
#include <ql/experimental/barrieroption/analyticdoublebarrierbinaryengine.hpp>
#include <ql/experimental/volatility/sabrvoltermstructure.hpp>

#if defined(__GNUC__) && !defined(__clang__) && BOOST_VERSION > 106300
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
#include <boost/math/distributions/non_central_chi_squared.hpp>
#if defined(__GNUC__) && !defined(__clang__) && BOOST_VERSION > 106300
#pragma GCC diagnostic pop
#endif

#include <set>
#include <utility>

using namespace QuantLib;
using namespace boost::unit_test_framework;

namespace square_root_clv_model {
    class CLVModelPayoff : public PlainVanillaPayoff {
      public:
        CLVModelPayoff(Option::Type type, Real strike, ext::function<Real(Real)> g)
        : PlainVanillaPayoff(type, strike), g_(std::move(g)) {}

        Real operator()(Real x) const override { return PlainVanillaPayoff::operator()(g_(x)); }

      private:
        const ext::function<Real(Real)> g_;
    };

    typedef boost::math::non_central_chi_squared_distribution<Real>
        chi_squared_type;
}

void SquareRootCLVModelTest::testSquareRootCLVVanillaPricing() {
    BOOST_TEST_MESSAGE(
        "Testing vanilla option pricing with square-root kernel process...");

    using namespace square_root_clv_model;

    SavedSettings backup;

    const Date todaysDate(5, Oct, 2016);
    Settings::instance().evaluationDate() = todaysDate;

    const DayCounter dc = ActualActual(ActualActual::ISDA);
    const Date maturityDate = todaysDate + Period(3, Months);
    const Time maturity = dc.yearFraction(todaysDate, maturityDate);

    const Real s0 = 100;
    const Handle<Quote> spot(ext::make_shared<SimpleQuote>(s0));

    const Rate r = 0.08;
    const Rate q = 0.03;
    const Volatility vol = 0.3;

    const Handle<YieldTermStructure> rTS(flatRate(r, dc));
    const Handle<YieldTermStructure> qTS(flatRate(q, dc));
    const Handle<BlackVolTermStructure> volTS(flatVol(todaysDate, vol, dc));
    const Real fwd = s0*qTS->discount(maturity)/rTS->discount(maturity);

    const ext::shared_ptr<GeneralizedBlackScholesProcess> bsProcess(
        ext::make_shared<GeneralizedBlackScholesProcess>(
            spot, qTS, rTS, volTS));

    const Real kappa       = 1.0;
    const Real theta       = 0.06;
    const Volatility sigma = 0.2;
    const Real x0          = 0.09;

    const ext::shared_ptr<SquareRootProcess> sqrtProcess(
        ext::make_shared<SquareRootProcess>(theta, kappa, sigma, x0));

    const std::vector<Date> maturityDates(1, maturityDate);

    const SquareRootCLVModel model(
        bsProcess, sqrtProcess, maturityDates, 14, 1-1e-14, 1e-14);

    const Array x = model.collocationPointsX(maturityDate);
    const Array y = model.collocationPointsY(maturityDate);

    const LagrangeInterpolation g(x.begin(), x.end(), y.begin());

    const Real df  = 4*theta*kappa/(sigma*sigma);
    const Real ncp = 4*kappa*std::exp(-kappa*maturity)
            / (sigma*sigma*(1-std::exp(-kappa*maturity)))*sqrtProcess->x0();

    const chi_squared_type dist(df, ncp);

    const Real strikes[] = { 50, 75, 100, 125, 150, 200 };
    for (double strike : strikes) {
        const Option::Type optionType = (strike > fwd) ? Option::Call : Option::Put;

        const Real expected = BlackCalculator(
            optionType, strike, fwd,
            std::sqrt(volTS->blackVariance(maturity, strike)),
            rTS->discount(maturity)).value();

        const CLVModelPayoff clvModelPayoff(optionType, strike, g);

        const ext::function<Real(Real)> f = [&](Real xi) {
            return clvModelPayoff(xi) * boost::math::pdf(dist, xi);
        };

        const Real calculated = GaussLobattoIntegral(1000, 1e-6)(
            f, x.front(), x.back()) * rTS->discount(maturity);

        const Real tol = 5e-3;
        if (std::fabs(expected - calculated) > tol) {
            BOOST_FAIL("failed to reproduce option SquaredCLVMOdel prices"
                    << "\n    time:       " << maturityDate
                    << "\n    strike:     " << strike
                    << "\n    expected:   " << expected
                    << "\n    calculated: " << calculated);
        }
    }
}

void SquareRootCLVModelTest::testSquareRootCLVMappingFunction() {
    BOOST_TEST_MESSAGE(
        "Testing mapping function of the square-root kernel process...");

    using namespace square_root_clv_model;

    SavedSettings backup;

    const Date todaysDate(16, Oct, 2016);
    Settings::instance().evaluationDate() = todaysDate;
    const Date maturityDate = todaysDate + Period(1, Years);

    const DayCounter dc = Actual365Fixed();

    const Real s0 = 100;
    const Handle<Quote> spot(ext::make_shared<SimpleQuote>(s0));

    const Rate r = 0.05;
    const Rate q = 0.02;

    const Handle<YieldTermStructure> rTS(flatRate(r, dc));
    const Handle<YieldTermStructure> qTS(flatRate(q, dc));

    //SABR
    const Real beta =  0.95;
    const Real alpha=  0.2;
    const Real rho  = -0.9;
    const Real gamma=  0.8;

    const Handle<BlackVolTermStructure> sabrVol(
        ext::make_shared<SABRVolTermStructure>(
            alpha, beta, gamma, rho, s0, r, todaysDate, dc));

    const ext::shared_ptr<GeneralizedBlackScholesProcess> bsProcess(
        ext::make_shared<GeneralizedBlackScholesProcess>(
            spot, qTS, rTS, sabrVol));

    std::vector<Date> calibrationDates(1, todaysDate + Period(3, Months));
    calibrationDates.reserve(Size(daysBetween(todaysDate, maturityDate)/7 + 1));
    while (calibrationDates.back() < maturityDate)
        calibrationDates.push_back(calibrationDates.back() + Period(1, Weeks));

    // sqrt process
    const Real kappa       = 1.0;
    const Real theta       = 0.09;
    const Volatility sigma = 0.2;
    const Real x0          = 0.09;

    const ext::shared_ptr<SquareRootProcess> sqrtProcess(
        ext::make_shared<SquareRootProcess>(theta, kappa, sigma, x0));

    const SquareRootCLVModel model(
        bsProcess, sqrtProcess, calibrationDates, 14, 1-1e-10, 1e-10);

    const ext::function<Real(Time, Real)> g = model.g();

    const Real strikes[] = { 80, 100, 120 };
    const Size offsets[] = { 92, 182, 183, 184, 185, 186, 365 };
    for (unsigned long offset : offsets) {
        const Date m = todaysDate + Period(offset, Days);
        const Time t = dc.yearFraction(todaysDate, m);

        const Real df  = 4*theta*kappa/(sigma*sigma);
        const Real ncp = 4*kappa*std::exp(-kappa*t)
                / (sigma*sigma*(1-std::exp(-kappa*t)))*sqrtProcess->x0();

        const chi_squared_type dist(df, ncp);

        const Real fwd = s0*qTS->discount(m)/rTS->discount(m);

        for (double strike : strikes) {
            const Option::Type optionType = (strike > fwd) ? Option::Call : Option::Put;

            const Real expected = BlackCalculator(
                optionType, strike, fwd,
                std::sqrt(sabrVol->blackVariance(m, strike)),
                rTS->discount(m)).value();

            const CLVModelPayoff clvModelPayoff(optionType, strike, [&](Real x) { return g(t, x); });

            const ext::function<Real(Real)> f = [&](Real xi) {
                return clvModelPayoff(xi) * boost::math::pdf(dist, xi);
            };

            const Array x = model.collocationPointsX(m);
            const Real calculated = GaussLobattoIntegral(1000, 1e-3)(
                f, x.front(), x.back()) * rTS->discount(m);

            const Real tol = 0.075;

            if (std::fabs(expected) > 0.01
                    && std::fabs((calculated - expected)/calculated) > tol) {
                BOOST_FAIL("failed to reproduce option SquaredCLVMOdel prices"
                        << "\n    time:       " << m
                        << "\n    strike:     " << strike
                        << "\n    expected:   " << expected
                        << "\n    calculated: " << calculated);
            }
        }
    }
}

namespace square_root_clv_model {
    class SquareRootCLVCalibrationFunction : public CostFunction {
      public:
        SquareRootCLVCalibrationFunction(Array strikes,
                                         const std::vector<Date>& resetDates,
                                         const std::vector<Date>& maturityDates,
                                         ext::shared_ptr<GeneralizedBlackScholesProcess> bsProcess,
                                         Array refVols,
                                         Size nScenarios = 10000)
        : strikes_(std::move(strikes)), resetDates_(resetDates), maturityDates_(maturityDates),
          bsProcess_(std::move(bsProcess)), refVols_(std::move(refVols)), nScenarios_(nScenarios) {
            std::set<Date> c(resetDates.begin(), resetDates.end());
            c.insert(maturityDates.begin(), maturityDates.end());
            calibrationDates_.insert(
                calibrationDates_.begin(), c.begin(), c.end());
        }

        Real value(const Array& params) const override {
            const Array diff = values(params);

            Real retVal = 0.0;
            for (double i : diff)
                retVal += i * i;

            return retVal;
        }

        Disposable<Array> values(const Array& params) const override {
            const Real theta = params[0];
            const Real kappa = params[1];
            const Real sigma = params[2];
            const Real x0    = params[3];

            const ext::shared_ptr<SimpleQuote> vol(
                ext::make_shared<SimpleQuote>(0.1));

            const Handle<YieldTermStructure> rTS(bsProcess_->riskFreeRate());
            const Handle<YieldTermStructure> qTS(bsProcess_->dividendYield());
            const Handle<Quote> spot(ext::make_shared<SimpleQuote>(
                bsProcess_->x0()));

            const ext::shared_ptr<PricingEngine> fwdEngine(
                ext::make_shared<ForwardVanillaEngine<AnalyticEuropeanEngine> >(
                    ext::make_shared<GeneralizedBlackScholesProcess>(
                        spot, qTS, rTS,
                        Handle<BlackVolTermStructure>(
                            flatVol(rTS->referenceDate(), vol,
                                    rTS->dayCounter())))));

            const ext::shared_ptr<SquareRootProcess> sqrtProcess(
                ext::make_shared<SquareRootProcess>(theta, kappa, sigma, x0));

            const SquareRootCLVModel clvSqrtModel(
                bsProcess_, sqrtProcess, calibrationDates_,
                14, 1-1e-14, 1e-14);

            const ext::function<Real(Time, Real)> gSqrt = clvSqrtModel.g();

            Array retVal(resetDates_.size()*strikes_.size());

            for (Size i=0, n=resetDates_.size(); i < n; ++i) {
                const Date resetDate = resetDates_[i];
                const Date maturityDate = maturityDates_[i];

                const Time t0 = bsProcess_->time(resetDate);
                const Time t1 = bsProcess_->time(maturityDate);

                const Real df  = 4*theta*kappa/(sigma*sigma);
                const Real ncp = 4*kappa*std::exp(-kappa*t0)
                    / (sigma*sigma*(1-std::exp(-kappa*t0)))*x0;

                typedef boost::math::non_central_chi_squared_distribution<Real>
                    chi_squared_type;

                const chi_squared_type dist(df, ncp);

                const Real ncp1 = 4*kappa*std::exp(-kappa*(t1-t0))
                    / (sigma*sigma*(1-std::exp(-kappa*(t1-t0))));

                const LowDiscrepancy::ursg_type ursg = LowDiscrepancy::ursg_type(2, 1235UL);

                std::vector<GeneralStatistics> stats(strikes_.size());

                for (Size j=0; j < nScenarios_; ++j) {
                    const std::vector<Real>& path = ursg.nextSequence().value;

                    const Real x1 = boost::math::quantile(dist, path[0]);
                    const Real u1 =
                        sigma*sigma*(1-std::exp(-kappa*t0))/(4*kappa)*x1;

                    const Real x2 = boost::math::quantile(
                        chi_squared_type(df, ncp1*u1), path[1]);
                    const Real u2 =
                        sigma*sigma*(1-std::exp(-kappa*(t1-t0)))/(4*kappa)*x2;
                    const Real X2 =
                        u2*4*kappa/(sigma*sigma*(1-std::exp(-kappa*t1)));

                    const Real s1 = gSqrt(t0, x1);
                    const Real s2 = gSqrt(t1, X2);

                    for (Size k=0; k < strikes_.size(); ++k) {
                        const Real strike = strikes_[k];

                        const Real payoff = (strike < 1.0)
                            ?  s1 * std::max(0.0, strike - s2/s1)
                            :  s1 * std::max(0.0, s2/s1 - strike);

                        stats[k].add(payoff);
                    }
                }

                const ext::shared_ptr<Exercise> exercise(
                    ext::make_shared<EuropeanExercise>(maturityDate));

                const DiscountFactor dF(
                    bsProcess_->riskFreeRate()->discount(maturityDate));

                for (Size k=0; k < strikes_.size(); ++k) {
                    const Real strike = strikes_[k];
                    const Real npv = stats[k].mean() * dF;

                    const ext::shared_ptr<StrikedTypePayoff> payoff(
                        ext::make_shared<PlainVanillaPayoff>(
                            (strike < 1.0) ? Option::Put : Option::Call, strike));

                    const ext::shared_ptr<ForwardVanillaOption> fwdOption(
                        ext::make_shared<ForwardVanillaOption>(
                            strike, resetDate, payoff, exercise));

                    const Volatility implVol =
                        QuantLib::detail::ImpliedVolatilityHelper::calculate(
                            *fwdOption, *fwdEngine, *vol, npv, 1e-8, 200, 1e-4, 2.0);

                    const Size idx = k + i*strikes_.size();
                    retVal[idx] = implVol - refVols_[idx];
                }
            }

            return retVal;
        }

      private:
        const Array strikes_;
        const std::vector<Date> resetDates_, maturityDates_;
        const ext::shared_ptr<GeneralizedBlackScholesProcess> bsProcess_;
        const Array refVols_;
        const Size nScenarios_;

        std::vector<Date> calibrationDates_;
    };

    class NonZeroConstraint : public Constraint {
      private:
        class Impl : public Constraint::Impl {
          public:
            bool test(const Array& params) const override {
                const Real theta = params[0];
                const Real kappa = params[1];
                const Real sigma = params[2];
                const Real x0    = params[3];

                return (sigma >= 0.001 && kappa > 1e-6 && theta > 0.001
                        && x0 > 1e-4);
            }

            Array upperBound(const Array& params) const override {
                const Real upper[] = { 1.0, 1.0, 1.0, 2.0 };

                return Array(upper, upper + 4);
            }

            Array lowerBound(const Array& params) const override {
                const Real lower[] = { 0.001, 0.001, 0.001, 1e-4 };

                return Array(lower, lower + 4);
            }
        };

      public:
        NonZeroConstraint()
        : Constraint(ext::make_shared<NonZeroConstraint::Impl>()) {}
    };
}

void SquareRootCLVModelTest::testForwardSkew() {
    BOOST_TEST_MESSAGE(
        "Testing forward skew dynamics with square-root kernel process...");

    using namespace square_root_clv_model;

    SavedSettings backup;

    const Date todaysDate(16, Oct, 2016);
    Settings::instance().evaluationDate() = todaysDate;
    const Date endDate = todaysDate + Period(4, Years);

    const DayCounter dc = Actual365Fixed();

    // Heston model is used to generate an arbitrage free volatility surface
    const Real s0    =  100;
    const Real r     =  0.1;
    const Real q     =  0.05;
    const Real v0    =  0.09;
    const Real kappa =  1.0;
    const Real theta =  0.09;
    const Real sigma =  0.3;
    const Real rho   = -0.75;

    const Handle<Quote> spot(ext::make_shared<SimpleQuote>(s0));
    const Handle<YieldTermStructure> rTS(flatRate(r, dc));
    const Handle<YieldTermStructure> qTS(flatRate(q, dc));

    const ext::shared_ptr<HestonModel> hestonModel(
        ext::make_shared<HestonModel>(
            ext::make_shared<HestonProcess>(
                rTS, qTS, spot, v0, kappa, theta, sigma, rho)));

    const Handle<BlackVolTermStructure> blackVol(
        ext::make_shared<HestonBlackVolSurface>(
            Handle<HestonModel>(hestonModel)));

    const Handle<LocalVolTermStructure> localVol(
        ext::make_shared<NoExceptLocalVolSurface>(
                blackVol, rTS, qTS, spot, std::sqrt(theta)));

    const Real sTheta = 0.389302;
    const Real sKappa = 0.1101849;
    const Real sSigma = 0.275368;
    const Real sX0    = 0.466809;

    const ext::shared_ptr<SquareRootProcess> sqrtProcess(
        ext::make_shared<SquareRootProcess>(
            sTheta, sKappa, sSigma, sX0));

    const ext::shared_ptr<GeneralizedBlackScholesProcess> bsProcess(
        ext::make_shared<GeneralizedBlackScholesProcess>(
            spot, qTS, rTS, blackVol));

    std::vector<Date> calibrationDates(1, todaysDate + Period(6, Months));
    while (calibrationDates.back() < endDate)
        calibrationDates.push_back(calibrationDates.back() + Period(3, Months));

    std::set<Date> clvCalibrationDates(
        calibrationDates.begin(), calibrationDates.end());

    Date tmpDate = todaysDate + Period(1, Days);
    while (tmpDate < todaysDate + Period(1, Years)) {
        clvCalibrationDates.insert(tmpDate);
        tmpDate += Period(1, Weeks);
    }

    const SquareRootCLVModel clvSqrtModel(
        bsProcess,
        sqrtProcess,
        std::vector<Date>(
            clvCalibrationDates.begin(), clvCalibrationDates.end()),
        14, 1-1e-14, 1e-14);

    const ext::function<Real(Time, Real)> gSqrt = clvSqrtModel.g();

    const ext::shared_ptr<SimpleQuote> vol(
        ext::make_shared<SimpleQuote>(0.1));

    const ext::shared_ptr<PricingEngine> fwdEngine(
        ext::make_shared<ForwardVanillaEngine<AnalyticEuropeanEngine> >(
            ext::make_shared<GeneralizedBlackScholesProcess>(
                spot, qTS, rTS,
                Handle<BlackVolTermStructure>(flatVol(todaysDate, vol, dc)))));

    // forward skew of the Heston-SLV model
    std::vector<Time> mandatoryTimes;
    mandatoryTimes.reserve(calibrationDates.size());
    for (auto& calibrationDate : calibrationDates)
        mandatoryTimes.push_back(dc.yearFraction(todaysDate, calibrationDate));

    const Size tSteps = 200;
    const TimeGrid grid(mandatoryTimes.begin(), mandatoryTimes.end(), tSteps);

    std::vector<Date> resetDates, maturityDates;
    std::vector<Size> resetIndices, maturityIndices;
    for (Size i=0, n = calibrationDates.size()-2; i < n; ++i) {
        resetDates.push_back(calibrationDates[i]);
        maturityDates.push_back(calibrationDates[i+2]);

        const Time resetTime    = mandatoryTimes[i];
        const Time maturityTime = mandatoryTimes[i+2];

        resetIndices.push_back(grid.closestIndex(resetTime)-1);
        maturityIndices.push_back(grid.closestIndex(maturityTime)-1);
    }

    const Real strikes[] = {
        0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2,
        1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0
    };

    const Size nScenarios = 20000;
    Array refVols(resetIndices.size()*LENGTH(strikes));

    // finite difference calibration of Heston SLV model

    // define Heston Stochastic Local Volatility model
    const Real eta = 0.25;
    const Real corr = -0.0;

    const ext::shared_ptr<HestonProcess> hestonProcess4slv(
        ext::make_shared<HestonProcess>(
            rTS, qTS, spot, v0, kappa, theta, eta*sigma, corr));

    const Handle<HestonModel> hestonModel4slv(
        ext::make_shared<HestonModel>(hestonProcess4slv));

    const HestonSLVFokkerPlanckFdmParams logParams = {
        301, 601, 1000, 30, 2.0, 0, 2,
        0.1, 1e-4, 10000,
        1e-5, 1e-5, 0.0000025, 1.0, 0.1, 0.9, 1e-5,
        FdmHestonGreensFct::Gaussian,
        FdmSquareRootFwdOp::Log,
        FdmSchemeDesc::ModifiedCraigSneyd()
    };

    const ext::shared_ptr<LocalVolTermStructure> leverageFctFDM =
        HestonSLVFDMModel(localVol, hestonModel4slv, endDate, logParams).
            leverageFunction();

    //  calibrating to forward volatility dynamics

    const ext::shared_ptr<HestonSLVProcess> fdmSlvProcess(
        ext::make_shared<HestonSLVProcess>(
            hestonProcess4slv, leverageFctFDM));

    std::vector<std::vector<GeneralStatistics> > slvStats(
        calibrationDates.size()-2,
            std::vector<GeneralStatistics>(LENGTH(strikes)));

    typedef SobolBrownianBridgeRsg rsg_type;
    typedef MultiPathGenerator<rsg_type>::sample_type sample_type;

    const Size factors = fdmSlvProcess->factors();

    const ext::shared_ptr<MultiPathGenerator<rsg_type> > pathGen(
        ext::make_shared<MultiPathGenerator<rsg_type> >(
            fdmSlvProcess, grid, rsg_type(factors, grid.size()-1), false));

    for (Size k=0; k < nScenarios; ++k) {
        const sample_type& path = pathGen->next();

        for (Size i=0, n=resetIndices.size(); i < n; ++i) {
            const Real S_t1 = path.value[0][resetIndices[i]];
            const Real S_T1 = path.value[0][maturityIndices[i]];

            for (Size j=0; j < LENGTH(strikes); ++j) {
                const Real strike = strikes[j];
                    slvStats[i][j].add((strike < 1.0)
                        ? S_t1 * std::max(0.0, strike - S_T1/S_t1)
                        : S_t1 * std::max(0.0, S_T1/S_t1 - strike));
            }

        }
    }

    for (Size i=0, n=resetIndices.size(); i < n; ++i) {
        const Date resetDate = calibrationDates[i];
        const Date maturityDate(calibrationDates[i+2]);
        const DiscountFactor df = rTS->discount(maturityDate);

        const ext::shared_ptr<Exercise> exercise(
            ext::make_shared<EuropeanExercise>(maturityDate));

        for (Size j=0; j < LENGTH(strikes); ++j) {
            const Real strike = strikes[j];
            const Real npv = slvStats[i][j].mean()*df;

            const ext::shared_ptr<StrikedTypePayoff> payoff(
                ext::make_shared<PlainVanillaPayoff>(
                    (strike < 1.0) ? Option::Put : Option::Call, strike));

            const ext::shared_ptr<ForwardVanillaOption> fwdOption(
                ext::make_shared<ForwardVanillaOption>(
                    strike, resetDate, payoff, exercise));

            const Volatility implVol =
                QuantLib::detail::ImpliedVolatilityHelper::calculate(
                    *fwdOption, *fwdEngine, *vol, npv, 1e-8, 200, 1e-4, 2.0);

            const Size idx = j + i*LENGTH(strikes);
            refVols[idx] = implVol;
        }
    }

    SquareRootCLVCalibrationFunction costFunction(
        Array(strikes, strikes+LENGTH(strikes)),
        resetDates,
        maturityDates,
        bsProcess,
        refVols,
        nScenarios);

    NonZeroConstraint nonZeroConstraint;

    CompositeConstraint constraint(
        nonZeroConstraint,
        HestonModel::FellerConstraint());

    Array params(4);
    params[0] = sTheta; params[1] = sKappa;
    params[2] = sSigma; params[3] = sX0;

    //    Optimization would take too long
    //
    //    Problem prob(costFunction, nonZeroConstraint, params);
    //
    //    Simplex simplex(0.05);
    //    simplex.minimize(prob, EndCriteria(400, 40, 1.0e-8, 1.0e-8, 1.0e-8));

    const Real tol = 0.5;
    const Real costValue = costFunction.value(params);

    if (costValue > tol) {
        BOOST_FAIL("failed to reproduce small cost function value"
                << "\n    value:       " << costValue
                << "\n    tolerance:   " << tol);
    }

    const Date maturityDate = todaysDate + Period(1, Years);
    const Time maturityTime = bsProcess->time(maturityDate);

    const ext::shared_ptr<Exercise> europeanExercise(
        ext::make_shared<EuropeanExercise>(maturityDate));

    VanillaOption vanillaATMOption(
        ext::make_shared<PlainVanillaPayoff>(Option::Call,
            s0*qTS->discount(maturityDate)/rTS->discount(maturityDate)),
        europeanExercise);

    vanillaATMOption.setPricingEngine(
        ext::make_shared<AnalyticHestonEngine>(hestonModel));

    const Volatility atmVol = vanillaATMOption.impliedVolatility(
        vanillaATMOption.NPV(),
        ext::make_shared<GeneralizedBlackScholesProcess>(spot, qTS, rTS,
            Handle<BlackVolTermStructure>(flatVol(std::sqrt(theta), dc))));

    const ext::shared_ptr<PricingEngine> analyticEngine(
        ext::make_shared<AnalyticDoubleBarrierBinaryEngine>(
            ext::make_shared<GeneralizedBlackScholesProcess>(
                spot, qTS, rTS,
                Handle<BlackVolTermStructure>(flatVol(atmVol, dc)))));

    const ext::shared_ptr<PricingEngine> fdSLVEngine(
        ext::make_shared<FdHestonDoubleBarrierEngine>(
            hestonModel4slv.currentLink(),
            51, 201, 51, 1,
            FdmSchemeDesc::Hundsdorfer(), leverageFctFDM));

    const Size n = 16;
    Array barrier_lo(n), barrier_hi(n), bsNPV(n), slvNPV(n);

    const ext::shared_ptr<CashOrNothingPayoff> payoff =
        ext::make_shared<CashOrNothingPayoff>(Option::Call, 0.0, 1.0);

    for (Size i=0; i < n; ++i) {
        const Real dist = 20.0+5.0*i;

        barrier_lo[i] = std::max(s0 - dist, 1e-2);
        barrier_hi[i] = s0 + dist;
        DoubleBarrierOption doubleBarrier(
            DoubleBarrier::KnockOut, barrier_lo[i], barrier_hi[i], 0.0,
            payoff,
            europeanExercise);

        doubleBarrier.setPricingEngine(analyticEngine);
        bsNPV[i] = doubleBarrier.NPV();

        doubleBarrier.setPricingEngine(fdSLVEngine);
        slvNPV[i] = doubleBarrier.NPV();
    }

    const TimeGrid bGrid(maturityTime, tSteps);

    const PseudoRandom::ursg_type ursg = PseudoRandom::ursg_type(tSteps, 1235UL);

    std::vector<GeneralStatistics> stats(n);

    const Real df = 4*sTheta*sKappa/(sSigma*sSigma);

    for (Size i=0; i < nScenarios; ++i) {
        std::vector<bool> touch(n, false);

        const std::vector<Real>& path = ursg.nextSequence().value;

        Real x = sX0;

        for (Size j=0; j < tSteps; ++j) {
            const Time t0 = bGrid.at(j);
            const Time t1 = bGrid.at(j+1);

            const Real ncp = 4*sKappa*std::exp(-sKappa*(t1-t0))
                / (sSigma*sSigma*(1-std::exp(-sKappa*(t1-t0))))*x;

            const boost::math::non_central_chi_squared_distribution<Real>
                dist(df, ncp);

            const Real u = boost::math::quantile(dist, path[j]);

            x = sSigma*sSigma*(1-std::exp(-sKappa*(t1-t0)))/(4*sKappa) * u;

            const Real X = x*4*sKappa/(sSigma*sSigma*(1-std::exp(-sKappa*t1)));

            const Real s = gSqrt(t1, X);

            if (t1 > 0.05) {
                for (Size u=0; u < n; ++u) {
                    if (s <= barrier_lo[u] || s >= barrier_hi[u]) {
                        touch[u] = true;
                    }
                }
            }
        }
        for (Size u=0; u < n; ++u) {
            if (touch[u]) {
                stats[u].add(0.0);
            }
            else {
                stats[u].add(rTS->discount(maturityDate));
            }
        }
    }

    for (Size u=0; u < n; ++u) {
        const Real calculated = stats[u].mean();
        const Real error = stats[u].errorEstimate();
        const Real expected = slvNPV[u];

        const Real tol = 2.35*error;

        if (std::fabs(calculated-expected) > tol) {
            BOOST_FAIL("failed to reproduce CLV double no touch barrier price"
                    << "\n    CLV value:   " << calculated
                    << "\n    error    :   " << error
                    << "\n    SLV value: " << expected);
        }
    }
}

test_suite* SquareRootCLVModelTest::experimental() {
    auto* suite = BOOST_TEST_SUITE("SquareRootCLVModel tests");

    suite->add(QUANTLIB_TEST_CASE(
        &SquareRootCLVModelTest::testSquareRootCLVVanillaPricing));

    suite->add(QUANTLIB_TEST_CASE(
        &SquareRootCLVModelTest::testSquareRootCLVMappingFunction));

//    this test takes very long
//    suite->add(QUANTLIB_TEST_CASE(
//        &SquareRootCLVModelTest::testForwardSkew));

    return suite;
}


#ifndef __SCHEME_UTILITY_COMPLEXTYPE
#define __SCHEME_UTILITY_COMPLEXTYPE

#include "rationaltype.hpp"
#include "bigint.hpp"
#include <boost/operators.hpp>

class ComplexType:
    public boost::equality_comparable<ComplexType,
    boost::arithmetic<ComplexType>
    >
{
    bool exact_;
    RationalType realr_, imagr_;
    long double reald_, imagd_;

    public:
        ComplexType();
        ComplexType(const RationalType& a, const RationalType& b);
        ComplexType(const RationalType& a);
        ComplexType(const RationalType& a, const long double b);
        ComplexType(const long double a);
        ComplexType(const long double a, const RationalType& b);
        ComplexType(const long double a, const long double b);
        bool operator == (const ComplexType& b) const;
        ComplexType& operator += (const ComplexType& b);
        ComplexType& operator -= (const ComplexType& b);
        ComplexType& operator *= (const ComplexType& b);
        ComplexType& operator /= (const ComplexType& b);
        friend std::istream& operator >>(std::istream& i, ComplexType& a);
        friend std::ostream& operator <<(std::ostream& o, const ComplexType& a);
        bool exact() const;
        bool isReal() const;
        bool isRational() const;
        bool isInt() const;
        BigInt toInt() const;
        RationalType getRealR() const;
        RationalType getImagR() const;
        long double getRealD() const;
        long double getImagD() const;
        ComplexType& setRealR(const RationalType& b);
        ComplexType& setImagR(const RationalType& b);
        ComplexType& setRealD(const long double b);
        ComplexType& setImagD(const long double d);
        ComplexType operator -();
        ComplexType& toexact();
        ComplexType& toinexact();

};

#endif


//          Copyright Carl Philipp Reh 2009 - 2016.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#include <fcppt/algorithm/set_difference.hpp>
#include <fcppt/preprocessor/disable_gcc_warning.hpp>
#include <fcppt/preprocessor/pop_warning.hpp>
#include <fcppt/preprocessor/push_warning.hpp>
#include <fcppt/config/external_begin.hpp>
#include <boost/test/unit_test.hpp>
#include <set>
#include <fcppt/config/external_end.hpp>

FCPPT_PP_PUSH_WARNING
FCPPT_PP_DISABLE_GCC_WARNING(-Weffc++)

BOOST_AUTO_TEST_CASE(
	algorithm_set_difference
)
{
FCPPT_PP_POP_WARNING

	typedef
	std::set<
		int
	>
	int_set;

	BOOST_CHECK((
		fcppt::algorithm::set_difference(
			int_set{
				1, 2, 3
			},
			int_set{
				2, 3, 4
			}
		)
		==
		int_set{
			1
		}
	));
}


// Copyright Louis Dionne 2013

// Use, modification and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy
// at http://www.boost.org/LICENSE_1_0.txt)

#include <boost/assert.hpp>
#include <boost/graph/directed_graph.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/hawick_circuits.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/next_prior.hpp>
#include <boost/property_map/property_map.hpp>
#include <cstdlib>
#include <iostream>
#include <iterator>
#include <map>

template < typename OutputStream > struct cycle_printer
{
    cycle_printer(OutputStream& stream) : os(stream) {}

    template < typename Path, typename Graph >
    void cycle(Path const& p, Graph const& g)
    {
        if (p.empty())
            return;

        // Get the property map containing the vertex indices
        // so we can print them.
        typedef typename boost::property_map< Graph,
            boost::vertex_index_t >::const_type IndexMap;

        IndexMap indices = get(boost::vertex_index, g);

        // Iterate over path printing each vertex that forms the cycle.
        typename Path::const_iterator i, before_end = boost::prior(p.end());
        for (i = p.begin(); i != before_end; ++i)
        {
            os << get(indices, *i) << " ";
        }
        os << get(indices, *i) << '\n';
    }
    OutputStream& os;
};

// VertexPairIterator is an iterator over pairs of whitespace separated
// vertices `u` and `v` representing a directed edge from `u` to `v`.
template < typename Graph, typename VertexPairIterator >
void build_graph(Graph& graph, unsigned int const nvertices,
    VertexPairIterator first, VertexPairIterator last)
{
    typedef boost::graph_traits< Graph > Traits;
    typedef typename Traits::vertex_descriptor vertex_descriptor;
    std::map< unsigned int, vertex_descriptor > vertices;

    for (unsigned int i = 0; i < nvertices; ++i)
        vertices[i] = add_vertex(graph);

    for (; first != last; ++first)
    {
        unsigned int u = *first++;

        BOOST_ASSERT_MSG(first != last,
            "there is a lonely vertex at the end of the edge list");

        unsigned int v = *first;

        BOOST_ASSERT_MSG(vertices.count(u) == 1 && vertices.count(v) == 1,
            "specified a vertex over the number of vertices in the graph");

        add_edge(vertices[u], vertices[v], graph);
    }
    BOOST_ASSERT(num_vertices(graph) == nvertices);
}

int main(int argc, char const* argv[])
{
    if (argc < 2)
    {
        std::cout << "usage: " << argv[0] << " num_vertices < input\n";
        return EXIT_FAILURE;
    }

    unsigned int num_vertices = boost::lexical_cast< unsigned int >(argv[1]);
    std::istream_iterator< unsigned int > first_vertex(std::cin), last_vertex;
    boost::directed_graph<> graph;
    build_graph(graph, num_vertices, first_vertex, last_vertex);

    cycle_printer< std::ostream > visitor(std::cout);
    boost::hawick_circuits(graph, visitor);

    return EXIT_SUCCESS;
}


// Copyright (C) 2001-2003
// William E. Kempf
//
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <boost/thread/thread.hpp>
#include <boost/thread/once.hpp>
#include <iostream>
using namespace std;

template <int n> struct F{

    enum {Result = n * F<n -1>::Result};
};

template <> struct F<1>{

    enum {Result = 1};
};

int main(int argc, char* argv[])
{
cout<< F<3>::Result<<endl;
}


#include "SPlisHSPlasH/Common.h"
#include <Eigen/Dense>
#include <iostream>
#include "SPlisHSPlasH/Utilities/Timing.h"
#include "Utilities/PartioReaderWriter.h"
#include "Utilities/OBJLoader.h"
#include "SPlisHSPlasH/Utilities/PoissonDiskSampling.h"
#include "Utilities/FileSystem.h"
#include "Utilities/StringTools.h"

// Enable memory leak detection
#ifdef _DEBUG
#ifndef EIGEN_ALIGN
	#define new DEBUG_NEW
#endif
#endif

using namespace SPH;
using namespace Eigen;
using namespace std;

string inputFile = "";
string outputFile = "";
Real particleRadius = 0.025;
Vector3r scale = Vector3r::Ones();

// main
int main( int argc, char **argv )
{
	REPORT_MEMORY_LEAKS;

	for (int i = 1; i < argc; i++)
	{
		string argStr = argv[i];
		string type_str = argStr.substr(0, 2);
		if ((type_str == "-r") && (i+1 < argc))
			particleRadius = stof(argv[++i]);
		else if ((type_str == "-s") && (i + 1 < argc))
		{
			vector<string> tokens;
			StringTools::tokenize(argv[++i], tokens, ",");
			scale[0] = stof(tokens[0]);
			scale[1] = stof(tokens[1]);
			scale[2] = stof(tokens[2]);
		}
		else if (i + 1 < argc)
		{
			inputFile = argv[i];
			outputFile = argv[++i];
		}
		else
		{
			std::cerr << "Not enough parameters!\n";
			std::cerr << "Usage: SurfaceSampling.exe [-r particle_radius] [-s scaleX,scaleY,scaleZ] in.obj out.bgeo\n";
			return -1;
		}
	}

	TriangleMesh mesh;
	OBJLoader::loadObj(inputFile, mesh, scale);

	std::cout << "Surface sampling of " << inputFile << "\n";
	START_TIMING("Poisson disk sampling");
	PoissonDiskSampling sampling;
	std::vector<Vector3r> samplePoints;
	sampling.sampleMesh(mesh.numVertices(), mesh.getVertices().data(), mesh.numFaces(), mesh.getFaces().data(), particleRadius, 10, 1, samplePoints);
	STOP_TIMING_AVG;
	std::cout << "Number of sample points: " << samplePoints.size() << "\n";

	PartioReaderWriter::writeParticles(outputFile, (unsigned int) samplePoints.size(), samplePoints.data(), NULL, particleRadius);

	Timing::printAverageTimes();
	Timing::printTimeSums();

	return 0;
}


#include "mex.h"
#include <Eigen/Dense>
#include <Eigen/SparseCore>
#include "mexHelpers.cpp"
#include "panozo/Param_State.h"
#include "panozo/GlobalLocalParametrization.h"
#include "panozo/StateManager.h"
#include <igl/components.h>
#include <iostream>

using namespace std;
using namespace Eigen;

void mexFunction(int nlhs, mxArray *plhs[],
	int nrhs, const mxArray*prhs[])
{
	// assign input
	int n_tri = mxGetM(prhs[0]); // # rows of F
	int d_simplex = mxGetN(prhs[0]); // # cols of F
	int n_vert = mxGetM(prhs[1]); // # rows of V
	int dim = mxGetN(prhs[1]); // # cols of V
	const Map<MatrixXd, Aligned> Fmatlab(mxGetPr(prhs[0]), n_tri, d_simplex);
	const Map<MatrixXd, Aligned> V(mxGetPr(prhs[1]), n_vert, dim);

	// update index numbers to 0-base
	MatrixXd Fd (Fmatlab);
	Fd = Fd.array() - 1;
    MatrixXi F = MatrixXi::Zero(Fd.rows(), Fd.cols());

    for(int i = 0; i < Fd.rows(); i++)
    {
        for(int j = 0; j < Fd.cols(); j++)
        {
            F(i, j) = Fd(i, j);
        }
    }

	// compute

    Param_State state;

    state.method = Param_State::GLOBAL_ARAP_IRLS;
    state.flips_linesearch = true;
    state.update_all_energies = false;
    state.proximal_p = 0.0001;

    state.V = V;
    state.F = F;
    state.v_num = state.V.rows();
    state.f_num = state.F.rows();

    igl::doublearea(state.V,state.F, state.M); state.M /= 2.;

    state.global_local_energy = Param_State::SYMMETRIC_DIRICHLET;
    state.cnt_flips = false;

    state.mesh_area = state.M.sum();
    //state.V /= sqrt(state.mesh_area);
    //state.mesh_area = 1;

    StateManager state_manager;
    GlobalLocalParametrization param(state_manager, &state);

    param.init_parametrization();

    MatrixXd UV = state.uv;

	// assign outputs
	mapDenseMatrixToMex(UV, &(plhs[0]));
}


#include <cmath>
#include <map>
#include <math.h>  // pow
#include <sys/time.h>
#include <sys/resource.h>   // check the memory usage

#include <iostream>
#include <stdio.h>
#include <thread>
#include <vector>

#include <NTL/ZZ.h>
#include <NTL/BasicThreadPool.h>

#include <HELR/TestScheme.h>
#include <HELR/Cipher.h>
#include <HELR/CZZ.h>
#include <HELR/EvaluatorUtils.h>
#include <HELR/Message.h>
#include <HELR/Params.h>
#include <HELR/PubKey.h>
#include <HELR/Scheme.h>
#include <HELR/SchemeAlgo.h>
#include <HELR/SchemeAux.h>
#include <HELR/SecKey.h>
#include <HELR/StringUtils.h>

#include "LRtest.h"
#include "HELR.h"

#define debug 0

long LogReg::getctlvl(Cipher& ctxt){
    long nBits = NumBits(ctxt.mod)-1;
    return nBits;
}

//---------------------------------------------------------------------------------------------------

//! @ output ztrain = (2*y[k]-1) * (1, x[k])  -> encryption: zTrainCipher
void LogReg::EncryptData(Cipher*& zTrainCipher, dMat zTrain){

    //long slots = LRparams.n_training;

    CZZ**  mvec= new CZZ*[LRparams.dim1];

    // zTrainCipher = (2*y[k]-1) * (1, x[k])
    for (long i = 0; i < LRparams.dim1; ++i){
        mvec[i]= new CZZ[LRparams.nslots];
        for(int k=0; k< LRparams.nslots; ++k){
            if(zTrain[k][i]!=0){
                mvec[i][k].r = scaleup(zTrain[k][i], LRparams.logp-3);  // logp/8
            }
            else{
                mvec[i][k].r = to_ZZ("0");
            }
            mvec[i][k].i = to_ZZ("0");
        }
    }

    NTL_EXEC_RANGE(LRparams.dim1, first, last);
    for (long i = first; i < last; ++i){
        zTrainCipher[i] = scheme.encrypt(mvec[i], LRparams.nslots);
    }
    NTL_EXEC_RANGE_END;

    delete[] mvec;

}

void LogReg::show_and_compare(dVec& theta, dMat zTrain, CZZ*& dtheta){

    LR_poly(theta, zTrain, LRparams);

    double maxErrorBit = 0.0;
    double minRelativeBit= 20.0;

    for(int i=0; i< LRparams.dim1; i++){
        ZZ msg_scaleup= scaleup(theta[i], LRparams.logp);

        cout << "m " << i << " : ["  <<  msg_scaleup << "], theta: " << theta[i] << endl;   // unencrypted

        double theta;
        conv(theta, dtheta[i].r);
        theta = scaledown(theta, LRparams.logp);

        cout << "d " << i << " : ["  << dtheta[i].r << "], (HE)theta: " << theta << endl;      // encrypted
        cout << "e " << i << " : ["  << (msg_scaleup - dtheta[i].r)  << "],  ";    // error

        // Msg-bit
        ZZ msgbnd= dtheta[i].r;
        double MsgBit = 0.0;
        if(msgbnd!=0)
            MsgBit= (log(abs(msgbnd))/log(2)) ;
        cout << "Msg: " << MsgBit << ", ";

        // Error-bit
        ZZ error = (msg_scaleup - dtheta[i].r);
        double ErrorBit = 0.0;
        if(error!=0)
            ErrorBit= (log(abs(error))/log(2)) ;
        cout << "Error: " << ErrorBit << endl;

        double RelativeBit= MsgBit- ErrorBit;

        if(maxErrorBit < ErrorBit) maxErrorBit= ErrorBit;
        if(minRelativeBit > RelativeBit)  minRelativeBit= RelativeBit;
        cout << "-------------------------------------------------------------" << endl;

    }

    cout << "MAX error bit : " << maxErrorBit << ", Min relative-error bit: " << minRelativeBit<< endl;
    cout << "-------------------------------------------------------------" << endl;

}

void LogReg::HElogreg(Cipher*& thetaCipher, Cipher*& zTrainCipher,  dMat zTrain){
    struct rusage usage;
    auto start= chrono::steady_clock::now();

    // zSumCipher = 1/(n/polyscale) * sum (2y[k]-1) x[k] = 1/n * sum z[k]
    // At the first round, we just take zTrainCipher as theta (lvl= 2)
    Cipher* zSumCipher= new Cipher[LRparams.dim1];

    //! rot and sum
    Cipher* ctemp= new Cipher[LRparams.dim1];

    NTL_EXEC_RANGE(LRparams.dim1, first, last);
    for (long i = first; i < last; ++i){
        zSumCipher[i]= zTrainCipher[i];

        for(long j= 0; j< LRparams.logn; ++j){
            int l = (1<<j);
            ctemp[i] = scheme.leftRotate(zSumCipher[i], l);
            scheme.addAndEqual(zSumCipher[i], ctemp[i]);
        }

        scheme.modSwitchAndEqual(zSumCipher[i], LRparams.logn - LRparams.log2polyscale);
        thetaCipher[i] = zSumCipher[i];
    }
    NTL_EXEC_RANGE_END;

    auto end = std::chrono::steady_clock::now();
    auto diff = end - start;
    double timeElapsed= chrono::duration <double, milli> (diff).count()/1000.0;
    double totaltime = timeElapsed;

    int ret = getrusage(RUSAGE_SELF,&usage);

    cout << "-------------------------------------------------------------" << endl;
    cout << "1-iter : mod(theta)= " << getctlvl(thetaCipher[0]) << ", running time= "  << timeElapsed << "s, " ;
    cout<<  "Mem= " << usage.ru_maxrss/(1024)  << "MB" << endl;
    cout << "-------------------------------------------------------------" << endl;

    dVec mtheta(LRparams.dim1, 0.0);
    CZZ* dtheta = new CZZ[LRparams.dim1];
    for(int i = 0; i< LRparams.dim1; ++i){
        dtheta[i] = (scheme.decrypt(secretKey, thetaCipher[i]))[0];
    }

    show_and_compare(mtheta, zTrain, dtheta);

    //--------------------------------------------------------------
    double zlvl =  getctlvl(zSumCipher[0]);

    for(int j= 1; j< LRparams.max_iter; ++j){
        auto start= chrono::steady_clock::now();

        Cipher* gradCipher= new Cipher[LRparams.dim1];

        switch(LRparams.polydeg){
            case 3:
                gradCipher= getgrad_deg3(thetaCipher, zTrainCipher);
                break;
            case 7:
                gradCipher= getgrad_deg7(thetaCipher, zTrainCipher);
                break;

        }

        double tlvl = getctlvl(thetaCipher[0]);
        double glvl = getctlvl(gradCipher[0]);

        NTL_EXEC_RANGE(LRparams.dim1, first, last);
        for (long i = first; i < last; ++i){
            Cipher ztemp = scheme.modEmbed(zSumCipher[i], zlvl- tlvl);
            scheme.addAndEqual(thetaCipher[i], ztemp);

            scheme.modEmbedAndEqual(thetaCipher[i], tlvl- glvl);
            scheme.subAndEqual(thetaCipher[i], gradCipher[i]);
        }
        NTL_EXEC_RANGE_END;

        auto end = std::chrono::steady_clock::now();
        auto diff = end - start;
        double timeElapsed= chrono::duration <double, milli> (diff).count()/1000.0;
        totaltime += timeElapsed;

        ret = getrusage(RUSAGE_SELF,&usage);

        delete[] gradCipher;

        cout << "-------------------------------------------------------------" << endl;
        cout << j+1 << "-iter : mod(theta)= " << getctlvl(thetaCipher[0]) << ", running time= "  << timeElapsed << "s, " ;
        cout<<  "Mem= " << usage.ru_maxrss/1024  << "MB" << endl;
        cout << "-------------------------------------------------------------" << endl;

        for(int i=0; i< LRparams.dim1; ++i){
            dtheta[i] = (scheme.decrypt(secretKey, thetaCipher[i]))[0];
        }

        show_and_compare(mtheta, zTrain, dtheta);
    }

    cout << "Total Evaluation Time = " << totaltime << " s" << endl;

    delete[] zSumCipher;
    delete[] ctemp;
    delete[] dtheta;

}

Cipher* LogReg::getgrad_deg7(Cipher*& thetaCipher, Cipher*& zTrainCipher){

    //! compute ztheta =  (p* z[k]/8) (p* theta)/p  : mod(theta)+ logp
    double tlvl =  getctlvl(thetaCipher[0]);
    double zlvl =  getctlvl(zTrainCipher[0]);

    Cipher* ctemp= new Cipher[LRparams.dim1];

    NTL_EXEC_RANGE(LRparams.dim1, first, last);
    for (long i = first; i < last; i++){
        ctemp[i] = scheme.modEmbed(zTrainCipher[i], zlvl - tlvl);
        scheme.multAndEqual(ctemp[i], thetaCipher[i]);
    }
    NTL_EXEC_RANGE_END;

    Cipher ztheta = ctemp[0];
    for (long i = 1; i < LRparams.dim1; i++){
        scheme.addAndEqual(ztheta, ctemp[i]);
    }

    scheme.modSwitchAndEqual(ztheta, LRparams.logp);  // ztheta.lvl : tlvl - logp

    //-----------------------------------------------------------------------------------------
    // ctemp  =  alpha * (ztheta) * (a7*ztheta^6 + a5*ztheta^4 +a3*ztheta^2 + a1) * z[i] for 0<=i<=dim
    //        =  ((alpha*a7*z[i]) * (ztheta)) * (ztheta^6 + a5/a7*ztheta^4 +a3/a7*ztheta^2 + a1/a7)
    //-----------------------------------------------------------------------------------------

    // zSquare = p * (theat*z[k]/8)^2  with  tlvl - 2*logp
    Cipher zSquare= scheme.square(ztheta);
    scheme.modSwitchAndEqual(zSquare, LRparams.logp);

    // zQuartic = p * (theat*z[k]/8)^4 with  tlvl - 3*logp
    Cipher zQuartic = scheme.square(zSquare);
    scheme.modSwitchAndEqual(zQuartic, LRparams.logp);

    // zQuartic = p * (a7*ztheta^4 + a5*ztheta^2 + a3)  with  tlvl - 3*logp
    Cipher ctemp1= scheme.multByConst(zSquare, LRparams.evalcoeff[5]);
    scheme.modSwitchAndEqual(ctemp1, LRparams.logp);     // lvl(theta)+3
    scheme.addConstAndEqual(ctemp1, LRparams.evalcoeff[3]);

    scheme.multByConst(zQuartic, LRparams.evalcoeff[7]);
    scheme.addAndEqual(zQuartic, ctemp1);

    Cipher* res= new Cipher[LRparams.dim1];

    NTL_EXEC_RANGE(LRparams.dim1, first, last);
    for (long i = first; i < last; ++i){
        res[i] = scheme.modEmbed(zTrainCipher[i], zlvl - tlvl + LRparams.logp);
        scheme.multAndEqual(res[i], ztheta);
        scheme.modSwitchAndEqual(res[i], LRparams.logp);       // res: tlvl - logp

        ctemp[i]= scheme.multByConst(res[i], LRparams.evalcoeff[1]);
        scheme.modSwitchAndEqual(ctemp[i], LRparams.logp);     // res: tlvl - 2*logp

        scheme.multAndEqual(res[i], zSquare);
        scheme.modSwitchAndEqual(res[i], LRparams.logp);      // res: tlvl - 3 *logp

        scheme.multAndEqual(res[i], zQuartic);
        scheme.modSwitchAndEqual(res[i], LRparams.logp);
        scheme.multByConst(res[i], LRparams.evalcoeff[7]);    // res: tlvl - 4 *logp

        scheme.addAndEqual(res[i], ctemp[i]);
    }
    NTL_EXEC_RANGE_END;

    //! rot and sum

    NTL_EXEC_RANGE(LRparams.dim1, first, last);
    for (long i = first; i < last; ++i){
        for(long j= 0; j< LRparams.logn; ++j){
            long l = (1<<j);
            ctemp[i] = scheme.leftRotate(res[i], l);
            scheme.addAndEqual(res[i], ctemp[i]);
        }
        scheme.modSwitchAndEqual(res[i], LRparams.logn - LRparams.log2polyscale);
    }
    NTL_EXEC_RANGE_END;

    delete[] ctemp;
    return res;
}

//-----------------------------------------------------------------------------------------
// res[i]  =  1/n * sum_k (ztheta) * (a3*ztheta^3 + a1*ztheta) * z[i]/8 for 0<=i<=dim
//         =  1/n * sum_k ((z[i]/8) * (ztheta)) * (ztheta^2 + a1/a3) * a3)
//-----------------------------------------------------------------------------------------

Cipher* LogReg::getgrad_deg3(Cipher*& thetaCipher, Cipher*& zTrainCipher){

    double tlvl =  getctlvl(thetaCipher[0]);
    double zlvl =  getctlvl(zTrainCipher[0]);

    Cipher* ctemp= new Cipher[LRparams.dim1];

    NTL_EXEC_RANGE(LRparams.dim1, first, last);
    for (long i = first; i < last; i++){
        ctemp[i] = scheme.modEmbed(zTrainCipher[i], zlvl - tlvl);
        scheme.multAndEqual(ctemp[i], thetaCipher[i]);
    }
    NTL_EXEC_RANGE_END;

    Cipher ztheta = ctemp[0];
    for (long i = 1; i < LRparams.dim1; i++){
        scheme.addAndEqual(ztheta, ctemp[i]);
    }

    scheme.modSwitchAndEqual(ztheta, LRparams.logp);  // ztheta.lvl : tlvl - logp

    //---------------------------------------------------
    //! zSquare= p * (theat*z[k]/8)^2 + p (a1/a3) with  tlvl - 2*logp
    Cipher zSquare= scheme.square(ztheta);
    scheme.modSwitchAndEqual(zSquare, LRparams.logp);
    scheme.addConstAndEqual(zSquare, LRparams.evalcoeff[1]);

    //! res[i]= ((z[i]/8) * a3 ) * (ztheta)) * (ztheta^2 + a1/a3))
    Cipher* res= new Cipher[LRparams.dim1];

    NTL_EXEC_RANGE(LRparams.dim1, first, last);
    for (long i = first; i < last; ++i){
        res[i]= scheme.multByConst(zTrainCipher[i], LRparams.evalcoeff[3]);
        scheme.modSwitchAndEqual(res[i], LRparams.logp);  // ((z[i]/8) * a3 ) with zlvl - logp

        scheme.modEmbedAndEqual(res[i], zlvl - tlvl);
        scheme.multAndEqual(res[i], ztheta);
        scheme.modSwitchAndEqual(res[i], LRparams.logp);  // ((z[i]/8) * a3 ) * (ztheta)) with zlvl - 2*logp

        scheme.multAndEqual(res[i], zSquare);
        scheme.modSwitchAndEqual(res[i], LRparams.logp);  // with zlvl - 3*logp
    }
    NTL_EXEC_RANGE_END;

    //! (sum res[i]) / (n/polyscale)

    NTL_EXEC_RANGE(LRparams.dim1, first, last);
    for (long i = first; i < last; ++i){
        for(long j= 0; j< LRparams.logn; ++j){
            long l = (1<<j);
            ctemp[i] = scheme.leftRotate(res[i], l);
            scheme.addAndEqual(res[i], ctemp[i]);
        }

        scheme.modSwitchAndEqual(res[i], LRparams.logn - LRparams.log2polyscale);
    }
    NTL_EXEC_RANGE_END;

    delete[] ctemp;
    return res;
}


#define BOOST_TEST_MODULE "test_gocontact_potential"

#ifdef BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#else
#include <boost/test/included/unit_test.hpp>
#endif

#include <test/util/check_potential.hpp>
#include <mjolnir/forcefield/local/GoContactPotential.hpp>

BOOST_AUTO_TEST_CASE(GoContact_double)
{
    using real_type = double;
    constexpr std::size_t N = 1000;
    constexpr real_type   h = 1e-6;
    constexpr real_type tol = 1e-6;

    const real_type e  = 1.0;
    const real_type r0 = 5.0;

    mjolnir::GoContactPotential<real_type> potential(e, r0);

    const real_type x_min = 0.8 * r0;
    const real_type x_max = 5.0 * r0;

    mjolnir::test::check_potential(potential, x_min, x_max, tol, h, N);
}

BOOST_AUTO_TEST_CASE(GoContact_float)
{
    using real_type = double;
    constexpr std::size_t N = 100;
    constexpr real_type   h = 1e-3;
    constexpr real_type tol = 1e-3;

    const real_type e  = 1.0;
    const real_type r0 = 5.0;

    mjolnir::GoContactPotential<real_type> potential(e, r0);

    const real_type x_min = 0.8 * r0;
    const real_type x_max = 5.0 * r0;

    mjolnir::test::check_potential(potential, x_min, x_max, tol, h, N);
}


/**

\file
\author Datta Ramadasan
//==============================================================================
//         Copyright 2015 INSTITUT PASCAL UMR 6602 CNRS/Univ. Clermont II
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================

*/

#ifndef __MODULES_NUMERIC_AD_CD_ADCT_HPP__
#define __MODULES_NUMERIC_AD_CD_ADCT_HPP__

#include <Eigen/Core>
#include <boost/type_traits/is_floating_point.hpp>
#include <boost/utility/enable_if.hpp>

#ifdef USE_TOON
#include <TooN/TooN.h>
#include <libv/lma/lm/container/tag.hpp>
#endif
#include <libv/lma/ttt/traits/naming.hpp>

namespace adct
{
  template<class T> struct Expr
  {
    T const& cast() const {return static_cast<T const &>(*this); }
  };

  template<class T, int N, class Default=void> class Ad : public Expr<Ad<T,N,Default>>
  {
    public:
      typedef Eigen::Matrix<T, N, 1/*, Eigen::DontAlign*/> Array;
      Ad(const T& val = T()):value_(val),infinite_(Array::Zero()) {}
      Ad(const T& val , int i):value_(val),infinite_(Array::Zero()) { infinite_[i] = 1; }
      template<class X> Ad(const Expr<X>& expr):value_(expr.cast().value()),infinite_(expr.cast().infinite()) {}

      const T&     value()    const { return value_; }
      const Array& infinite() const { return infinite_; }

    private:
      T     value_;
      Array infinite_;
  };

#ifdef USE_TOON
  template<class T, int N> class Ad<T,N,lma::Toon> : public Expr<Ad<T,N,lma::Toon>>
  {
    public:
      typedef TooN::Vector<N,T> Array;
      Ad(const T& val = T()):value_(val),infinite_(TooN::Zeros) {}
      Ad(const T& val , int i):value_(val),infinite_(TooN::Zeros) { infinite_[i] = 1; }
      template<class X> Ad(const Expr<X>& expr):value_(expr.cast().value()),infinite_(expr.cast().infinite()) {}

      const T&     value()    const { return value_; }
      const Array& infinite() const { return infinite_; }

    private:
      T     value_;
      Array infinite_;
  };
#endif

  template<class T, class = void> struct Traits { typedef const T& type; };

  template<class A, class Op> struct Unary : Expr<Unary<A,Op>>
  {
    typename Traits<A>::type a;
    Unary(const A& a_):a(a_){}
    auto value()    const { return Op::value(a); }
    auto infinite() const { return Op::infinite(a); }
  };

  template<class A, class B, class Op> struct Binary : Expr<Binary<A,B,Op>>
  {
    typename Traits<A>::type a;
    typename Traits<B>::type b;
    Binary(const A& a_, const B& b_):a(a_),b(b_){}
    auto value()    const { return Op::value(a,b); }
    auto infinite() const { return Op::infinite(a,b); }
  };

  template<class T> struct Traits<T, typename boost::enable_if< boost::is_floating_point<T> >::type > { typedef T type; };
  template<class A, class B, class Op>  struct Traits<Binary<A,B,Op>> { typedef Binary<A,B,Op> type; };
  template<class A, class Op>           struct Traits<Unary<A,Op>>    { typedef Unary<A,Op> type; };

  struct Minus
  {
    template<class A> static auto value   (const A& a) { return - a.value();}
    template<class A> static auto infinite(const A& a) { return - a.infinite();}
  };

  struct Sqrt
  {
    template<class A> static auto value   (const A& a) { return sqrt(a.value()); }
    template<class A> static auto infinite(const A& a) { return a.infinite() / (2.0 * sqrt(a.value())); }
  };

  struct Cos
  {
    template<class A> static auto value   (const A& a) { return cos(a.value()); }
    template<class A> static auto infinite(const A& a) { return - sin(a.value()) * a.infinite(); }
  };

  struct Sin
  {
    template<class A> static auto value   (const A& a) { return sin(a.value()); }
    template<class A> static auto infinite(const A& a) { return cos(a.value()) * a.infinite(); }
  };

  struct Addition
  {
    template<class A, class B> static auto value   (const A& a, const B& b) { return a.value() + b.value() ;}
    template<class A, class B> static auto infinite(const A& a, const B& b) { return a.infinite() + b.infinite() ;}
  };

  struct Substract
  {
    template<class A, class B> static auto value   (const A& a, const B& b) { return a.value() - b.value() ;}
    template<class A, class B> static auto infinite(const A& a, const B& b) { return a.infinite() - b.infinite() ;}
  };

  struct Multiply
  {
    template<class A, class B> static auto value   (const A& a, const B& b) { return a.value() * b.value() ;}
    template<class A, class B> static auto infinite(const A& a, const B& b) { return a.value() * b.infinite() + a.infinite() * b.value(); }
  };

  struct AdditionScalar
  {
    template<class A> static auto value   (const A& a, double b) { return a.value() + b ;}
    template<class A> static auto infinite(const A& a, double  ) { return a.infinite() ;}
  };

  struct SubstractScalar
  {
    template<class A> static auto value   (const A& a, double b) { return a.value() - b ;}
    template<class A> static auto infinite(const A& a, double  ) { return a.infinite() ;}
  };

  struct ScalarSubstract
  {
    template<class A> static auto value   (double b, const A& a) { return b - a.value() ;}
    template<class A> static auto infinite(double  , const A& a) { return -a.infinite() ;}
  };

  struct MultiplyScalar
  {
    template<class A> static auto value   (const A& a, double b) { return a.value() * b ;}
    template<class A> static auto infinite(const A& a, double b) { return a.infinite() * b;}
  };

  struct DivideScalar
  {
    template<class A> static auto value   (const A& a, double b) { return a.value() / b ;}
    template<class A> static auto infinite(const A& a, double b) { return a.infinite() / b;}
  };

  struct ScalarDivide
  {
    static auto sqr(double t) { return t * t; }
    template<class B> static auto value   (double a, const B& b) { return a / b.value() ;}
    template<class B> static auto infinite(double a, const B& b) { return - b.infinite() * a / sqr(b.value()); }
  };

  struct Divide
  {
    template<class A, class B> static auto value(const A& a, const B& b) { return a.value() / b.value() ; }
    template<class A, class B> static auto infinite(const A& a, const B& b)
    {
      const double val_inv = 1.0 / b.value();
      return (a.infinite() - a.value() * val_inv * b.infinite()) * val_inv;
    }
  };

  template<class A, class B> auto operator+(const Expr<A>& a, const Expr<B>& b) { return Binary<A,B,Addition>(a.cast(),b.cast()); }
  template<class A, class B> auto operator-(const Expr<A>& a, const Expr<B>& b) { return Binary<A,B,Substract>(a.cast(),b.cast()); }
  template<class A, class B> auto operator*(const Expr<A>& a, const Expr<B>& b) { return Binary<A,B,Multiply>(a.cast(),b.cast()); }
  template<class A, class B> auto operator/(const Expr<A>& a, const Expr<B>& b) { return Binary<A,B,Divide>(a.cast(),b.cast()); }

  template<class A>          auto operator+(const Expr<A>& a, double b)         { return Binary<A,double,AdditionScalar>(a.cast(),b); }
  template<class A>          auto operator+(double b, const Expr<A>& a)         { return a+b; }
  template<class A>          auto operator-(const Expr<A>& a, double b)         { return Binary<A,double,SubstractScalar>(a.cast(),b); }
  template<class A>          auto operator-(double b, const Expr<A>& a)         { return Binary<double,A,ScalarSubstract>(b,a.cast()); }

  template<class A>          auto operator*(const Expr<A>& a, double b)         { return Binary<A,double,MultiplyScalar>(a.cast(),b); }
  template<class A>          auto operator*(double b, const Expr<A>& a)         { return a*b; }
  template<class A>          auto operator/(const Expr<A>& a, double b)         { return Binary<A,double,DivideScalar>(a.cast(),b); }
  template<class B>          auto operator/(double a, const Expr<B>& b)         { return Binary<double,B,ScalarDivide>(a,b.cast()); }

  template<class A>          auto operator-(const Expr<A>& a)                   { return Unary<A,Minus>(a.cast()); }
  template<class A>          auto sqrt     (const Expr<A>& a)                   { return Unary<A,Sqrt>(a.cast()); }
  template<class A>          auto cos      (const Expr<A>& a)                   { return Unary<A,Cos>(a.cast()); }
  template<class A>          auto sin      (const Expr<A>& a)                   { return Unary<A,Sin>(a.cast()); }

  template<class A, class B> bool operator>(const Expr<A>& a, const Expr<B>& b) { return a.cast().value() > b.cast().value() ; }
  template<class A, class B> bool operator<(const Expr<A>& a, const Expr<B>& b) { return a.cast().value() < b.cast().value() ; }
}

#include <libv/lma/ttt/traits/naming.hpp>
#include <libv/lma/string/string_utiliy.hpp>

namespace ttt
{
  template<class T, int N> struct Name<adct::Ad<T,N>>
  {
    static std::string name(){ return std::string("Ad<") + ttt::name<T>() + "," + lma::to_string(N) + ">"; }
  };

}

#endif