/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*                                                                           */
/*  This file is part of the library KASKADE 7                               */
/*    see http://www.zib.de/projects/kaskade7-finite-element-toolbox         */
/*                                                                           */
/*  Copyright (C) 2002-2011 Zuse Institute Berlin                            */
/*                                                                           */
/*  KASKADE 7 is distributed under the terms of the ZIB Academic License.    */
/*    see $KASKADE/academic.txt                                              */
/*                                                                           */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef CHECK_DERIVATIVE_HH
#define CHECK_DERIVATIVE_HH

#include <algorithm>
#include <cassert>
#include <cmath>
#include <fstream>
#include <string>
#include <utility>
#include <vector>

#include <boost/mpl/size.hpp>
#include <boost/lexical_cast.hpp>

#include "algorithm/dune_bridge.hh"
#include "utilities/linalg/scalarproducts.hh"

namespace Kaskade{
/// Some helper's for a correct indentation of XML-code
namespace XMLHelper{

  static int const xml_increment=2;
  static int xml_level;

  std::string getXMLIndent()
  {
    std::string result;
    for(int i=0; i<xml_level; ++i) result.append(" ");
    return result;
  }

  struct XMLBlock{
    XMLBlock(std::ofstream &f, const char* s, const char* d="") : file(f), str(s), data(d), offset("")
    {
      xml_level += xml_increment;
      getOffset();
      writeBegin();
    }
    XMLBlock(std::ofstream &f, std::string &s, std::string d="") : file(f), str(s), data(d), offset("")
    {
      xml_level += xml_increment;
      getOffset();
      writeBegin();
    }
    ~XMLBlock()
    {
      file << offset.c_str() << "</" << str.c_str() << ">" << std::endl;
      xml_level -= xml_increment;
    }

  private:
    void getOffset(){
      for(int i=0; i<xml_level; ++i) offset.append(" ");
    }

    void writeBegin(){
      file << offset.c_str() << "<" << str.c_str() << data.c_str() << ">" << std::endl;
    }

    std::ofstream& file;
    std::string const str, data;
    std::string offset;
  };

}

namespace DerivativeCheck
{
  namespace Private
  {
    template <class SparseInt>
    void writeConnectivity(std::ofstream &file, SparseInt row, SparseInt col, SparseInt numRows)
    {
      int const upperLeftVertexId = row+col*numRows;
      file << upperLeftVertexId << " " << upperLeftVertexId+1 << " " << upperLeftVertexId+numRows << " " << upperLeftVertexId+numRows+1 << " ";
    }
  }

  template <class Assembler, class Functional,int firstBlock=0, int lastBlock = Functional::AnsatzVars::noOfVariables>
  typename Functional::Scalar d1(Assembler& assembler, Functional const& f, typename Functional::AnsatzVars::VariableSet& x, typename Functional::Scalar tolerance = 1e-6, typename Functional::Scalar increment = 1e-12, bool toFile=false, std::string const& filename = std::string("d1error"))
  {
    typedef typename Functional::Scalar Scalar;
    typedef typename Functional::AnsatzVars::template CoefficientVectorRepresentation<firstBlock,lastBlock>::type CoefficientVector;

    std::cout << "checking first derivative" << std::endl;
    std::cout << "assembling...";

    // storage for the evaluation of the derivative at x and the at x+increment*e_i
    Scalar distorted;

    // get reference solution
    assembler.assemble(linearization(f,x), Assembler::RHS | Assembler::VALUE );

    CoefficientVector analytic(assembler.template rhs<firstBlock,lastBlock>());
    std::vector<Scalar> analyticD1(assembler.template nrows<firstBlock,lastBlock>());
    analytic.write(analyticD1.begin());

    // get undistorted rhs
    Scalar const reference_value = assembler.functional();

    std::vector<Scalar> xAsVector(assembler.nrows());
    x.write(xAsVector.begin());

    // reserve storage for finite difference solution
    std::vector<Scalar> firstDerivativeError(analyticD1.size(),0.);

    size_t offset = (0!=firstBlock) ? assembler.template nrows<0,firstBlock>() : 0;
    size_t end = (lastBlock!=Functional::AnsatzVars::noOfVariables) ? (offset + assembler.template nrows<firstBlock,lastBlock>()) : xAsVector.size();

    // get finite difference gradient
    for(size_t i=offset; i<end; ++i)
    {

      xAsVector[i] += increment;
      x.read(xAsVector.begin());

      assembler.assemble(linearization(f,x),Assembler::VALUE);
      distorted = assembler.functional();
      xAsVector[i] -= increment;

      // store finite difference
      firstDerivativeError[i-offset] = (distorted - reference_value)/increment;
      if(i%100==0) std::cout << ".";
    }
    // set x back to initial state
    x.read(xAsVector.begin());
    std::cout << "done." << std::endl;
    for(size_t i=0; i<firstDerivativeError.size(); ++i)
        firstDerivativeError[i] -= analyticD1[i];

    Scalar infinityError = LinAlg::InfinityNorm()(firstDerivativeError);
    bool errorBelowTolerance = infinityError < tolerance;
    if(errorBelowTolerance)
      std::cout << "\n first derivative seems to be trustworthy!\nInfinityError: " << infinityError << std::endl << std::endl;
    else
      std::cout << "\n first derivative does not seem to be trustworthy!\nInfinity error: " << infinityError << std::endl << std::endl;

    if(toFile) vectorToVTK(firstDerivativeError);

    return infinityError;
  }

  template <class Assembler, class Functional, int firstRow=0, int lastRow=Functional::TestVars::noOfVariables, int firstCol=0, int lastCol=Functional::AnsatzVars::noOfVariables>
  typename Functional::Scalar d2(Assembler& assembler, Functional const& f, typename Functional::AnsatzVars::VariableSet const& x, typename Functional::Scalar increment = 1e-12, typename Functional::Scalar tolerance = 1e-6, bool toFile = false, std::string const& savefilename = std::string("d2error"))
  {
    typedef typename Functional::Scalar Scalar;
    typedef typename Functional::TestVars::template CoefficientVectorRepresentation<firstRow,lastRow>::type  RowVector;
    typedef typename Functional::AnsatzVars::template CoefficientVectorRepresentation<firstCol,lastCol>::type ColumnVector;

    std::cout << "checking second derivative:" << std::endl;
    std::cout << "assembling...";

    // sparse storage for the analytic solution
    typedef MatrixAsTriplet<Scalar> SparseMatrix;
    SparseMatrix secondDerivativeError;

    // get reference solution
    assembler.assemble(linearization(f,x), Assembler::RHS, Assembler::MATRIX);
    SparseMatrix analytic_solution = assembler.template get<SparseMatrix,firstRow,lastRow,firstCol,lastCol>(false);
    RowVector reference_value(assembler. template rhs<firstRow,lastRow>());
    RowVector copyOfRefVal(reference_value);

    // get evaluation point
    std::vector<Scalar> xAsVector(assembler.nrows());
    x.write(xAsVector.begin());

    size_t offset = (firstRow != 0) ? assembler.template nrows<0,firstRow>() : 0;
    size_t end = (lastRow!=Functional::TestVars::noOfVariables) ? (offset + assembler.template nrows<firstRow,lastRow>()) : xAsVector.size();

    // get finite difference gradient matrix
    for(size_t i=offset; i<end; ++i)
    {
      copyOfRefVal = reference_value;
      xAsVector[i] += increment;
      x.read(xAsVector.begin());

      assembler.assemble(linearization(f,x), Assembler::RHS);
      RowVector distorted_value(assembler.template rhs<firstRow,lastRow>());

      xAsVector[i] -= increment;

      std::vector<Scalar> column(end-offset);
      copyOfRefVal -= distorted_value;
      copyOfRefVal.write(column.begin());

      secondDerivativeError.addColumn(column,i);
      // store finite difference
      //      for(size_t k=0; k<numRows; ++k)
      //      {
      //        secondDerivativeError.addEntry(k, i, (distorted_value[k] - reference_value[k])/increment);
      //      }
      if(i%100==0) std::cout << ".";
    }
    // set x back to initial state
    x.read(xAsVector.begin());
    std::cout << "done." << std::endl;
    analytic_solution *= -1;
    secondDerivativeError += analytic_solution;

    Scalar infinityError = LinAlg::InfinityNorm()(secondDerivativeError);
    if( (tolerance > infinityError) )
      std::cout << "\nSecond derivative seems to be trustworthy!\nInfinity error: " << infinityError << std::endl << std::endl;
    else
      std::cout << "\nSecond derivative does not seem to be trustworthy!\nInfinity error: " << infinityError << std::endl << std::endl;

    return infinityError;
  }

  template <class Vector>
  void vectorToVTK(Vector const& vec, std::string savefilename = std::string("d1error"))
  {
    using std::endl;
    using std::string;

    size_t const numberOfRows = vec.size(),
                 numberOfPoints = (1+numberOfRows)*2;
    size_t const cell_type = 8,
                 offset = 4;

    XMLHelper::xml_level = 0;
    typedef typename XMLHelper::XMLBlock XMLBlock;

    savefilename.append(".vtu");
    std::ofstream file(savefilename.c_str());
    file << "<?xml version=\"1.0\"?>" << endl;
    { XMLBlock xvtk(file, "VTKFile", " type=\"UnstructuredGrid\" version=\"0.1\" byte_order=\"LittleEndian\"");
      { XMLBlock xgrid(file, "UnstructuredGrid");
        { string str = string(" NumberOfPoints=\"") + boost::lexical_cast<string>(numberOfPoints) + " \" numberOfRows=\"" +
          boost::lexical_cast<string>(numberOfRows) + "\"";
          XMLBlock xpiece(file, "Piece", str.c_str());
          { XMLBlock xcd(file, "CellData");
            { XMLBlock xda(file, "DataArray", " type=\"Float32\" Name=\"d2error\" NumberOfComponents=\"1\" format=\"ascii\"");
              for(size_t cell=0; cell<numberOfRows; ++cell){
                if(cell%12==0) file << "    ";
                // invert row order for visualization tools
                file << fabs(vec[numberOfRows-1-cell]) << " ";
                if(cell%12==11 || cell==(numberOfRows-1) ) file << endl;
              }
            }
          }
          { XMLBlock xpoints(file, "Points");
            { XMLBlock xda(file, "DataArray", " type=\"Float32\" Name=\"Coordinates\" NumberOfComponents=\"3\" format=\"ascii\"");
              for(size_t cell=0; cell<numberOfRows+1; ++cell){
                if(cell%2==0) file << "    ";
                file << cell << " 0 0 " << cell << " 1 0 ";
                if(cell%2==1 || cell==numberOfRows) file << endl;
              }
            }
          }
          { XMLBlock xcells(file, "Cells");
            { XMLBlock xda(file, "DataArray", " type=\"Int32\" Name=\"connectivity\" NumberOfComponents=\"1\" format=\"ascii\"");
              for(size_t cell=0; cell<numberOfRows; ++cell){
                if(cell%3==0) file << "    ";
                file << 2*cell << " " << 2*cell+1 << " " << 2*cell+2 << " " << 2*cell+3 << " " << endl;
                if(cell%3==11 || cell==(numberOfRows-1)) file << endl;
              }
            }
            { XMLBlock xda(file, "DataArray", " type=\"Int32\" Name=\"offsets\" NumberOfComponents=\"1\" format=\"ascii\"");
              for(size_t cell=0; cell<numberOfRows; ++cell){
                if(cell%12==0) file << "    ";
                file << cell*offset << " ";
                if(cell%12==1 || cell==(numberOfRows-1)) file << endl;
              }
            }
            { XMLBlock xda(file, "DataArray", " type=\"UInt8\" Name=\"types\" NumberOfComponents=\"1\" format=\"ascii\"");
              for(size_t cell=0; cell<numberOfRows; ++cell){
                if(cell%12==0) file << "    ";
                file << cell_type << " ";
                if(cell%12==11 || cell==(numberOfRows-1) ) file << endl;
              }
            }
          }
        }
      }
    }
  }

  template <class Scalar, class SparseInt>
  void matrixToVTK(MatrixAsTriplet<Scalar,SparseInt> const& matrix, std::string savefilename = std::string("d2error"))
    {
      using std::cout;
      using std::endl;
      using std::string;

      cout << "writing file";

      /// store index pairs
      std::vector<std::pair<SparseInt,SparseInt> > indices(matrix.data.size());
      for(size_t i=0; i<indices.size(); ++i)
        indices[i] = std::make_pair(matrix.ridx[i], matrix.cidx[i]);

      SparseInt const numberOfRows = matrix.nrows()+1,
          numberOfColumns = matrix.ncols()+1,
          numberOfPoints = numberOfRows*numberOfColumns,
          numberOfCells = (numberOfRows-1)*(numberOfColumns-1);
      size_t const cell_type = 8, // = vtk_pixel
          offset = 4;

      XMLHelper::xml_level = 0;
      string indent;
      typedef typename XMLHelper::XMLBlock XMLBlock;

      savefilename.append(".vtu");
      std::ofstream file(savefilename.c_str());
      file << "<?xml version=\"1.0\"?>" << endl;
      { XMLBlock xvtk(file, "VTKFile", " type=\"UnstructuredGrid\" version=\"0.1\" byte_order=\"LittleEndian\"");
        { XMLBlock xgrid(file, "UnstructuredGrid");
          { string str = string(" NumberOfPoints=\"") + boost::lexical_cast<string>(numberOfPoints) + " \" NumberOfCells=\"" +
            boost::lexical_cast<string>(numberOfCells) + "\"";
            XMLBlock xpiece(file, "Piece", str.c_str());
            { XMLBlock xcd(file, "CellData");
              { XMLBlock xda(file, "DataArray", " type=\"Float32\" Name=\"d2error\" NumberOfComponents=\"1\" format=\"ascii\"");
                int cell = 0;
                indent = XMLHelper::getXMLIndent();
                for(SparseInt col=0; col<numberOfColumns-1; ++col){
                  for(SparseInt row=0; row<numberOfRows-1; ++row){
                    if(cell%12==0) file << indent.c_str();
                    typename std::vector<std::pair<SparseInt,SparseInt> >::const_iterator iter = std::find(indices.begin(), indices.end(), std::make_pair(numberOfRows-2-row,col)),
                        iterStart = indices.begin();
                    if(iter!=indices.end())
                      file << fabs(matrix.data[std::distance(iterStart, iter)]) << " ";
                    else
                      file << 0.0 << " ";
                    if(cell%12==11 || cell==numberOfCells-1) file << endl;
                    ++cell;
                  }
                }
              }
            }
            cout << ".";
            { XMLBlock xp(file, "Points");
              { XMLBlock xda(file, "DataArray", " type=\"Float32\" Name=\"Coordinates\" NumberOfComponents=\"3\" format=\"ascii\"");
                int cell = 0;
                for(SparseInt col=0; col<numberOfColumns; ++col){
                  for(SparseInt row=0; row<numberOfRows; ++row){
                    if(cell%4==0) file << indent.c_str();
                    file << row << " " << col << " 0 ";
                    if(cell%4==3 || cell==(numberOfCells-1)) file << endl;
                    ++cell;
                  }
                }
              }
            }
            cout << ".";
            { XMLBlock xcell(file, "Cells");
              { XMLBlock xda(file, "DataArray", " type=\"Int32\" Name=\"connectivity\" NumberOfComponents=\"1\" format=\"ascii\"");
                int cell = 0;
                for(SparseInt col=0; col<numberOfColumns-1; ++col){
                  for(SparseInt row=0; row<numberOfRows-1; ++row){
                    if(cell%3==0) file << indent.c_str();
                    Private::writeConnectivity(file, row, col, numberOfRows);
                    if(cell%3==2 || cell==(numberOfCells-1)) file << endl;
                    ++cell;
                  }
                }
              }
              cout << ".";
              { XMLBlock xda(file, "DataArray", " type=\"Int32\" Name=\"offsets\" NumberOfComponents=\"1\" format=\"ascii\"");
                for(SparseInt cell=0; cell<numberOfCells; ++cell){
                  if(cell%12==0) file << indent.c_str();
                  file << (cell+1)*offset << " ";
                  if(cell%12==11 || cell==(numberOfCells-1)) file << endl;
                }
              }
              cout << ".";
              { XMLBlock xda(file, "DataArray", " type=\"UInt8\" Name=\"types\" NumberOfComponents=\"1\" format=\"ascii\"");
                for(SparseInt cell=0; cell<numberOfCells; ++cell){
                  if(cell%12==0) file << indent.c_str();
                  file << cell_type << " ";
                  if(cell%12==11 || cell==(numberOfCells-1) ) file << endl;
                }
              }
            }
          }
        }
      }
      cout << "done" << endl;
    }

} // end of namespace DerivativeCheck

/// Class that checks the derivatives of a functional at a linearization point.
/**
 * The analytic derivatives are compared with a finite difference approximation. Note that this
 * just gives you a hint (but a good one if you don't use stupid parameters for the tolerance and
 * the increment) about the correctness of your analytically calculated derivatives.
 * Keep in mind that it is a heuristic.
 *
 * \param checkD1 enable/disable check of first derivative (i.e. disable it if d0() has not been implemented
 *                correctly in boundary or domain cache (optional, default=true(enabled))
 * \param SparseInt integer type used for sparse matrix indices
 */
template <class Functional, bool checkD1=true, class SparseInt=int>
class DerivativeChecker
{
  typedef VariationalFunctionalAssembler<LinearizationAt<Functional> > Assembler;
  typedef typename Functional::AnsatzVars VariableSet;
  typedef typename VariableSet::VariableSet EvaluationPoint;
  typedef typename VariableSet::template CoefficientVectorRepresentation<> CoefficientVectorRepresentation;
  typedef typename CoefficientVectorRepresentation::type CoefficientVector;

public:
  typedef typename Functional::Scalar Scalar;
  enum{ idOfFirstBlock = 0,
    idOfLastBlock  = boost::mpl::size<typename Functional::AnsatzVars::Variables>::type::value };

  /// Constructor
  /**
   * \param functional instance of the Functional
   * \param x_ linearization point
   * \param t error tolerance (point-wise, default=1.0e-6)
   * \param incr increment for finite differences (default=1.0e-9)
   */
  DerivativeChecker(Assembler& assembler_, Functional const& f_, EvaluationPoint const& x_, VariableSet const& variableSet_, Scalar const tol = 1.0e-6, Scalar const incr = 1.0e-9) :
    assembler(assembler_), f(f_), x(x_), variableSet(variableSet_), tolerance(tol), increment(incr)
  {
    if(checkD1) checkFirstDerivative();
    checkSecondDerivative();
  }

  /// Check first derivative.
  /**
   * This function is called by the constructor. You only need to call this function if you have
   * changed the tolerance.
   */
  void checkFirstDerivative()
  {
    std::cout << "checking first derivative" << std::endl;
    std::cout << "assembling...";

    // storage for the evaluation of the derivative at x and the at x+increment*e_i
    Scalar distorted;

    // get reference solution
    assembler.assemble(linearization(f,x), Assembler::RHS | Assembler::VALUE );

    CoefficientVector analytic(assembler.rhs());
    std::vector<Scalar> analyticD1(assembler.nrows());
    analytic.write(analyticD1.begin());

    // get undistorted rhs
    Scalar const reference_value = assembler.functional();

    std::vector<Scalar> xAsVector(analyticD1.size());
    x.write(xAsVector.begin());

    // reserve storage for finite difference solution
    firstDerivativeError.resize(xAsVector.size(),0);

    EvaluationPoint copyOfX(x);
    // get finite difference gradient
    for(size_t i=0; i<xAsVector.size(); ++i)
    {
      xAsVector[i] += increment;
      copyOfX.read(xAsVector.begin());

      assembler.assemble(linearization(f,copyOfX),Assembler::VALUE);
      Scalar distorted_value = assembler.functional();
      xAsVector[i] -= increment;

      // store finite difference
      firstDerivativeError[i] = (distorted_value - reference_value)/increment;
      if(i%100==0) std::cout << ".";
    }
    std::cout << "done." << std::endl;
    for(size_t i=0; i<firstDerivativeError.size(); ++i)
      firstDerivativeError[i] -= analyticD1[i];

    Scalar infinityError = LinAlg::InfinityNorm()(firstDerivativeError);
    firstDerivativeOk_ = infinityError < tolerance;
    if(firstDerivativeOk_)
      std::cout << "\n first derivative seems to be trustworthy!\nInfinityError: " << infinityError << std::endl << std::endl;
    else
      std::cout << "\n first derivative does not seem to be trustworthy!\nInfinity error: " << infinityError << std::endl << std::endl;
  }

  /// Check second derivative.
  /**
   * This function is called by the constructor. You only need to call this function if you have
   * changed the tolerance.
   */
  void checkSecondDerivative()
  {
    std::cout << "checking second derivative:" << std::endl;
    // Define linearization at x
    std::cout << "assembling...";

    // sparse storage for the analytic solution
    typedef MatrixAsTriplet<Scalar,SparseInt> SparseMatrix;

    // get reference solution
    assembler.assemble(linearization(f,x), Assembler::RHS, Assembler::MATRIX);
    SparseMatrix analytic_solution = assembler.template get<SparseMatrix>(false);
    CoefficientVector reference_value(assembler.rhs());
    CoefficientVector copyOfRefVal(reference_value);

    // get evaluation point
    std::vector<Scalar> xAsVector(assembler.nrows());
    x.write(xAsVector.begin());

    EvaluationPoint copyOfX(x);
    // get finite difference gradient matrix
    for(size_t i=0; i<xAsVector.size(); ++i)
    {
      copyOfRefVal = reference_value;
      xAsVector[i] += increment;
      copyOfX.read(xAsVector.begin());

      assembler.assemble(linearization(f,copyOfX), Assembler::RHS);
      CoefficientVector distorted_value(assembler.rhs());

      xAsVector[i] -= increment;

      std::vector<Scalar> column(xAsVector.size());
      copyOfRefVal -= distorted_value;
      copyOfRefVal.write(column.begin());

      secondDerivativeError.addColumn(column,i);
      // store finite difference
//      for(size_t k=0; k<numRows; ++k)
//      {
//        secondDerivativeError.addEntry(k, i, (distorted_value[k] - reference_value[k])/increment);
//      }
      if(i%100==0) std::cout << ".";
    }
    std::cout << "done." << std::endl;
    analytic_solution *= -1;
    secondDerivativeError += analytic_solution;

    Scalar infinityError = LinAlg::InfinityNorm()(secondDerivativeError);
    if( (secondDerivativeOk_ = tolerance > infinityError) )
      std::cout << "\nSecond derivative seems to be trustworthy!\nInfinity error: " << infinityError << std::endl << std::endl;
    else
      std::cout << "\nSecond derivative does not seem to be trustworthy!\nInfinity error: " << infinityError << std::endl << std::endl;
  }

  bool firstDerivativeOk() const { return firstDerivativeOk_; }

  bool secondDerivativeOk() const { return secondDerivativeOk_; }

//  /// get block of d1Error
//  std::vector<Scalar> getBlocksD1Error(int const firstBlockId, int const lastBlockId) const
//  {
//    assert(firstBlockId < lastBlockId);
//    std::pair<int,int> ids = getRowIds(firstBlockId, lastBlockId);
//    std::vector<Scalar> result;
//    for(int row = ids.first; row<ids.second; ++row)
//      result.push_back(firstDerivativeError[row]);
//    return result;
//  }
//
//  /// get block of d2Error
//  MatrixAsTriplet<Scalar> getBlocksD2Error(int const firstBlockId, int const lastBlockId) const
//  {
//    assert(firstBlockId < lastBlockId);
//    std::pair<int,int> ids = getRowIds(firstBlockId, lastBlockId);
//
//    MatrixAsTriplet<Scalar> blocks;
//    for(int i=0; i<secondDerivativeError.size(); ++i)
//    {
//      if(secondDerivativeError.ridx[i] >= ids.first && secondDerivativeError.ridx[i] < ids.second &&
//          secondDerivativeError.cidx[i] >= ids.first && secondDerivativeError.cidx[i] < ids.second)
//        blocks.addEntry(secondDerivativeError.ridx[i], secondDerivativeError.cidx[i], secondDerivativeError.data[i]);
//    }
//    return blocks;
//  }

  /// Only errors below tolerance will be displayed.
  void setTolerance(Scalar const tol){ tolerance = tol; }

  /// Set increment for finite differences
  void setIncrement(Scalar const incr){ increment = incr; }

  /// print error in first derivative (only values below tolerance)
  void printD1Error() const
  {
    std::cout << "components in D1 over tolerance:\n";
    for(size_t i=0; i<firstDerivativeError.size(); ++i)
      if(fabs(firstDerivativeError[i]) > tolerance)
        std::cout << i << ": " << firstDerivativeError[i] << std::endl;
  }

  /// print error in first derivative
  void printFullD1Error() const
  {
    std::cout << "components in D1:\n" << firstDerivativeError << std::endl;
  }

  /// print error in second derivative (only values below tolerance)
  void printD2Error() const
  {
    std::cout << "components in D2 over tolerance:\n";
    secondDerivativeError.print(std::cout,tolerance);
  }

  /// print error in second derivative
  void printFullD2Error() const
  {
    std::cout << "components in D2:\n";
    secondDerivativeError.print(std::cout);
  }

  /// write d2 to .vtu file
  void d2ToVTK(){
    assembler.assemble(linearization(f,x),Assembler::MATRIX);
    // sparse storage for the analytic solution
    MatrixAsTriplet<Scalar,SparseInt> analytic_solution = assembler.template get<MatrixAsTriplet<Scalar,SparseInt>>(false);
    // get reference solution
    matrixToVTK(analytic_solution);
  }

  /// write error in first derivative to .vtu file
  void d1ErrorToVTK(int const steps) const { vectorToVTK(firstDerivativeError, steps); }

//  /// write block of error in first derivative to .vtu file
//  void d1BlockErrorToVTK(int const firstBlockId, int const lastBlockId) const { vectorToVTK(getBlocksD1Error(firstBlockId,lastBlockId)); }

  /// write Vector vector to .vtu file
  /**
   * Vector must provide the function size() and  operator[](size_type n)
   */
  template <class Vector>
  void vectorToVTK(Vector &vector, int const steps=0) const {

    using std::endl;
    using std::string;

    size_t const numberOfRows = vector.size(),
        numberOfPoints = (1+numberOfRows)*2;
    size_t const cell_type = 8,
        offset = 4;

    XMLHelper::xml_level = 0;
    typedef typename XMLHelper::XMLBlock XMLBlock;

    string filename("d1error");
    filename.append(boost::lexical_cast<string>(steps)).append(".vtu");

    std::ofstream file(filename.c_str());
    file << "<?xml version=\"1.0\"?>" << endl;
    { XMLBlock xvtk(file, "VTKFile", " type=\"UnstructuredGrid\" version=\"0.1\" byte_order=\"LittleEndian\"");
      { XMLBlock xgrid(file, "UnstructuredGrid");
        { string str = string(" NumberOfPoints=\"") + boost::lexical_cast<string>(numberOfPoints) + " \" numberOfRows=\"" +
          boost::lexical_cast<string>(numberOfRows) + "\"";
          XMLBlock xpiece(file, "Piece", str.c_str());
          { XMLBlock xcd(file, "CellData");
            { XMLBlock xda(file, "DataArray", " type=\"Float32\" Name=\"d2error\" NumberOfComponents=\"1\" format=\"ascii\"");
              for(size_t cell=0; cell<numberOfRows; ++cell){
                if(cell%12==0) file << "    ";
                // invert row order for visualization tools
                file << fabs(vector[numberOfRows-1-cell]) << " ";
                if(cell%12==11 || cell==(numberOfRows-1) ) file << endl;
              }
            }
          }
          { XMLBlock xpoints(file, "Points");
            { XMLBlock xda(file, "DataArray", " type=\"Float32\" Name=\"Coordinates\" NumberOfComponents=\"3\" format=\"ascii\"");
              for(size_t cell=0; cell<numberOfRows+1; ++cell){
                if(cell%2==0) file << "    ";
                file << cell << " 0 0 " << cell << " 1 0 ";
                if(cell%2==1 || cell==numberOfRows) file << endl;
              }
            }
          }
          { XMLBlock xcells(file, "Cells");
            { XMLBlock xda(file, "DataArray", " type=\"Int32\" Name=\"connectivity\" NumberOfComponents=\"1\" format=\"ascii\"");
              for(size_t cell=0; cell<numberOfRows; ++cell){
                if(cell%3==0) file << "    ";
                file << 2*cell << " " << 2*cell+1 << " " << 2*cell+2 << " " << 2*cell+3 << " " << endl;
                if(cell%3==11 || cell==(numberOfRows-1)) file << endl;
              }
            }
            { XMLBlock xda(file, "DataArray", " type=\"Int32\" Name=\"offsets\" NumberOfComponents=\"1\" format=\"ascii\"");
              for(size_t cell=0; cell<numberOfRows; ++cell){
                if(cell%12==0) file << "    ";
                file << cell*offset << " ";
                if(cell%12==1 || cell==(numberOfRows-1)) file << endl;
              }
            }
            { XMLBlock xda(file, "DataArray", " type=\"UInt8\" Name=\"types\" NumberOfComponents=\"1\" format=\"ascii\"");
              for(size_t cell=0; cell<numberOfRows; ++cell){
                if(cell%12==0) file << "    ";
                file << cell_type << " ";
                if(cell%12==11 || cell==(numberOfRows-1) ) file << endl;
              }
            }
          }
        }
      }
    }
  }

  /// write error in second derivative to .vtu-file
  void d2ErrorToVTK(int const steps) const { matrixToVTK(secondDerivativeError,steps); }

//  /// write block of second derivative to .vtu-file
//  void d2BlockErrorToVTK(int firstBlockId, int lastBlockId) const { matrixToVTK(getBlocksD2Error(firstBlockId,lastBlockId)); }

  /// write, possibly sparse, matrix in triplet format to .vtu-file
  void matrixToVTK(MatrixAsTriplet<Scalar,SparseInt> const& matrix, int const steps=0) const
  {
    using std::cout;
    using std::endl;
    using std::string;

    cout << "writing file";

    /// store index pairs
    std::vector<std::pair<SparseInt,SparseInt> > indices(matrix.data.size());
    for(size_t i=0; i<indices.size(); ++i)
      indices[i] = std::make_pair(matrix.ridx[i], matrix.cidx[i]);

    SparseInt const numberOfRows = matrix.nrows()+1,
        numberOfColumns = matrix.ncols()+1,
        numberOfPoints = numberOfRows*numberOfColumns,
        numberOfCells = (numberOfRows-1)*(numberOfColumns-1);
    size_t const cell_type = 8, // = vtk_pixel
        offset = 4;

    XMLHelper::xml_level = 0;
    string indent;
    typedef typename XMLHelper::XMLBlock XMLBlock;

    string filename("d2error");
    filename.append(boost::lexical_cast<string>(steps)).append(".vtu");

    std::ofstream file(filename.c_str());
    file << "<?xml version=\"1.0\"?>" << endl;
    { XMLBlock xvtk(file, "VTKFile", " type=\"UnstructuredGrid\" version=\"0.1\" byte_order=\"LittleEndian\"");
      { XMLBlock xgrid(file, "UnstructuredGrid");
        { string str = string(" NumberOfPoints=\"") + boost::lexical_cast<string>(numberOfPoints) + " \" NumberOfCells=\"" +
          boost::lexical_cast<string>(numberOfCells) + "\"";
          XMLBlock xpiece(file, "Piece", str.c_str());
          { XMLBlock xcd(file, "CellData");
            { XMLBlock xda(file, "DataArray", " type=\"Float32\" Name=\"d2error\" NumberOfComponents=\"1\" format=\"ascii\"");
              int cell = 0;
              indent = XMLHelper::getXMLIndent();
              for(SparseInt col=0; col<numberOfColumns-1; ++col){
                for(SparseInt row=0; row<numberOfRows-1; ++row){
                  if(cell%12==0) file << indent.c_str();
                  typename std::vector<std::pair<SparseInt,SparseInt> >::const_iterator iter = std::find(indices.begin(), indices.end(), std::make_pair(numberOfRows-2-row,col)),
                      iterStart = indices.begin();
                  if(iter!=indices.end())
                    file << fabs(matrix.data[std::distance(iterStart, iter)]) << " ";
                  if(cell%12==11 || cell==numberOfCells-1) file << endl;
                  ++cell;
                }
              }
            }
          }
          cout << ".";
          { XMLBlock xp(file, "Points");
            { XMLBlock xda(file, "DataArray", " type=\"Float32\" Name=\"Coordinates\" NumberOfComponents=\"3\" format=\"ascii\"");
              int cell = 0;
              for(size_t col=0; col<numberOfColumns; ++col){
                for(size_t row=0; row<numberOfRows; ++row){
                  if(cell%4==0) file << indent.c_str();
                  file << row << " " << col << " 0 ";
                  if(cell%4==3 || cell==(numberOfCells-1)) file << endl;
                  ++cell;
                }
              }
            }
          }
          cout << ".";
          { XMLBlock xcell(file, "Cells");
            { XMLBlock xda(file, "DataArray", " type=\"Int32\" Name=\"connectivity\" NumberOfComponents=\"1\" format=\"ascii\"");
              int cell = 0;
              for(size_t col=0; col<numberOfColumns-1; ++col){
                for(size_t row=0; row<numberOfRows-1; ++row){
                  if(cell%3==0) file << indent.c_str();
                  writeConnectivity(file, row, col, numberOfRows);
                  if(cell%3==2 || cell==(numberOfCells-1)) file << endl;
                  ++cell;
                }
              }
            }
            cout << ".";
            { XMLBlock xda(file, "DataArray", " type=\"Int32\" Name=\"offsets\" NumberOfComponents=\"1\" format=\"ascii\"");
              for(size_t cell=0; cell<numberOfCells; ++cell){
                if(cell%12==0) file << indent.c_str();
                file << (cell+1)*offset << " ";
                if(cell%12==11 || cell==(numberOfCells-1)) file << endl;
              }
            }
            cout << ".";
            { XMLBlock xda(file, "DataArray", " type=\"UInt8\" Name=\"types\" NumberOfComponents=\"1\" format=\"ascii\"");
              for(size_t cell=0; cell<numberOfCells; ++cell){
                if(cell%12==0) file << indent.c_str();
                file << cell_type << " ";
                if(cell%12==11 || cell==(numberOfCells-1) ) file << endl;
              }
            }
          }
        }
      }
    }
    cout << "done" << endl;
  }

private:
//  /// get ids of first and last row in [firstBlockId,lastBlockId]
//  std::pair<int,int> getRowIds(int const firstBlockId, int const lastBlockId) const
//  {
//    int const firstIdx = (firstBlockId > 0) ? linearization.rows(0,firstBlockId) : 0;
//    int const lastIdx = firstIdx + linearization.rows(firstBlockId, lastBlockId);
//    return std::make_pair(firstIdx, lastIdx);
//  }

  void writeConnectivity(std::ofstream &file, SparseInt row, SparseInt col, SparseInt numRows) const
  {
    int const upperLeftVertexId = row+col*numRows;
    file << upperLeftVertexId << " " << upperLeftVertexId+1 << " " << upperLeftVertexId+numRows << " " << upperLeftVertexId+numRows+1 << " ";
  }

  Assembler& assembler;
  Functional const& f;
  EvaluationPoint x;
  VariableSet const& variableSet;
  Scalar tolerance, increment;
  std::vector<Scalar> firstDerivativeError;
  MatrixAsTriplet<Scalar,SparseInt> secondDerivativeError;
  bool firstDerivativeOk_, secondDerivativeOk_;
};
} // end of namespace Kaskade
#endif /* CHECK_DERIVATIVE_HH */


#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <sstream>
#include <boost/regex.hpp>
#include <sqlite3ext.h>
#include <cmath>
SQLITE_EXTENSION_INIT1

#ifdef REDUCED_PRECISION
typedef float real;
#else
typedef double real;
#endif

/**
  Get a string argument from SQLite. It assumes you already know it's there.
  You can check that with argc() or trust SQLite to do that. */
const char *getSQLiteString(
    sqlite3_context *ctx,
    sqlite3_value *arg,
    const std::string& func,
    const std::string& name) {
  const char *value = (const char *) sqlite3_value_text(arg);
  if (value) {
    return value;
  } else {
    sqlite3_result_error(ctx, (func + "(): missing " + name).data(), -1);
    return NULL;
  }
}

extern "C" {
  /**
   * Regular Expressions
   **/

  /**
   * @brief Match a regular expression, giving a boolean.
   */
  void match(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
      const char *re = getSQLiteString(ctx, argv[0], "match", "regular expression");
      const char *subject = getSQLiteString(ctx, argv[1], "match", "subject");
      if (!re || !subject) return;

      // Catch all for regex errors and API cleanliness
      try {
        boost::regex regex(re);
        sqlite3_result_int(ctx, boost::regex_match(subject, regex));
      } catch (const boost::regex_error& e) {
        sqlite3_result_error(ctx, e.what(), -1);
      }

      return;
  }

  /**
   * @brief Search a string with a regex.
   *
   * This differs from match. See Boost::regex for more information.
   */
  void search(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
    const char *re = getSQLiteString(ctx, argv[0], "search", "regular expression");
    const char *subject = getSQLiteString(ctx, argv[1], "search", "subject");
    if (!re || !subject) return;

    // Catch all for regex errors and API cleanliness
    try {
      boost::regex regex(re);
      sqlite3_result_int(ctx, boost::regex_search(std::string(subject), regex));
    } catch (const boost::regex_error& e) {
      sqlite3_result_error(ctx, e.what(), -1);
    }
    return;
  }

  /**
   * @brief Substitute regex matches with a formatted string.
   * For more information about the string format, see Boost::regex or the README.
   */
  void sub(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
    const char *re = getSQLiteString(ctx, argv[0], "sub", "regular expression");
    const char *format = getSQLiteString(ctx, argv[1], "sub", "format string");
    const char *subject = getSQLiteString(ctx, argv[2], "sub", "subject");
    if (!re || !format || !subject) return;
    // Catch all for regex errors and API cleanliness
    try {
      boost::regex regex(re);
      std::string replaced = boost::regex_replace(std::string(subject), regex, format);
      sqlite3_result_text(ctx, replaced.data(), -1, SQLITE_TRANSIENT);
    } catch (const boost::regex_error& e) {
      sqlite3_result_error(ctx, e.what(), -1);
    }
    return;
  }

  /*****************************************************************************
   * Math
   */

   /** Helper functions */
  /** Perform a unary operator on either a scalar or vector */
  void vunop(sqlite3_context *ctx, sqlite3_value *arg, std::function<real(real)> unop) {
   switch (sqlite3_value_type(arg)) {
     case SQLITE_INTEGER:
     case SQLITE_FLOAT:
       sqlite3_result_double(ctx, unop(sqlite3_value_double(arg)));
       break;
     case SQLITE_BLOB: {
       int len = sqlite3_value_bytes(arg) / sizeof(real);
       real *vec = (real*) sqlite3_value_blob(arg);
       real result_vec[len];
       for (int i=0; i<len; i++) {
         result_vec[i] = unop(vec[i]);
       }
       sqlite3_result_blob(ctx, result_vec, len*sizeof(real), SQLITE_TRANSIENT);
       break;
     }
     default:
       sqlite3_result_error(ctx,
         "Invalid value type for vector/scalar operation. " \
         "Possible causes:\n" \
         "\tPerforming operations on an empty vector, \n"\
         "\tUsing text as a vector or scalar (convert them first with cast() or vread()),\n"\
         "\tNot space-separating values for vread().", -1);
      break;
   }
  }

  // Get the length of a scalar or vector, but scalars are -1 to distinguish them.
  int vecOrScalarLen(sqlite3_value *arg) {
    switch (sqlite3_value_type(arg)) {
      case SQLITE_INTEGER:
      case SQLITE_FLOAT:
        return -1;
      case SQLITE_BLOB:
        return sqlite3_value_bytes(arg) / sizeof(real);
      default:
        return 0;
    }
  }

  // Run a binary operation on two real vectors.
  // It's just choosing a type that makes this operation kinda hairy.
  void vbinop(sqlite3_context *ctx, sqlite3_value **argv, std::function<real(real, real)> binop) {
    int left_len = vecOrScalarLen(argv[0]);
    int right_len = vecOrScalarLen(argv[1]);

    if (left_len == 0 || right_len == 0) {
      // Error
      sqlite3_result_error(ctx,
        "Invalid value type for vector/scalar operation. " \
        "Possible causes:\n" \
        "\tPerforming operations on an empty vector, \n"\
        "\tUsing text as a vector or scalar (convert them first with cast() or vread()),\n"\
        "\tNot space-separating values for vread().", -1);
      return;
    } else if (left_len == -1 && right_len == -1) {
      // Scalar-scalar op
      sqlite3_result_double(ctx, binop(
        sqlite3_value_double(argv[0]),
        sqlite3_value_double(argv[1])
      ));
    } else if (left_len == -1) {
      // Scalar-vector op
      real left = sqlite3_value_double(argv[0]);
      real *right_vec = (real*) sqlite3_value_blob(argv[1]);
      real result_vec[right_len];
      for (int i=0; i<right_len; i++) {
        result_vec[i] = binop(left, right_vec[i]);
      }
      sqlite3_result_blob(ctx, result_vec, right_len*sizeof(real), SQLITE_TRANSIENT);
    } else if (right_len == -1) {
      // Vector-scalar op
      real *left_vec = (real*) sqlite3_value_blob(argv[0]);
      real right = sqlite3_value_double(argv[1]);
      real result_vec[left_len];
      for (int i=0; i<left_len; i++) {
        result_vec[i] = binop(left_vec[i], right);
      }
      sqlite3_result_blob(ctx, result_vec, left_len*sizeof(real), SQLITE_TRANSIENT);
    } else {
      // Vector-vector op
      int len = fmin(left_len, right_len);
      real *left_vec = (real*) sqlite3_value_blob(argv[0]);
      real *right_vec = (real*) sqlite3_value_blob(argv[1]);
      real result_vec[len];
      for (int i=0; i<len; i++) {
        result_vec[i] = binop(left_vec[i], right_vec[i]);
      }
      sqlite3_result_blob(ctx, result_vec, len*sizeof(real), SQLITE_TRANSIENT);
    }
  }

  /*****************************************************************************
   * SQLite-visible functions, aggregates and wrappers (all start with sql_)
   */

   void sql_sin(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
     vunop(ctx, argv[0], sin);
   }
   void sql_asin(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
     vunop(ctx, argv[0], asin);
   }
   void sql_cos(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
     vunop(ctx, argv[0], cos);
   }
   void sql_acos(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
     vunop(ctx, argv[0], acos);
   }
   void sql_tan(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
     vunop(ctx, argv[0], tan);
   }
   void sql_atan(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
     vunop(ctx, argv[0], atan);
   }
   void sql_log(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
     vunop(ctx, argv[0], log);
   }
   void sql_exp(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
     vunop(ctx, argv[0], exp);
   }
   void sql_pow(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
     vbinop(ctx, argv, pow);
   }
   void sql_sqrt(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
     vunop(ctx, argv[0], sqrt);
   }

  // Create a new zero vector
  void sql_vzero(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
    int len = sqlite3_value_int(argv[0]);

    real result_vec[len];
    for (int i=0; i<len; i++) {
      result_vec[i] = 0;
    }
    sqlite3_result_blob(ctx, result_vec, len*sizeof(real), SQLITE_TRANSIENT);
  }

  // Create a new one vector
  void sql_vone(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
    int len = sqlite3_value_int(argv[0]);

    real result_vec[len];
    for (int i=0; i<len; i++) {
      result_vec[i] = 1;
    }
    sqlite3_result_blob(ctx, result_vec, len*sizeof(real), SQLITE_TRANSIENT);
  }

  // Add two vectors
  void sql_add(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
    vbinop(ctx, argv, [](real a, real b){ return a + b; });
  }
  // Subtract the second vector from the first
  void sql_subtract(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
    vbinop(ctx, argv, [](real a, real b){ return a - b; });
  }
  // Multiply two vectors
  void sql_mult(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
    vbinop(ctx, argv, [](real a, real b){ return a * b; });
  }
  // Divide two vectors
  void sql_div(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
    vbinop(ctx, argv, [](real a, real b){ return a / b; });
  }

  // Complain to SQLite if we don't get the vectors we want (saves code later)
  bool must_be_vectors(
      const char *name,
      sqlite3_context *ctx,
      int argc,
      sqlite3_value **argv) {
    for (int i=0; i<argc; i++) {
      if (sqlite3_value_type(argv[i]) != SQLITE_BLOB) {
        char buffer[100];
        snprintf(buffer, 100, "Wrong datatype supplied. %s requires %d vectors.", name, argc);
        sqlite3_result_error(ctx, buffer, -1);
        return false;
      }
    }
    return true;
  }

  // Compute the sum of the elements of a vector: It's not the most numerically
  // stable way though.
  void sql_vsum(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
    if (!must_be_vectors("vsum", ctx, 1, argv)) return;
    int len = sqlite3_value_bytes(argv[0]) / sizeof(real);
    real *vec = (real*) sqlite3_value_blob(argv[0]);
    real end = 0.0;
    for (int i=0; i<len; i++) {
      end += vec[i];
    }
    sqlite3_result_double(ctx, end);
  }

  // Compute the product of the elements of a vector: It's not the most
  // numerically stable way though.
  void sql_vprod(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
    if (!must_be_vectors("vprod", ctx, 1, argv)) return;
    int len = sqlite3_value_bytes(argv[0]) / sizeof(real);
    real *vec = (real*) sqlite3_value_blob(argv[0]);
    real end = 0.0;
    for (int i=0; i<len; i++) {
      end *= vec[i];
    }
    sqlite3_result_double(ctx, end);
  }

  // Compute the dot product: It's not the most numerically stable way though.
  void sql_dot(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
    if (!must_be_vectors("dot", ctx, 2, argv)) return;
    int len = fmin(sqlite3_value_bytes(argv[0]), sqlite3_value_bytes(argv[1]))
              / sizeof(real);

    real *a = (real*) sqlite3_value_blob(argv[0]);
    real *b = (real*) sqlite3_value_blob(argv[1]);
    real end = 0.0;
    for (int i=0; i<len; i++) {
      end += a[i] * b[i];
    }
    sqlite3_result_double(ctx, end);
  }

  // Compute the cosine similarity between two vectors
  void sql_cossim(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
    if (!must_be_vectors("cossim", ctx, 2, argv)) return;
    int len = fmin(sqlite3_value_bytes(argv[0]), sqlite3_value_bytes(argv[1]))
              / sizeof(real);

    real *a = (real*) sqlite3_value_blob(argv[0]);
    real *b = (real*) sqlite3_value_blob(argv[1]);

    real asq = 0.0;  for (int i=0; i<len; i++) asq  += a[i] * a[i];
    real bsq = 0.0;  for (int i=0; i<len; i++) bsq  += b[i] * b[i];
    real absq = 0.0; for (int i=0; i<len; i++) absq += a[i] * b[i];

    sqlite3_result_double(ctx, absq / (sqrt(asq) * sqrt(bsq)));
  }

  typedef struct FatBuffer {
    int len; // Length of the content array (in bytes)
    int count; // Useful for accumulators.
    real content[1]; // The actual data (probably longer than 1, btw)
  } FatBuffer;
  int wrapped_size(int len_reals) {
    return (2*sizeof(int)) + (len_reals*sizeof(real));
  };

  // Compute the sum of many vectors (as part of an aggregate)
  void sql_vsum_aggregate_step(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
    if (!must_be_vectors("vsum_aggregate", ctx, 1, argv)) return;
    /// --- The only hard part here is allocating the accumulator. ---
    // First decide how long we'd like it to be *in units*. Optimally it should
    // be the same size as the vector we're reading right now.
    int target_len = sqlite3_value_bytes(argv[0]) / sizeof(real);
    if (target_len == 0) return; // Do nothing for empty vectors.
    // Maybe allocate it (or it may return an existing buffer of any length
    // or it may return NULL.)
    FatBuffer *accum = (FatBuffer*) sqlite3_aggregate_context(ctx, wrapped_size(target_len));
    if (accum == NULL) {sqlite3_result_error_nomem(ctx); return;} // die.
    // Find out how long the buffer really is, *in bytes*
    // If it's new, it's 0'd out so the length will be 0 and we know we got the
    // size we asked for. Otherwise use the length we asked for.
    if (accum->len == 0) accum->len = target_len;
    // Now we should iterate the lesser of the two lengths (in units of reals)
    int iter_len = fmin(accum->len, target_len);
    real *vec = (real*) sqlite3_value_blob(argv[0]);
    for (int i=0; i<iter_len; i++) {
      accum->content[i] += vec[i];
    }
    accum->count++;
  }

  // Compute the sum of many vectors (as part of an aggregate) (final part)
  void sql_vsum_aggregate_final(sqlite3_context *ctx) {
    /// --- The only hard part here is allocating the accumulator. ---
    // Maybe allocate it (or it may return an existing buffer of any length
    // or it may return NULL.)
    FatBuffer *accum = (FatBuffer*) sqlite3_aggregate_context(ctx, 0);
    if (accum == NULL) {
      // I'm not sure if a zero-length blob or NULL is better.
      // I'm choosing this right now merely because of my dislike for NULL.
      sqlite3_result_zeroblob(ctx, 0);
    } else {
      sqlite3_result_blob(ctx, accum->content, accum->len, SQLITE_TRANSIENT);
    }
  }
  // Compute the average of many vectors (as part of an aggregate) (final part)
  void sql_vavg_aggregate_final(sqlite3_context *ctx) {
    /// --- The only hard part here is allocating the accumulator. ---
    // Maybe allocate it (or it may return an existing buffer of any length
    // or it may return NULL.)
    FatBuffer *accum = (FatBuffer*) sqlite3_aggregate_context(ctx, 0);
    if (accum == NULL) {
      // I'm not sure if a zero-length blob or NULL is better.
      // I'm choosing this right now merely because of my dislike for NULL.
      sqlite3_result_zeroblob(ctx, 0);
    } else {
      for (int i=0; i<accum->len; i++) accum->content[i] /= accum->count;
      sqlite3_result_blob(ctx, accum->content, accum->len, SQLITE_TRANSIENT);
    }
  }

  // Read a vector from a space separated string
  void sql_vread(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
    const char *text = getSQLiteString(ctx, argv[0], "vread", "space separated floating point values");
    std::string str(text);
    std::stringstream stream(str);
    std::vector<real> vec;
    for (real item; stream >> item;) {
      vec.push_back(item);
    }
    sqlite3_result_blob(ctx, &vec.front(), vec.size()*sizeof(real), SQLITE_TRANSIENT);
  }

  // Write a vector to a space separated string
  void sql_vshow(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
    int len = sqlite3_value_bytes(argv[0]) / sizeof(real);
    real *vec = (real*) sqlite3_value_blob(argv[0]);
    std::stringstream stream;
    for (int i=0; i<len; i++) {
      stream << vec[i] << ' ';
    }
    sqlite3_result_text(ctx, stream.str().c_str(), -1, SQLITE_TRANSIENT);
  }

  int sqlite3_extension_init(sqlite3 *db, char **err, const sqlite3_api_routines *api) {
    SQLITE_EXTENSION_INIT2(api)

    // Regular Expressions
    sqlite3_create_function(db, "match",  2, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, match, NULL, NULL);
    sqlite3_create_function(db, "search", 2, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, search, NULL, NULL);
    sqlite3_create_function(db, "sub",    3, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sub, NULL, NULL);

    // Math
    sqlite3_create_function(db, "sin",    1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_sin, NULL, NULL);
    sqlite3_create_function(db, "asin",   1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_asin, NULL, NULL);
    sqlite3_create_function(db, "cos",    1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_cos, NULL, NULL);
    sqlite3_create_function(db, "acos",   1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_acos, NULL, NULL);
    sqlite3_create_function(db, "tan",    1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_tan, NULL, NULL);
    sqlite3_create_function(db, "atan",   1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_atan, NULL, NULL);
    sqlite3_create_function(db, "log",    1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_log, NULL, NULL);
    sqlite3_create_function(db, "exp",    1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_exp, NULL, NULL);
    sqlite3_create_function(db, "pow",    2, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_pow, NULL, NULL);
    sqlite3_create_function(db, "sqrt",   1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_sqrt, NULL, NULL);

    // Vector operations
    sqlite3_create_function(db, "vread", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_vread, NULL, NULL);
    sqlite3_create_function(db, "vshow", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_vshow, NULL, NULL);
    //sqlite3_create_function(db, "vburst", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sqlvburst, NULL, NULL);
    //sqlite3_create_function(db, "vcollapse", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sqlvcollase, NULL, NULL);
    sqlite3_create_function(db, "vzero",  1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_vzero, NULL, NULL);
    sqlite3_create_function(db, "vone",   1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_vone, NULL, NULL);
    sqlite3_create_function(db, "add",    2, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_add, NULL, NULL);
    sqlite3_create_function(db, "subtract", 2, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_subtract, NULL, NULL);
    sqlite3_create_function(db, "mult",   2, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_mult, NULL, NULL);
    sqlite3_create_function(db, "div",    2, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_div, NULL, NULL);
    sqlite3_create_function(db, "vsum",   1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_vsum, NULL, NULL);
    sqlite3_create_function(db, "vprod",  1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_vprod, NULL, NULL);
    sqlite3_create_function(db, "dot",    2, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_dot, NULL, NULL);
    sqlite3_create_function(db, "cossim", 2, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_cossim, NULL, NULL);

    // Aggregate functions
    sqlite3_create_function(db, "vsum_aggregate", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, NULL, sql_vsum_aggregate_step, sql_vsum_aggregate_final);
    sqlite3_create_function(db, "vavg_aggregate", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, NULL, sql_vsum_aggregate_step, sql_vavg_aggregate_final);
    return 0;
  }
}


// ====================================================================
// This file is part of FlexibleSUSY.
//
// FlexibleSUSY is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License,
// or (at your option) any later version.
//
// FlexibleSUSY is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with FlexibleSUSY.  If not, see
// <http://www.gnu.org/licenses/>.
// ====================================================================

/**
 * @file standard_model.hpp
 *
 * @brief contains class for Standard model running and self-energies
 *
 */

#ifndef STANDARD_MODEL_H
#define STANDARD_MODEL_H

#include "betafunction.hpp"
#include "standard_model_physical.hpp"
#include "loop_corrections.hpp"
#include "threshold_corrections.hpp"
#include "error.hpp"
#include "problems.hpp"
#include "config.h"
#include "physical_input.hpp"

#include <array>
#include <iosfwd>
#include <string>

#include <Eigen/Core>

namespace softsusy {
   class QedQcd;
} // namespace softsusy

namespace flexiblesusy {

class EWSB_solver;

namespace standard_model_info {

   enum Particles : int {VG, Hp, Fv, Ah, hh, VP, VZ, Fd, Fu, Fe, VWp,
      NUMBER_OF_PARTICLES};

   enum Parameters : int {g1, g2, g3, Lambdax, Yu0_0, Yu0_1, Yu0_2, Yu1_0,
      Yu1_1, Yu1_2, Yu2_0, Yu2_1, Yu2_2, Yd0_0, Yd0_1, Yd0_2, Yd1_0, Yd1_1, Yd1_2
      , Yd2_0, Yd2_1, Yd2_2, Ye0_0, Ye0_1, Ye0_2, Ye1_0, Ye1_1, Ye1_2, Ye2_0,
      Ye2_1, Ye2_2, mu2, v, NUMBER_OF_PARAMETERS};

   enum Mixings : int {ReVd00, ImVd00, ReVd01, ImVd01, ReVd02, ImVd02,
      ReVd10, ImVd10, ReVd11, ImVd11, ReVd12, ImVd12, ReVd20, ImVd20, ReVd21,
      ImVd21, ReVd22, ImVd22, ReUd00, ImUd00, ReUd01, ImUd01, ReUd02, ImUd02,
      ReUd10, ImUd10, ReUd11, ImUd11, ReUd12, ImUd12, ReUd20, ImUd20, ReUd21,
      ImUd21, ReUd22, ImUd22, ReVu00, ImVu00, ReVu01, ImVu01, ReVu02, ImVu02,
      ReVu10, ImVu10, ReVu11, ImVu11, ReVu12, ImVu12, ReVu20, ImVu20, ReVu21,
      ImVu21, ReVu22, ImVu22, ReUu00, ImUu00, ReUu01, ImUu01, ReUu02, ImUu02,
      ReUu10, ImUu10, ReUu11, ImUu11, ReUu12, ImUu12, ReUu20, ImUu20, ReUu21,
      ImUu21, ReUu22, ImUu22, ReVe00, ImVe00, ReVe01, ImVe01, ReVe02, ImVe02,
      ReVe10, ImVe10, ReVe11, ImVe11, ReVe12, ImVe12, ReVe20, ImVe20, ReVe21,
      ImVe21, ReVe22, ImVe22, ReUe00, ImUe00, ReUe01, ImUe01, ReUe02, ImUe02,
      ReUe10, ImUe10, ReUe11, ImUe11, ReUe12, ImUe12, ReUe20, ImUe20, ReUe21,
      ImUe21, ReUe22, ImUe22, NUMBER_OF_MIXINGS};

   extern const double normalization_g1;
   extern const double normalization_g2;
   extern const double normalization_g3;

   extern const std::array<int, NUMBER_OF_PARTICLES> particle_multiplicities;
   extern const std::array<std::string, NUMBER_OF_PARTICLES> particle_names;
   extern const std::array<std::string, NUMBER_OF_PARTICLES> particle_latex_names;
   extern const std::array<std::string, NUMBER_OF_PARAMETERS> parameter_names;
   extern const std::array<std::string, NUMBER_OF_MIXINGS> particle_mixing_names;
   extern const std::string model_name;
   constexpr bool is_low_energy_model = false;
   constexpr bool is_supersymmetric_model = false;

   class Standard_model_particle_names : public Names {
   public:
      virtual ~Standard_model_particle_names() = default;
      virtual const std::string& get(int index) const override {
         return particle_names[index];
      }
      virtual int size() const override {
         return NUMBER_OF_PARAMETERS;
      }
   };

   class Standard_model_parameter_names : public Names {
   public:
      virtual ~Standard_model_parameter_names() = default;
      virtual const std::string& get(int index) const override {
         return parameter_names[index];
      }
      virtual int size() const override {
         return NUMBER_OF_PARTICLES;
      }
   };

   const Standard_model_particle_names  particle_names_getter{};
   const Standard_model_parameter_names parameter_names_getter{};

} // namespace standard_model_info

namespace standard_model {

template <class T>
class StandardModel;

/**
 * @class Standard_model
 * @brief model class with routines for SM running and self-energies
 */
class Standard_model : public Beta_function {
public:

   Standard_model();
   Standard_model(double scale_, double loops_, double thresholds_
   , double g1_, double g2_, double g3_, double Lambdax_, const Eigen::Matrix<
   double,3,3>& Yu_, const Eigen::Matrix<double,3,3>& Yd_, const Eigen::Matrix<
   double,3,3>& Ye_, double mu2_, double v_);
   Standard_model(const Standard_model&) = default;
   Standard_model(Standard_model&&) = default;

   virtual ~Standard_model() = default;

   Standard_model& operator=(const Standard_model&) = default;
   Standard_model& operator=(Standard_model&&) = default;

   /// number of EWSB equations
   static const int number_of_ewsb_equations = 1;

   void calculate_DRbar_masses();
   void calculate_pole_masses();
   void check_pole_masses_for_tachyons();
   void do_force_output(bool);
   bool do_force_output() const;
   void set_ewsb_iteration_precision(double);
   void set_ewsb_loop_order(int);
   void set_loop_corrections(const Loop_corrections&);
   const Loop_corrections& get_loop_corrections() const;
   void set_threshold_corrections(const Threshold_corrections&);
   const Threshold_corrections& get_threshold_corrections() const;
   void set_pole_mass_loop_order(int);
   int get_pole_mass_loop_order() const;
   void set_physical(const Standard_model_physical&);
   double get_ewsb_iteration_precision() const;
   double get_ewsb_loop_order() const;
   const Standard_model_physical& get_physical() const;
   Standard_model_physical& get_physical();
   const Problems& get_problems() const;
   Problems& get_problems();
   int solve_ewsb_tree_level();
   int solve_ewsb_one_loop();
   int solve_ewsb();            ///< solve EWSB at ewsb_loop_order level

   virtual Eigen::ArrayXd beta() const override;
   virtual Eigen::ArrayXd get() const override;
   void print(std::ostream& out = std::cerr) const;
   virtual void set(const Eigen::ArrayXd&) override;

   Standard_model calc_beta() const;
   void clear();
   void clear_running_parameters();
   void clear_DRbar_parameters();
   void clear_problems();

   void calculate_spectrum();
   std::string name() const;
   virtual void run_to(double scale, double eps = -1.0) override;
   void set_precision(double);
   double get_precision() const;

   void set_physical_input(const Physical_input& input_) { input = input_; }
   const Physical_input& get_physical_input() const { return input; }
   Physical_input& get_physical_input() { return input; }

   void initialise_from_input(const softsusy::QedQcd&);

   void set_g1(double g1_) { g1 = g1_; }
   void set_g2(double g2_) { g2 = g2_; }
   void set_g3(double g3_) { g3 = g3_; }
   void set_Lambdax(double Lambdax_) { Lambdax = Lambdax_; }
   void set_Yu(const Eigen::Matrix<double,3,3>& Yu_) { Yu = Yu_; }
   void set_Yu(int i, int k, double value) { Yu(i,k) = value; }
   void set_Yd(const Eigen::Matrix<double,3,3>& Yd_) { Yd = Yd_; }
   void set_Yd(int i, int k, double value) { Yd(i,k) = value; }
   void set_Ye(const Eigen::Matrix<double,3,3>& Ye_) { Ye = Ye_; }
   void set_Ye(int i, int k, double value) { Ye(i,k) = value; }
   void set_mu2(double mu2_) { mu2 = mu2_; }
   void set_v(double v_) { v = v_; }

   double get_g1() const { return g1; }
   double get_g2() const { return g2; }
   double get_g3() const { return g3; }
   double get_Lambdax() const { return Lambdax; }
   const Eigen::Matrix<double,3,3>& get_Yu() const { return Yu; }
   double get_Yu(int i, int k) const { return Yu(i,k); }
   const Eigen::Matrix<double,3,3>& get_Yd() const { return Yd; }
   double get_Yd(int i, int k) const { return Yd(i,k); }
   const Eigen::Matrix<double,3,3>& get_Ye() const { return Ye; }
   double get_Ye(int i, int k) const { return Ye(i,k); }
   double get_mu2() const { return mu2; }
   double get_v() const { return v; }

   double get_MVG() const { return MVG; }
   double get_MHp() const { return MHp; }
   const Eigen::Array<double,3,1>& get_MFv() const { return MFv; }
   double get_MFv(int i) const { return MFv(i); }
   double get_MAh() const { return MAh; }
   double get_Mhh() const { return Mhh; }
   double get_MVP() const { return MVP; }
   double get_MVZ() const { return MVZ; }
   const Eigen::Array<double,3,1>& get_MFd() const { return MFd; }
   double get_MFd(int i) const { return MFd(i); }
   const Eigen::Array<double,3,1>& get_MFu() const { return MFu; }
   double get_MFu(int i) const { return MFu(i); }
   const Eigen::Array<double,3,1>& get_MFe() const { return MFe; }
   double get_MFe(int i) const { return MFe(i); }
   double get_MVWp() const { return MVWp; }
   const Eigen::Array<double,2,1>& get_MVPVZ() const { return MVPVZ; }
   double get_MVPVZ(int i) const { return MVPVZ(i); }

   const Eigen::Matrix<std::complex<double>,3,3>& get_Vd() const { return Vd; }
   const std::complex<double>& get_Vd(int i, int k) const { return Vd(i,k); }
   const Eigen::Matrix<std::complex<double>,3,3>& get_Ud() const { return Ud; }
   const std::complex<double>& get_Ud(int i, int k) const { return Ud(i,k); }
   const Eigen::Matrix<std::complex<double>,3,3>& get_Vu() const { return Vu; }
   const std::complex<double>& get_Vu(int i, int k) const { return Vu(i,k); }
   const Eigen::Matrix<std::complex<double>,3,3>& get_Uu() const { return Uu; }
   const std::complex<double>& get_Uu(int i, int k) const { return Uu(i,k); }
   const Eigen::Matrix<std::complex<double>,3,3>& get_Ve() const { return Ve; }
   const std::complex<double>& get_Ve(int i, int k) const { return Ve(i,k); }
   const Eigen::Matrix<std::complex<double>,3,3>& get_Ue() const { return Ue; }
   const std::complex<double>& get_Ue(int i, int k) const { return Ue(i,k); }
   const Eigen::Matrix<double,2,2>& get_ZZ() const { return ZZ; }
   double get_ZZ(int i, int k) const { return ZZ(i,k); }

   double get_mass_matrix_VG() const;
   void calculate_MVG();
   double get_mass_matrix_Hp() const;
   void calculate_MHp();
   Eigen::Matrix<double,3,3> get_mass_matrix_Fv() const;
   void calculate_MFv();
   double get_mass_matrix_Ah() const;
   void calculate_MAh();
   double get_mass_matrix_hh() const;
   void calculate_Mhh();
   double get_mass_matrix_VP() const;
   void calculate_MVP();
   double get_mass_matrix_VZ() const;
   void calculate_MVZ();
   Eigen::Matrix<double,3,3> get_mass_matrix_Fd() const;
   void calculate_MFd();
   Eigen::Matrix<double,3,3> get_mass_matrix_Fu() const;
   void calculate_MFu();
   Eigen::Matrix<double,3,3> get_mass_matrix_Fe() const;
   void calculate_MFe();
   double get_mass_matrix_VWp() const;
   void calculate_MVWp();
   Eigen::Matrix<double,2,2> get_mass_matrix_VPVZ() const;
   void calculate_MVPVZ();

   double get_ewsb_eq_hh_1() const;

   double CpconjHpHphh() const;
   double CpconjHpVWpVP() const;
   double CpconjHpVZVWp() const;
   double CpHpgWpCbargZ() const;
   double CpconjHpbargWpCgZ() const;
   double CpHpgZbargWp() const;
   double CpconjHpbargZgWp() const;
   double CpHpconjHpAhAh() const;
   double CpHpconjHphhhh() const;
   double CpHpconjHpconjHpHp() const;
   std::complex<double> CpconjHpVWpAh() const;
   double CpconjHpVWphh() const;
   double CpconjHpVPHp() const;
   double CpconjHpVZHp() const;
   double CpHpconjHpconjVWpVWp() const;
   std::complex<double> CpHpconjHpVZVZ() const;
   std::complex<double> CpconjHpbarFdFuPR(int gI1, int gI2) const;
   std::complex<double> CpconjHpbarFdFuPL(int gI1, int gI2) const;
   double CpconjHpbarFeFvPR(int , int ) const;
   std::complex<double> CpconjHpbarFeFvPL(int gI1, int gI2) const;
   double CpAhhhAh() const;
   std::complex<double> CpAhbargWpgWp() const;
   std::complex<double> CpAhbargWpCgWpC() const;
   double CpAhAhAhAh() const;
   double CpAhAhhhhh() const;
   double CpAhAhconjHpHp() const;
   std::complex<double> CpAhVZhh() const;
   std::complex<double> CpAhconjVWpHp() const;
   double CpAhAhconjVWpVWp() const;
   std::complex<double> CpAhAhVZVZ() const;
   std::complex<double> CpAhbarFdFdPR(int gI1, int gI2) const;
   std::complex<double> CpAhbarFdFdPL(int gI1, int gI2) const;
   std::complex<double> CpAhbarFeFePR(int gI1, int gI2) const;
   std::complex<double> CpAhbarFeFePL(int gI1, int gI2) const;
   std::complex<double> CpAhbarFuFuPR(int gI1, int gI2) const;
   std::complex<double> CpAhbarFuFuPL(int gI1, int gI2) const;
   double CphhAhAh() const;
   double Cphhhhhh() const;
   double CphhVZVZ() const;
   double CphhbargWpgWp() const;
   double CphhbargWpCgWpC() const;
   double CphhbargZgZ() const;
   double CphhconjHpHp() const;
   double CphhconjVWpVWp() const;
   double CphhhhAhAh() const;
   double Cphhhhhhhh() const;
   double CphhhhconjHpHp() const;
   std::complex<double> CphhVZAh() const;
   double CphhconjVWpHp() const;
   double CphhhhconjVWpVWp() const;
   std::complex<double> CphhhhVZVZ() const;
   std::complex<double> CphhbarFdFdPR(int gI1, int gI2) const;
   std::complex<double> CphhbarFdFdPL(int gI1, int gI2) const;
   std::complex<double> CphhbarFeFePR(int gI1, int gI2) const;
   std::complex<double> CphhbarFeFePL(int gI1, int gI2) const;
   std::complex<double> CphhbarFuFuPR(int gI1, int gI2) const;
   std::complex<double> CphhbarFuFuPL(int gI1, int gI2) const;
   std::complex<double> CpVZhhAh() const;
   double CpVZVZhh() const;
   double CpVZbargWpgWp() const;
   double CpVZbargWpCgWpC() const;
   double CpVZconjHpHp() const;
   double CpVZconjVWpHp() const;
   std::complex<double> CpVZVZAhAh() const;
   std::complex<double> CpVZVZhhhh() const;
   std::complex<double> CpVZVZconjHpHp() const;
   double CpVZconjVWpVWp() const;
   double CpVZbarFdFdPL(int gI1, int gI2) const;
   double CpVZbarFdFdPR(int gI1, int gI2) const;
   double CpVZbarFeFePL(int gI1, int gI2) const;
   double CpVZbarFeFePR(int gI1, int gI2) const;
   double CpVZbarFuFuPL(int gI1, int gI2) const;
   double CpVZbarFuFuPR(int gI1, int gI2) const;
   double CpVZbarFvFvPL(int gI1, int gI2) const;
   double CpVZbarFvFvPR(int , int ) const;
   double CpVZVZconjVWpVWp1() const;
   double CpVZVZconjVWpVWp2() const;
   double CpVZVZconjVWpVWp3() const;
   std::complex<double> CpconjVWpHpAh() const;
   double CpconjVWpHphh() const;
   double CpconjVWpVPHp() const;
   double CpconjVWpVWphh() const;
   double CpconjVWpVZHp() const;
   double CpconjVWpbargPgWp() const;
   double CpconjVWpbargWpCgP() const;
   double CpconjVWpbargWpCgZ() const;
   double CpconjVWpbargZgWp() const;
   double CpVWpconjVWpAhAh() const;
   double CpVWpconjVWphhhh() const;
   double CpVWpconjVWpconjHpHp() const;
   double CpconjVWpVWpVP() const;
   double CpconjVWpVZVWp() const;
   std::complex<double> CpconjVWpbarFdFuPL(int gI1, int gI2) const;
   double CpconjVWpbarFdFuPR(int , int ) const;
   std::complex<double> CpconjVWpbarFeFvPL(int gI1, int gI2) const;
   double CpconjVWpbarFeFvPR(int , int ) const;
   double CpVWpconjVWpVPVP1() const;
   double CpVWpconjVWpVPVP2() const;
   double CpVWpconjVWpVPVP3() const;
   double CpVWpconjVWpVZVZ1() const;
   double CpVWpconjVWpVZVZ2() const;
   double CpVWpconjVWpVZVZ3() const;
   double CpVWpconjVWpconjVWpVWp1() const;
   double CpVWpconjVWpconjVWpVWp2() const;
   double CpVWpconjVWpconjVWpVWp3() const;
   std::complex<double> CpbarUFdFdAhPL(int gO2, int gI1) const;
   std::complex<double> CpbarUFdFdAhPR(int gO1, int gI1) const;
   std::complex<double> CpbarUFdhhFdPL(int gO2, int gI2) const;
   std::complex<double> CpbarUFdhhFdPR(int gO1, int gI2) const;
   std::complex<double> CpbarUFdVGFdPR(int gO2, int gI2) const;
   std::complex<double> CpbarUFdVGFdPL(int gO1, int gI2) const;
   std::complex<double> CpbarUFdVPFdPR(int gO2, int gI2) const;
   std::complex<double> CpbarUFdVPFdPL(int gO1, int gI2) const;
   std::complex<double> CpbarUFdVZFdPR(int gO2, int gI2) const;
   std::complex<double> CpbarUFdVZFdPL(int gO1, int gI2) const;
   std::complex<double> CpbarUFdconjHpFuPL(int gO2, int gI2) const;
   std::complex<double> CpbarUFdconjHpFuPR(int gO1, int gI2) const;
   double CpbarUFdconjVWpFuPR(int , int ) const;
   std::complex<double> CpbarUFdconjVWpFuPL(int gO1, int gI2) const;
   std::complex<double> CpbarUFuFuAhPL(int gO2, int gI1) const;
   std::complex<double> CpbarUFuFuAhPR(int gO1, int gI1) const;
   std::complex<double> CpbarUFuhhFuPL(int gO2, int gI2) const;
   std::complex<double> CpbarUFuhhFuPR(int gO1, int gI2) const;
   std::complex<double> CpbarUFuHpFdPL(int gO2, int gI2) const;
   std::complex<double> CpbarUFuHpFdPR(int gO1, int gI2) const;
   std::complex<double> CpbarUFuVGFuPR(int gO2, int gI2) const;
   std::complex<double> CpbarUFuVGFuPL(int gO1, int gI2) const;
   std::complex<double> CpbarUFuVPFuPR(int gO2, int gI2) const;
   std::complex<double> CpbarUFuVPFuPL(int gO1, int gI2) const;
   double CpbarUFuVWpFdPR(int , int ) const;
   std::complex<double> CpbarUFuVWpFdPL(int gO1, int gI2) const;
   std::complex<double> CpbarUFuVZFuPR(int gO2, int gI2) const;
   std::complex<double> CpbarUFuVZFuPL(int gO1, int gI2) const;
   std::complex<double> CpbarUFeFeAhPL(int gO2, int gI1) const;
   std::complex<double> CpbarUFeFeAhPR(int gO1, int gI1) const;
   std::complex<double> CpbarUFehhFePL(int gO2, int gI2) const;
   std::complex<double> CpbarUFehhFePR(int gO1, int gI2) const;
   std::complex<double> CpbarUFeVPFePR(int gO2, int gI2) const;
   std::complex<double> CpbarUFeVPFePL(int gO1, int gI2) const;
   std::complex<double> CpbarUFeVZFePR(int gO2, int gI2) const;
   std::complex<double> CpbarUFeVZFePL(int gO1, int gI2) const;
   std::complex<double> CpbarUFeconjHpFvPL(int gO2, int gI2) const;
   double CpbarUFeconjHpFvPR(int , int ) const;
   double CpbarUFeconjVWpFvPR(int , int ) const;
   double CpbarUFeconjVWpFvPL(int gO1, int gI2) const;
   std::complex<double> CpbarFdFdAhPL(int gO2, int gI1) const;
   std::complex<double> CpbarFdFdAhPR(int gO1, int gI1) const;
   std::complex<double> CpbarFdhhFdPL(int gO2, int gI2) const;
   std::complex<double> CpbarFdhhFdPR(int gO1, int gI2) const;
   double CpbarFdVZFdPR(int gO2, int gI2) const;
   double CpbarFdVZFdPL(int gO1, int gI2) const;
   std::complex<double> CpbarFdconjHpFuPL(int gO2, int gI2) const;
   std::complex<double> CpbarFdconjHpFuPR(int gO1, int gI2) const;
   double CpbarFdconjVWpFuPR(int , int ) const;
   std::complex<double> CpbarFdconjVWpFuPL(int gO1, int gI2) const;
   std::complex<double> CpbarFeFeAhPL(int gO2, int gI1) const;
   std::complex<double> CpbarFeFeAhPR(int gO1, int gI1) const;
   std::complex<double> CpbarFehhFePL(int gO2, int gI2) const;
   std::complex<double> CpbarFehhFePR(int gO1, int gI2) const;
   double CpbarFeVZFePR(int gO2, int gI2) const;
   double CpbarFeVZFePL(int gO1, int gI2) const;
   std::complex<double> CpbarFeconjHpFvPL(int gO2, int gI2) const;
   double CpbarFeconjHpFvPR(int , int ) const;
   double CpbarFeconjVWpFvPR(int , int ) const;
   std::complex<double> CpbarFeconjVWpFvPL(int gO1, int gI2) const;
   std::complex<double> CpbarFuFuAhPL(int gO2, int gI1) const;
   std::complex<double> CpbarFuFuAhPR(int gO1, int gI1) const;
   std::complex<double> CpbarFuhhFuPL(int gO2, int gI2) const;
   std::complex<double> CpbarFuhhFuPR(int gO1, int gI2) const;
   std::complex<double> CpbarFuHpFdPL(int gO2, int gI2) const;
   std::complex<double> CpbarFuHpFdPR(int gO1, int gI2) const;
   double CpbarFuVPFuPR(int gO2, int gI2) const;
   double CpbarFuVPFuPL(int gO1, int gI2) const;
   double CpbarFuVWpFdPR(int , int ) const;
   std::complex<double> CpbarFuVWpFdPL(int gO1, int gI2) const;
   double CpbarFuVZFuPR(int gO2, int gI2) const;
   double CpbarFuVZFuPL(int gO1, int gI2) const;
   std::complex<double> self_energy_Hp_1loop(double p ) const;
   std::complex<double> self_energy_Ah_1loop(double p ) const;
   std::complex<double> self_energy_hh_1loop(double p ) const;
   std::complex<double> self_energy_VZ_1loop(double p ) const;
   std::complex<double> self_energy_VWp_1loop(double p ) const;
   std::complex<double> self_energy_Fd_1loop_1(double p , int gO1, int gO2) const;
   std::complex<double> self_energy_Fd_1loop_PR(double p , int gO1, int gO2) const;
   std::complex<double> self_energy_Fd_1loop_PL(double p , int gO1, int gO2) const;
   std::complex<double> self_energy_Fu_1loop_1(double p , int gO1, int gO2) const;
   std::complex<double> self_energy_Fu_1loop_PR(double p , int gO1, int gO2) const;
   std::complex<double> self_energy_Fu_1loop_PL(double p , int gO1, int gO2) const;
   std::complex<double> self_energy_Fe_1loop_1(double p , int gO1, int gO2) const;
   std::complex<double> self_energy_Fe_1loop_PR(double p , int gO1, int gO2) const;
   std::complex<double> self_energy_Fe_1loop_PL(double p , int gO1, int gO2) const;
   std::complex<double> self_energy_Fd_1loop_1_heavy_rotated(double p , int gO1, int gO2) const;
   std::complex<double> self_energy_Fd_1loop_PR_heavy_rotated(double p , int gO1, int gO2) const;
   std::complex<double> self_energy_Fd_1loop_PL_heavy_rotated(double p , int gO1, int gO2) const;
   std::complex<double> self_energy_Fe_1loop_1_heavy_rotated(double p , int gO1, int gO2) const;
   std::complex<double> self_energy_Fe_1loop_PR_heavy_rotated(double p , int gO1, int gO2) const;
   std::complex<double> self_energy_Fe_1loop_PL_heavy_rotated(double p , int gO1, int gO2) const;
   std::complex<double> self_energy_Fu_1loop_1_heavy_rotated(double p , int gO1, int gO2) const;
   std::complex<double> self_energy_Fu_1loop_PR_heavy_rotated(double p , int gO1, int gO2) const;
   std::complex<double> self_energy_Fu_1loop_PL_heavy_rotated(double p , int gO1, int gO2) const;
   std::complex<double> self_energy_Fu_1loop_1_heavy(double p , int gO1, int gO2) const;
   std::complex<double> self_energy_Fu_1loop_PR_heavy(double p , int gO1, int gO2) const;
   std::complex<double> self_energy_Fu_1loop_PL_heavy(double p , int gO1, int gO2) const;

   Eigen::Matrix<std::complex<double>,3,3> self_energy_Fd_1loop_1(double p) const;
   Eigen::Matrix<std::complex<double>,3,3> self_energy_Fd_1loop_PR(double p) const;
   Eigen::Matrix<std::complex<double>,3,3> self_energy_Fd_1loop_PL(double p) const;
   Eigen::Matrix<std::complex<double>,3,3> self_energy_Fu_1loop_1(double p) const;
   Eigen::Matrix<std::complex<double>,3,3> self_energy_Fu_1loop_PR(double p) const;
   Eigen::Matrix<std::complex<double>,3,3> self_energy_Fu_1loop_PL(double p) const;
   Eigen::Matrix<std::complex<double>,3,3> self_energy_Fe_1loop_1(double p) const;
   Eigen::Matrix<std::complex<double>,3,3> self_energy_Fe_1loop_PR(double p) const;
   Eigen::Matrix<std::complex<double>,3,3> self_energy_Fe_1loop_PL(double p) const;

   std::complex<double> tadpole_hh_1loop() const;

   /// calculates the tadpoles at current loop order
   Eigen::Matrix<double, number_of_ewsb_equations, 1> tadpole_equations() const;

   /// calculates Higgs 2-loop self-energy
   double self_energy_hh_2loop(double p) const;
   /// calculates Higgs 3-loop self-energy
   double self_energy_hh_3loop() const;

   void calculate_MVG_pole();
   void calculate_MFv_pole();
   void calculate_Mhh_pole();
   void calculate_MVP_pole();
   void calculate_MVZ_pole();
   void calculate_MFd_pole();
   void calculate_MFu_pole();
   void calculate_MFe_pole();
   void calculate_MVWp_pole();
   double calculate_MVWp_pole(double);
   double calculate_MVZ_pole(double);

   double calculate_MFv_DRbar(double, int) const;
   double calculate_MFe_DRbar(double, int) const;
   double calculate_MFu_DRbar(double, int) const;
   double calculate_MFd_DRbar(double, int) const;
   double calculate_MVP_DRbar(double);
   double calculate_MVZ_DRbar(double);
   double calculate_MVWp_DRbar(double);
   double calculate_Mhh_DRbar(double);

   double ThetaW() const;

   double calculate_delta_alpha_em(double alphaEm) const;
   double calculate_delta_alpha_s(double alphaS) const;
   void calculate_Lambdax_DRbar();
   double calculate_theta_w(const softsusy::QedQcd&, double alpha_em_drbar);
   void calculate_Yu_DRbar(const softsusy::QedQcd&);
   void calculate_Yd_DRbar(const softsusy::QedQcd&);
   void calculate_Ye_DRbar(const softsusy::QedQcd&);
   double recalculate_mw_pole(double);
   double max_rel_diff(const Standard_model& old) const;

protected:

   // Running parameters
   double g1{};
   double g2{};
   double g3{};
   double Lambdax{};
   Eigen::Matrix<double,3,3> Yu{Eigen::Matrix<double,3,3>::Zero()};
   Eigen::Matrix<double,3,3> Yd{Eigen::Matrix<double,3,3>::Zero()};
   Eigen::Matrix<double,3,3> Ye{Eigen::Matrix<double,3,3>::Zero()};
   double mu2{};
   double v{};

private:

   static const int numberOfParameters = 33;

   struct Beta_traces {
      double traceYdAdjYd{};
      double traceYeAdjYe{};
      double traceYuAdjYu{};
      double traceYdAdjYdYdAdjYd{};
      double traceYeAdjYeYeAdjYe{};
      double traceYuAdjYuYuAdjYu{};
      double traceYdAdjYuYuAdjYd{};
      double traceYdAdjYdYdAdjYdYdAdjYd{};
      double traceYdAdjYdYdAdjYuYuAdjYd{};
      double traceYdAdjYuYuAdjYdYdAdjYd{};
      double traceYdAdjYuYuAdjYuYuAdjYd{};
      double traceYeAdjYeYeAdjYeYeAdjYe{};
      double traceYuAdjYuYuAdjYuYuAdjYu{};
   };
   void calc_beta_traces(Beta_traces&) const;

   double calc_beta_g1_one_loop(const Beta_traces&) const;
   double calc_beta_g1_two_loop(const Beta_traces&) const;
   double calc_beta_g1_three_loop(const Beta_traces&) const;
   double calc_beta_g2_one_loop(const Beta_traces&) const;
   double calc_beta_g2_two_loop(const Beta_traces&) const;
   double calc_beta_g2_three_loop(const Beta_traces&) const;
   double calc_beta_g3_one_loop(const Beta_traces&) const;
   double calc_beta_g3_two_loop(const Beta_traces&) const;
   double calc_beta_g3_three_loop(const Beta_traces&) const;
   double calc_beta_Lambdax_one_loop(const Beta_traces&) const;
   double calc_beta_Lambdax_two_loop(const Beta_traces&) const;
   double calc_beta_Lambdax_three_loop(const Beta_traces&) const;
   Eigen::Matrix<double,3,3> calc_beta_Yu_one_loop(const Beta_traces&) const;
   Eigen::Matrix<double,3,3> calc_beta_Yu_two_loop(const Beta_traces&) const;
   Eigen::Matrix<double,3,3> calc_beta_Yu_three_loop(const Beta_traces&) const;
   Eigen::Matrix<double,3,3> calc_beta_Yd_one_loop(const Beta_traces&) const;
   Eigen::Matrix<double,3,3> calc_beta_Yd_two_loop(const Beta_traces&) const;
   Eigen::Matrix<double,3,3> calc_beta_Yd_three_loop(const Beta_traces&) const;
   Eigen::Matrix<double,3,3> calc_beta_Ye_one_loop(const Beta_traces&) const;
   Eigen::Matrix<double,3,3> calc_beta_Ye_two_loop(const Beta_traces&) const;
   Eigen::Matrix<double,3,3> calc_beta_Ye_three_loop(const Beta_traces&) const;
   double calc_beta_mu2_one_loop(const Beta_traces&) const;
   double calc_beta_mu2_two_loop(const Beta_traces&) const;
   double calc_beta_mu2_three_loop(const Beta_traces&) const;
   double calc_beta_v_one_loop(const Beta_traces&) const;
   double calc_beta_v_two_loop(const Beta_traces&) const;
   double calc_beta_v_three_loop(const Beta_traces&) const;

   using EWSB_vector_t = Eigen::Matrix<double,number_of_ewsb_equations,1>;

   class EEWSBStepFailed : public Error {
   public:
      virtual ~EEWSBStepFailed() = default;
      virtual std::string what() const override { return "Could not perform EWSB step."; }
   };

   int ewsb_loop_order{2};
   int pole_mass_loop_order{2};
   bool force_output{false};      ///< switch to force output of pole masses
   double precision{1e-3};        ///< RG running precision
   double ewsb_iteration_precision{1e-5};
   Standard_model_physical physical{}; ///< contains the pole masses and mixings
   Problems problems{standard_model_info::model_name,
                     &standard_model_info::particle_names_getter,
                     &standard_model_info::parameter_names_getter};
   Loop_corrections loop_corrections{}; ///< used loop pole mass corrections
   Threshold_corrections threshold_corrections{}; ///< used low-energy threshold corrections
   Physical_input input{};

   int get_number_of_ewsb_iterations() const;
   int get_number_of_mass_iterations() const;
   int solve_ewsb_iteratively();
   int solve_ewsb_iteratively(int);
   int solve_ewsb_iteratively_with(EWSB_solver*, const Eigen::Matrix<double, number_of_ewsb_equations, 1>&);
   int solve_ewsb_tree_level_custom();
   EWSB_vector_t ewsb_initial_guess();
   EWSB_vector_t ewsb_step() const;
   void copy_DRbar_masses_to_pole_masses();

   void initial_guess_for_parameters(const softsusy::QedQcd&);
   bool check_convergence(const Standard_model& old) const;

   // Passarino-Veltman loop functions
   double A0(double) const;
   double B0(double, double, double) const;
   double B1(double, double, double) const;
   double B00(double, double, double) const;
   double B22(double, double, double) const;
   double H0(double, double, double) const;
   double F0(double, double, double) const;
   double G0(double, double, double) const;

   // DR-bar masses
   double MVG{};
   double MHp{};
   Eigen::Array<double,3,1> MFv{Eigen::Array<double,3,1>::Zero()};
   double MAh{};
   double Mhh{};
   double MVP{};
   double MVZ{};
   Eigen::Array<double,3,1> MFd{Eigen::Array<double,3,1>::Zero()};
   Eigen::Array<double,3,1> MFu{Eigen::Array<double,3,1>::Zero()};
   Eigen::Array<double,3,1> MFe{Eigen::Array<double,3,1>::Zero()};
   double MVWp{};
   Eigen::Array<double,2,1> MVPVZ{Eigen::Array<double,2,1>::Zero()};

   // DR-bar mixing matrices
   Eigen::Matrix<std::complex<double>,3,3> Vd{Eigen::Matrix<std::complex<double>,3,3>::Zero()};
   Eigen::Matrix<std::complex<double>,3,3> Ud{Eigen::Matrix<std::complex<double>,3,3>::Zero()};
   Eigen::Matrix<std::complex<double>,3,3> Vu{Eigen::Matrix<std::complex<double>,3,3>::Zero()};
   Eigen::Matrix<std::complex<double>,3,3> Uu{Eigen::Matrix<std::complex<double>,3,3>::Zero()};
   Eigen::Matrix<std::complex<double>,3,3> Ve{Eigen::Matrix<std::complex<double>,3,3>::Zero()};
   Eigen::Matrix<std::complex<double>,3,3> Ue{Eigen::Matrix<std::complex<double>,3,3>::Zero()};
   Eigen::Matrix<double,2,2> ZZ{Eigen::Matrix<double,2,2>::Zero()};

};

std::ostream& operator<<(std::ostream&, const Standard_model&);

} // namespace standard_model

} // namespace flexiblesusy

#endif


/*=============================================================================

  NifTK: A software platform for medical image computing.

  Copyright (c) University College London (UCL). All rights reserved.

  This software is distributed WITHOUT ANY WARRANTY; without even
  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  PURPOSE.

  See LICENSE.txt in the top level directory for details.

=============================================================================*/

#include <boost/lexical_cast.hpp>
#include <boost/math/special_functions/round.hpp>
#include "niftkConversionUtils.h"
#include <math.h>
#include <algorithm>

namespace niftk
{

//-----------------------------------------------------------------------------
std::string ConvertToString(int x)
{
  return boost::lexical_cast<std::string>(x);
}

//-----------------------------------------------------------------------------
std::string ConvertToString(unsigned int x)
{
  return boost::lexical_cast<std::string>(x);
}

//-----------------------------------------------------------------------------
std::string ConvertToString(unsigned long long x)
{
  return boost::lexical_cast<std::string>(x);
}

//-----------------------------------------------------------------------------
std::string ConvertToString(long int x)
{
  return boost::lexical_cast<std::string>(x);
}

//-----------------------------------------------------------------------------
std::string ConvertToString(long unsigned int x)
{
  return boost::lexical_cast<std::string>(x);
}

//-----------------------------------------------------------------------------
std::string ConvertToString(double x)
{
  return boost::lexical_cast<std::string>(x);
}

//-----------------------------------------------------------------------------
std::string ConvertToString(bool x)
{
  return boost::lexical_cast<std::string>(x);
}

//-----------------------------------------------------------------------------
std::string ConvertToString(float x)
{
  return boost::lexical_cast<std::string>(x);
}

//-----------------------------------------------------------------------------
int ConvertToInt(const std::string& x)
{
  return boost::lexical_cast<int>(x);
}

//-----------------------------------------------------------------------------
double ConvertToDouble(const std::string& x)
{
  return boost::lexical_cast<double>(x);
}

//-----------------------------------------------------------------------------
bool ConvertToBool(const std::string& x)
{
  if ("true" == x || "True" == x)
  {
    return true;
  }
  else
  {
    return false;
  }
}

//-----------------------------------------------------------------------------
double CalculateVarianceFromFWHM(double fwhm)
{
  // http://mathworld.wolfram.com/GaussianFunction.html
  double variance = ((fwhm * fwhm) / (8.0 * log(2.0)));
  return variance;
}

//-----------------------------------------------------------------------------
double CalculateStdDevFromFWHM(double fwhm)
{
  // http://mathworld.wolfram.com/GaussianFunction.html
  double stdDev = fwhm / (2.0 * sqrt(2.0 * log(2.0)));
  return stdDev;
}

//-----------------------------------------------------------------------------
double ConvertFirstVoxelCoordinateToMiddleOfImageCoordinate(
  double millimetreCoordinateOfFirstVoxel,
  int numberOfVoxelsInThatAxis,
  double voxelSpacingInThatAxis)
{
  return (static_cast<double>(numberOfVoxelsInThatAxis - 1)
      * voxelSpacingInThatAxis / 2.0)
    + millimetreCoordinateOfFirstVoxel;
}

//-----------------------------------------------------------------------------
double ConvertMiddleOfImageCoordinateToFirstVoxelCoordinate(
    double millimetreCoordinateOfMiddleVoxel,
    int numberOfVoxelsInThatAxis,
    double voxelSpacingInThatAxis)
{
  return (static_cast<double>(numberOfVoxelsInThatAxis - 1)
      * voxelSpacingInThatAxis / -2.0)
    + millimetreCoordinateOfMiddleVoxel;
}

//-----------------------------------------------------------------------------
double fixRangeTo1(double d)
{
  return std::min(std::max(d, -1.0), 1.0);

}

//-----------------------------------------------------------------------------
int Round(double d)
{
  return boost::math::iround<double>(d);
}

//-----------------------------------------------------------------------------
double Round(double d, int numberDecimalPlaces)
{
  double f = pow((double)10, numberDecimalPlaces);
  return ((double)Round(d*f))/f;
}

//-----------------------------------------------------------------------------
std::string GetLastNCharacters(std::string s, int n)
{
  int length = s.length();
  if (n >= length)
  {
    return s;
  }
  else
  {
    int start = length - n;

    if (start < 0)
    {
      start = 0;
    }

    return s.substr(start, n);
  }
}

} // end namespace