/* Copyright (C) 2012-2017 IBM Corp.
 * This program is Licensed under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *   http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. See accompanying LICENSE file.
 */

/* Test_Timing.cpp - A program that tests the timing of various operations, outputs the results in a comma-separate-value (csv) format.
 */
#include <cassert>
#include <cstdio>
#include <memory>
#include <NTL/ZZ.h>
NTL_CLIENT

#include "FHE.h"
#include "timing.h"
#include "EncryptedArray.h"
#include "matmul.h"
#include "replicate.h"
#include "permutations.h"

// We measure low-level timing at all levels
class LowLvlTimingData {
public:
  long lvl;
  double addConst;
  double add;
  double multConst;
  double mult;
  double multBy2;
  double autoNative;
  double autoTypical;
  double innerProd;

  explicit LowLvlTimingData(long _lvl=-1) {lvl=_lvl;}
};

// We only measure high-level timing at one level (lvl=8 or lower)
class HighLvlTimingData {
public:
  long lvl;
  double rotate;
  double shift;
  double permute;
  double matmul;
  double replicate;
  double replAll;

  explicit HighLvlTimingData(long _lvl=-1) {lvl=_lvl;}
};

class OtherTimingData {
public:
  double init2;
  double init4;
  double keyGen;
  double encode2;
  double encode2d;
  double encode4;
  double encode4d;
  double encrypt;
  double decode2;
  double decode4;
  double decrypt;
};

class TimingData {
public:
  long m;
  long phim;
  long nSlots;
  long p;
  vector<LowLvlTimingData> lowLvl;
  HighLvlTimingData highLvl;
  OtherTimingData other;

  explicit TimingData(long _m=-1) {m=_m;}
};

static FHEtimer _init_timer_2("init2", "--");
static FHEtimer _init_timer_4("init4", "--");

void timeInit(long m, long p, long r, long d, long L, long nTests)
{
  for (long i=0; i<nTests; i++) {
    cerr << "." << std::flush;

    // Complicated mumbo-jumbo to get one of two timers, depending on r
    auto_timer _init_timer((r>1)? &_init_timer_4 : &_init_timer_2);

    FHEcontext context(m, p, r);

    ZZX G;
    if (d==1) SetX(G); // set G(X)=X
    else G = context.alMod.getFactorsOverZZ()[0];

    buildModChain(context, L, /*c=*/3);
    EncryptedArray ea(context, G);
    _init_timer.stop();

    FHE_NTIMER_START(keyGen);
    FHESecKey secretKey(context);
    const FHEPubKey& publicKey = secretKey;
    secretKey.GenSecKey(64); // A Hamming-weight-64 secret key
    addSome1DMatrices(secretKey); // compute key-switching matrices
    addSomeFrbMatrices(secretKey);
    FHE_NTIMER_STOP(keyGen);

    ZZX poly;
    NewPlaintextArray pp(ea);
    random(ea, pp);

    Ctxt cc(publicKey);

    if (r==1 && d==1) {
      FHE_NTIMER_START(encode2);
      ea.encode(poly, pp);
      FHE_NTIMER_STOP(encode2);
    }
    else if (r==1 && d!=1) {
      FHE_NTIMER_START(encode2d);
      ea.encode(poly, pp);
      FHE_NTIMER_STOP(encode2d);
    }
    else if (r!=1 && d==1) {
      FHE_NTIMER_START(encode4);
      ea.encode(poly, pp);
      FHE_NTIMER_STOP(encode4);
    }
    else {
      FHE_NTIMER_START(encode4d);
      ea.encode(poly, pp);
      FHE_NTIMER_STOP(encode4d);
    }

    FHE_NTIMER_START(encrypt);
    publicKey.Encrypt(cc, poly);
    FHE_NTIMER_STOP(encrypt);

    FHE_NTIMER_START(decrypt);
    secretKey.Decrypt(poly, cc);
    FHE_NTIMER_STOP(decrypt);

    if (r>1) {
      FHE_NTIMER_START(decode4);
      ea.decode(pp, poly);
      FHE_NTIMER_STOP(decode4);
    } else {
      FHE_NTIMER_START(decode2);
      ea.decode(pp, poly);
      FHE_NTIMER_STOP(decode2);
    }
  }
}

// Returns either a random automorphism amount or an amount
// for which we have a key-switching matrix s^k -> s.
long rotationAmount(const EncryptedArray& ea, const FHEPubKey& publicKey,
	       bool onlyWithMatrix)
{
  const PAlgebra& pa = ea.getPAlgebra();
  long nSlots = pa.getNSlots();
  long r = RandomBnd(nSlots);
  long k = pa.ith_rep(r);
  if (onlyWithMatrix) { // return the 1st step in the path to k
    const KeySwitch& matrix = publicKey.getNextKSWmatrix(k,0);
    k = matrix.fromKey.getPowerOfX();
  }
  return k;
}

void timeOps(const EncryptedArray& ea, const FHEPubKey& publicKey, Ctxt& ret,
	     const vector<Ctxt>& c, ZZX& p, long nTests, LowLvlTimingData& td)
{
  assert(c.size()>=3);
  vector<Ctxt> cc = c;
  // perform operations at a lower level
  long level = td.lvl;
  if (level>0) for (long i=0; i<(long)cc.size(); i++)
    cc[i].modDownToLevel(level);
  else {
    level = cc[0].findBaseLevel();
    td.lvl = level;
  }

  // inner-product of vectors
  cerr << "." << std::flush;
  FHE_NTIMER_START(innerProduct);
  innerProduct(ret,cc,cc);
  ret.modDownToLevel(ret.findBaseLevel());
  FHE_NTIMER_STOP(innerProduct);

  // Multiplication with 2,3 arguments
  cerr << "." << std::flush;
  for (long i=0; i<nTests; i++) {
    Ctxt c0 = cc[0];
    FHE_NTIMER_START(multiplyBy);
    c0.multiplyBy(cc[1]);
    c0.modDownToLevel(c0.findBaseLevel());
    FHE_NTIMER_STOP(multiplyBy);
    ret += c0; // Just so the compiler doesn't optimize it away
  }

  if (level > 2) {
    cerr << "." << std::flush;
    for (long i=0; i<nTests; i++) {
      Ctxt c0 = cc[0];
      FHE_NTIMER_START(multiplyBy2);
      c0.multiplyBy2(cc[1],cc[2]);
      c0.modDownToLevel(c0.findBaseLevel()); // mod-down if needed
      FHE_NTIMER_STOP(multiplyBy2);
      ret += c0; // Just so the compiler doesn't optimize it away
    }
  }

  // Multiply by constant
  cerr << "." << std::flush;
  for (long i=0; i<4*nTests; i++) {
    Ctxt c0 = cc[0];
    FHE_NTIMER_START(multByConstant);
    c0.multByConstant(p);
    FHE_NTIMER_STOP(multByConstant);
    ret -= c0; // Just so the compiler doesn't optimize it away
  }

  // Add constant
  cerr << "." << std::flush;
  for (long i=0; i<10*nTests; i++) {
    Ctxt c0 = cc[0];
    FHE_NTIMER_START(addConstant);
    c0.addConstant(p);
    FHE_NTIMER_STOP(addConstant);
    ret += c0; // Just so the compiler doesn't optimize it away
  }

  // Addition
  cerr << "." << std::flush;
  for (long i=0; i<10*nTests; i++) {
    FHE_NTIMER_START(add);
    ret += cc[0];
    FHE_NTIMER_STOP(add);
  }

  // Rotation by an amount k for which we have a key-switching matrix
  cerr << "." << std::flush;
  for (long i=0; i<nTests; i++) {
    Ctxt c0 = cc[0];
    long k = rotationAmount(ea,publicKey,/*withMatrix=*/true);
    FHE_NTIMER_START(nativeAutomorph);
    c0.smartAutomorph(k);
    c0.modDownToLevel(c0.findBaseLevel());
    FHE_NTIMER_STOP(nativeAutomorph);
    ret += c0; // Just so the compiler doesn't optimize it away
  }
  // Rotation by a random amount k
  cerr << "." << std::flush;
  for (long i=0; i<nTests; i++) {
    Ctxt c0 = cc[0];
    long k = rotationAmount(ea,publicKey,/*withMatrix=*/false);
    FHE_NTIMER_START(automorph);
    c0.smartAutomorph(k);
    c0.modDownToLevel(c0.findBaseLevel()); // mod-down if needed
    FHE_NTIMER_STOP(automorph);
    ret += c0; // Just so the compiler doesn't optimize it away
  }

  // record the results
  const FHEtimer *tp;

  tp = getTimerByName("addConstant");
  td.addConst = tp->getTime() / tp->getNumCalls();

  tp = getTimerByName("add");
  td.add = tp->getTime() / tp->getNumCalls();

  tp = getTimerByName("multByConstant");
  td.multConst = tp->getTime() / tp->getNumCalls();

  tp = getTimerByName("multiplyBy");
  td.mult = tp->getTime() / tp->getNumCalls();

  tp = getTimerByName("multiplyBy2");
  if (tp && tp->getNumCalls() > 0)
    td.multBy2 = tp->getTime() / tp->getNumCalls();
  else td.multBy2 = 0;

  tp = getTimerByName("nativeAutomorph");
  td.autoNative = tp->getTime() / tp->getNumCalls();

  tp = getTimerByName("automorph");
  td.autoTypical = tp->getTime() / tp->getNumCalls();

  tp = getTimerByName("innerProduct");
  td.innerProd = tp->getTime() / tp->getNumCalls();
  resetAllTimers();
}

// Implementation of the various random matrices is found here
#include "randomMatrices.h"
/*
 * Defined in this file are the following class templates:
 *
 *   class RandomMatrix: public MatMul1D_derived<type>
 *   class RandomMultiMatrix: public MatMul1D_derived<type>
 *   class RandomBlockMatrix: public BlockMatMul1D_derived<type>
 *   class RandomMultiBlockMatrix: public BlockMatMul1D_derived<type>
 *   class RandomFullMatrix: public MatMulFull_derived<type>
 *   class RandomFullBlockMatrix : public BlockMatMulFull_derived<type>
 *
 * Each of them has a corresponding build function, namely:
 *
 *   MatMul1D* buildRandomMatrix(const EncryptedArray& ea, long dim);
 *   MatMul1D* buildRandomMultiMatrix(const EncryptedArray& ea, long dim);
 *   BlockMatMul1D* buildRandomBlockMatrix(const EncryptedArray& ea, long dim);
 *   BlockMatMul1D* buildRandomMultiBlockMatrix(const EncryptedArray& ea, long dim);
 *   MatMulFull* buildRandomFullMatrix(EncryptedArray& ea);
 *   BlockMatMulFull* buildRandomFullBlockMatrix(EncryptedArray& ea);
 */

class ReplicateDummy : public ReplicateHandler {
public:
  ReplicateDummy() {}
  virtual void handle(const Ctxt& ctxt) {}
};

void timeHighLvl(const EncryptedArray& ea, const FHEPubKey& publicKey,
		 Ctxt& ret, const vector<Ctxt>& c, GeneratorTrees& trees,
		 long nTests, HighLvlTimingData& td)
{
  Ctxt tmp = c[0];
  tmp.modDownToLevel(td.lvl);
  cerr << "." << std::flush;
  std::unique_ptr< MatMulFull > ptr(buildRandomFullMatrix(ea));
  if (ea.getTag()==PA_GF2_tag) {
    RandomFullMatrix<PA_GF2>::ExecType mat_exec(*ptr);
    mat_exec.upgrade();
    FHE_NTIMER_START(MatMul);
    mat_exec.mul(tmp);
    FHE_NTIMER_STOP(MatMul);
  } else {
    RandomFullMatrix<PA_zz_p>::ExecType mat_exec(*ptr);
    mat_exec.upgrade();
    FHE_NTIMER_START(MatMul);
    mat_exec.mul(tmp);
    FHE_NTIMER_STOP(MatMul);
  }
  ret = tmp;

  for (long i=0; i<nTests; i++) {
    cerr << "." << std::flush;
    long nSlots = ea.size();
    long r = RandomBnd(nSlots);
    tmp = c[i % c.size()];
    tmp.modDownToLevel(td.lvl);
    // time rotation
    FHE_NTIMER_START(rotate);
    ea.rotate(tmp, r);
    FHE_NTIMER_STOP(rotate);
    // time shift, amount between -nSlots/2 and +nSlots/2
    if (r>nSlots/2) r -= nSlots;
    FHE_NTIMER_START(shift);
    ea.shift(tmp, r);
    FHE_NTIMER_STOP(shift);
    ret += tmp; // just so the compiler will not optimize it out
  }

  cerr << "." << std::flush;
  for (long i=0; i<nTests && i<ea.size(); i++) {
    tmp = c[i % c.size()];
    tmp.modDownToLevel(td.lvl);
    FHE_NTIMER_START(replicate);
    replicate(ea, tmp, i);
    FHE_NTIMER_STOP(replicate);
    ret += tmp; // just so the compiler will not optimize it out
  }

  cerr << "." << std::flush;
  ReplicateDummy handler;
  tmp = c[1];
  tmp.modDownToLevel(td.lvl);
  FHE_NTIMER_START(replicateAll);
  replicateAll(ea, tmp, &handler);
  FHE_NTIMER_STOP(replicateAll);
  ret += tmp;

  cerr << "." << std::flush;
  Permut pi;
  randomPerm(pi, trees.getSize());
  tmp = c[2];
  tmp.modDownToLevel(td.lvl);

  PermNetwork net;
  FHE_NTIMER_START(permutation);
  net.buildNetwork(pi, trees);  // Build a permutation network for pi
  net.applyToCtxt(tmp, ea);         // Apply permutation netwrok
  FHE_NTIMER_STOP(permutation);
  ret +=tmp; // just so the compiler will not optimize it out

  // record the timing data
  const FHEtimer *tp;

  tp = getTimerByName("rotate");
  td.rotate = tp->getTime() / tp->getNumCalls();

  tp = getTimerByName("shift");
  td.shift = tp->getTime() / tp->getNumCalls();

  tp = getTimerByName("permutation");
  td.permute = tp->getTime() / tp->getNumCalls();

  tp = getTimerByName("MatMul");
  td.matmul = tp->getTime() / tp->getNumCalls();

  tp = getTimerByName("replicate");
  td.replicate = tp->getTime() / tp->getNumCalls();

  tp = getTimerByName("replicateAll");
  td.replAll = tp->getTime() / tp->getNumCalls();
  resetAllTimers();
}

void  TimeIt(long m, long p, TimingData& data, bool high=false)
{
  setTimersOn();
  resetAllTimers();
  long phim = phi_N(m);
  long L = floor((7.2*phim)/(FHE_pSize* /*cc*/1.33* (110+/*k*/80)));
  if (L<5) L=5; // Make sure we have at least a few primes

  // Initialize a context with r=2,d=1
  auto_timer _init_timer(&_init_timer_4);
  FHEcontext context(m, 2, 2);
  buildModChain(context, L, /*c=*/3);

  ZZX G; SetX(G); // G(X) = X
  EncryptedArray ea(context, G);
  _init_timer.stop();

  FHE_NTIMER_START(keyGen);
  FHESecKey secretKey(context);
  const FHEPubKey& publicKey = secretKey;
  secretKey.GenSecKey(64); // A Hamming-weight-64 secret key
  addSome1DMatrices(secretKey); // compute key-switching matrices
  FHE_NTIMER_STOP(keyGen);

  // record timing results for init/geygen, etc.
  data.m = m;
  data.phim = phim;
  data.nSlots = ea.size();
  data.p = p;

  timeInit(m, p, /*r=*/1, /*d=*/1, L, /*nTests=*/4);
  timeInit(m, p, /*r=*/1, /*d=*/0, L, /*nTests=*/4);
  timeInit(m, p, /*r=*/2, /*d=*/1, L, /*nTests=*/4);
  timeInit(m, p, /*r=*/2, /*d=*/0, L, /*nTests=*/4);

  //  printAllTimers();
  const FHEtimer *tp;

  tp = getTimerByName("init2");
  data.other.init2 = tp? tp->getTime() / tp->getNumCalls() : 0;

  tp = getTimerByName("init4");
  data.other.init4 = tp? tp->getTime() / tp->getNumCalls() : 0;

  tp = getTimerByName("keyGen");
  data.other.keyGen = tp->getTime() / tp->getNumCalls();

  tp = getTimerByName("encode2");
  data.other.encode2 = tp->getTime() / tp->getNumCalls();

  tp = getTimerByName("encode2d");
  data.other.encode2d = tp->getTime() / tp->getNumCalls();

  tp = getTimerByName("encode4");
  data.other.encode4 = tp->getTime() / tp->getNumCalls();

  tp = getTimerByName("encode4d");
  data.other.encode4d = tp->getTime() / tp->getNumCalls();

  tp = getTimerByName("encrypt");
  data.other.encrypt = tp->getTime() / tp->getNumCalls();

  tp = getTimerByName("decrypt");
  data.other.decrypt = tp->getTime() / tp->getNumCalls();

  tp = getTimerByName("decode2");
  data.other.decode2 = tp->getTime() / tp->getNumCalls();

  tp = getTimerByName("decode4");
  data.other.decode4 = tp->getTime() / tp->getNumCalls();
  resetAllTimers();

  // time low-level operations
  cerr << "#" << std::flush;

  ZZX poly;
  NewPlaintextArray pp(ea);
  random(ea, pp);
  ea.encode(poly, pp);

  Ctxt cc(publicKey);
  long nTests = 10;
  vector<Ctxt> vc(nTests,cc);
  for (long i=0; i<nTests; i++) {
    random(ea, pp);
    ea.encrypt(vc[i], publicKey, pp);
  }

  LowLvlTimingData td;
  data.lowLvl.clear();
  for (long i=2; i<2*L-1; i*=2) {
    td.lvl=i;
    timeOps(ea, publicKey, cc,vc, poly, nTests, td);
    data.lowLvl.push_back(td);
  }
  td.lvl = 2*L -1;
  timeOps(ea, publicKey, cc,vc, poly, nTests, td);
  data.lowLvl.push_back(td);

  if (high && L > 4) { /// cannot test high-level routines at level <8
    cerr << "#" << std::flush;
    // Setup generator-descriptors for the PAlgebra generators
    Vec<GenDescriptor> vec(INIT_SIZE, ea.dimension());
    for (long i=0; i<ea.dimension(); i++)
      vec[i] = GenDescriptor(/*order=*/ea.sizeOfDimension(i),
			     /*good=*/ ea.nativeDimension(i), /*genIdx=*/i);

    // Some default for the width-bound, if not provided
    //  long widthBound = log2((double)ea.size()) -1;

    // Get the generator-tree structures and the corresponding hypercube
    GeneratorTrees trees;
    trees.buildOptimalTrees(vec, /*widthBound=*/7);
    //  cout << " cost =" << cost << endl;

    // build network for a random permutation, for the sole purpose
    // of adding key-switching matrices
    Permut pi;
    randomPerm(pi, trees.getSize());
    PermNetwork net;
    net.buildNetwork(pi, trees);
    addMatrices4Network(secretKey, net);

    data.highLvl.lvl = 8;
    timeHighLvl(ea, publicKey, cc, vc, trees, nTests, data.highLvl);
  }
  cerr << "!" << std::flush;
}

void printTimeData(TimingData& td)
{
  if (td.m <=0) return;

  OtherTimingData& otd = td.other;
  HighLvlTimingData& hl = td.highLvl;
  for (long i=0; i<(long)td.lowLvl.size(); i++) {
    LowLvlTimingData& ll = td.lowLvl[i];
    long lvl = ll.lvl;
    cout <<td.p<<","<<td.m<<","<<td.phim<<","<<td.nSlots<<","
	 <<otd.init2<<","<<otd.init4<<","<<otd.keyGen<<","
	 <<otd.encode2<<","<<otd.encode2d<<","
	 <<otd.encode4<<","<<otd.encode4d<<","
	 <<otd.encrypt<<","<<otd.decrypt<<","
	 <<otd.decode2<<","<<otd.decode4<<","
	 <<lvl<<","<<ll.addConst<<","<<ll.add<<","<<ll.multConst<<","
	 <<ll.mult<<",";
    if (ll.multBy2>0.0) cout << ll.multBy2;
    cout <<","<<ll.autoNative<<","
	 <<ll.autoTypical<<","<<ll.innerProd;
    if (lvl==hl.lvl)
      cout <<","<<hl.rotate<<","<<hl.shift<<","<<hl.permute<<","
	   <<hl.matmul<<","<<hl.replicate<<","<<hl.replAll;
    cout << endl;
  }
}

void usage(char *prog)
{
  cerr << "A program that tests the timing of various operations,\n";
  cerr << "  outputs the results in a comma-separate-value (csv) format.\n";
  cerr << "Usage: "<<prog<<" [ optional parameters ]... 2> logfile > results-file\n";
  cerr << "results on stdout in comma-separated-value format, ";
  cerr << "progress printed on stderr\n";
  cerr << "  optional parameters have the form 'attr1=val1 attr2=val2 ...'\n";
  cerr << "  e.g, 'm=11441 p=2 high=1'\n\n";
  cerr << "  m determines the cyclotomic ring, defaults to all the set\n";
  cerr << "    m in { 4051, 4369, 4859, 10261,11023,11441,\n";
  cerr << "          18631,20485,21845, 49981,53261       }\n";
  cerr << "  p is the plaintext base [default=2]" << endl;
  cerr << "  high=1 will time also high-level procedures [default==0]\n";
  exit(0);
}

int main(int argc, char *argv[])
{
  argmap_t argmap;
  argmap["p"] = "2";
  argmap["m"] = "0";
  argmap["high"] = "0";

  // get parameters from the command line
  if (!parseArgs(argc, argv, argmap)) usage(argv[0]);

  long p = atoi(argmap["p"]);
  long m = atoi(argmap["m"]);
  long high = atoi(argmap["high"]);

#define numTests 11
  long ms[numTests] = { 4051, 4369, 4859, 10261,11023,11441,
		 18631,20485,21845, 49981,53261};

  cout << "p,m,phim,nSlots,init(p),init(p^2),keyGen,encode(F_p),encode(F_p^d),encode(Z_p^r),encode(R_(p^r)^d),encrypt,decrypt,decode(p),decode(p^2),level,addConst,add,multConst,mult,mult2,autoNative,autoTypical,inProd10";
  if (high) cout << ",rotate,shift,permute,matmul,replicate,replAll\n";
  else cout << endl;

  TimingData td;
  if (m>0) {
    cerr << "\nTesting m="<<m;
    if (high) cerr << " (including high-level)";
    TimeIt(m, p, td, high);
    printTimeData(td);
  }
  else for (long i=0; i<numTests; i++) {
      cerr << "\nTesting m="<<ms[i];
      if (high) cout << " (including high-level)";
      TimeIt(ms[i], p, td, /*timeHighLvl=*/high);
      printTimeData(td);
    }
}


/*
 * 65. Valid Number
 *
 * Q: https://leetcode.com/problems/valid-number/
 * A: https://leetcode.com/problems/valid-number/discuss/1209666/A-few-solutions
 */

#define USE_BOOST 1  // 👈 uncomment this line to use boost 🚀

#ifdef USE_BOOST
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#endif
#include <algorithm>
#include <sstream>
#include <string>
#include <vector>

using namespace std;
using VS = vector<string>;

class Solution {
public:
    bool isNumber(string s) {
        auto indexAfterSign = [](auto& s) {
            auto i = 0;
            while (i < s.size() && s[i] == '+' || s[i] == '-')
                ++i;
            return i;
        };
        auto isOK = [&](auto& s, auto allowed) {
            auto i = indexAfterSign(s);
            auto t = s.substr(i);
            if (1 < i || t.empty() || t == ".")  // 💩 sanity check: 1 < i ensures there is at-most one + or - sign
                return false;
            return count_if(t.begin(), t.end(), [](auto c){ return c == '.'; }) <= allowed
                && all_of(t.begin(), t.end(), [](auto c){ return isdigit(c) || c == '.'; });
        };
        auto isInteger = [&](auto& s) { return isOK(s, 0); };
        auto isDecimal = [&](auto& s) { return isOK(s, 1); };
        VS A;
#if USE_BOOST
        boost::split(A, s, boost::is_any_of("eE")); // boost 🚀 is awesome! 🎉 🥳
#else
        //
        // 🙃 without boost: simply splitting a string on one 'e' xor 'E' is verbose
        //
        auto isE = [](auto c) { return c == 'e' || c == 'E'; };
        if (1 == count_if(s.begin(), s.end(), isE)) {
            string t;
            transform(s.begin(), s.end(), back_inserter(t), [&](auto c) { return !isE(c) ? c : ' '; });
            stringstream ss{ t };
            for (string word; ss >> word; A.emplace_back(word));
        }
#endif
        if (2 == A.size())
            return (isInteger(A[0]) || isDecimal(A[0])) && isInteger(A[1]);
        return isInteger(s) || isDecimal(s);
    }
};

int main() {
    Solution solution;
    VS A{ "2", "0089", "-0.1", "+3.14", "4.", "-.9", "2e10", "-90E3", "3e+7", "+6e-1", "53.5e93", "-123.456e789" },
       B{ "abc", "1a", "1e", "e3", "99e2.5", "--6", "-+3", "95a54e53", "e.7e5" };
    for (auto& s: A) assert( solution.isNumber(s));
    for (auto& s: B) assert(!solution.isNumber(s));
    return 0;
}


//
//
// MIT License
//
// Copyright (c) 2017 Stellacore Corporation.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject
// to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
// KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//

/*! \file
\brief  This file contains main application program demoGraph
*/

#include "libdat/info.h"
#include "libio/stream.h"

#include <boost/graph/adjacency_list.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/graph/connected_components.hpp>
#include <boost/graph/prim_minimum_spanning_tree.hpp>

#include <boost/graph/visitors.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>

#include <algorithm>
#include <cassert>
#include <iostream>
#include <string>

namespace
{
}

//! Demonstrate simple operations with boost graph library (BGL)
int
main
	( int const // argc
	, char const * const * const // argv
	)
{
	// type of graph structure to use
	using TypeVNdx = boost::vertex_index_t;
	using TypeWgt = double;
	using TypeGraph = boost::adjacency_list
		< boost::setS // EdgeStore
		, boost::setS // VertexStore
		, boost::undirectedS
		, boost::no_property // NodeProp
		, boost::no_property // EdgeProp -- handled explicitly below
		>;
	using TypeEDes = TypeGraph::edge_descriptor;
	using TypeVDes = TypeGraph::vertex_descriptor;
	using TypeVSize = boost::graph_traits<TypeGraph>::vertices_size_type;

//	io::out() << "TypeVNdx: " << dat::nameOfType(TypeVNdx()) << std::endl;
//	io::out() << "TypeVSize: " << dat::nameOfType(TypeVSize()) << std::endl;

	// construct graph with specified number of vertices
	TypeGraph graph(10u);
	TypeVSize const numVerts{ boost::num_vertices(graph) };

	// display initial value vertices
	for (TypeGraph::vertex_iterator
		itV{vertices(graph).first} ; vertices(graph).second != itV ; ++itV)
	{
		TypeGraph::vertex_descriptor const & subV = *itV;
		io::out() << "ctor vDesc: " << subV << std::endl;
	}

	// get vertex descriptors vDesc[vNdx] ...
	std::vector<TypeGraph::vertex_descriptor> vSubs(numVerts);
	std::copy
		( vertices(graph).first, vertices(graph).second
		, std::inserter(vSubs, vSubs.begin())
		);
	// ... and use to add a few edges
	//
	boost::add_edge(vSubs[0], vSubs[1], graph);
	boost::add_edge(vSubs[1], vSubs[2], graph);
	boost::add_edge(vSubs[2], vSubs[3], graph);
	//
	boost::add_edge(vSubs[2], vSubs[4], graph);
	boost::add_edge(vSubs[4], vSubs[5], graph);
	boost::add_edge(vSubs[5], vSubs[6], graph);
	boost::add_edge(vSubs[6], vSubs[7], graph);
	//
	boost::add_edge(vSubs[7], vSubs[1], graph);
	boost::add_edge(vSubs[7], vSubs[4], graph);

	// create a vNdx[vDesc] map - for reverse lookup ...
	std::map<TypeVDes, TypeVSize> vSizeForDesData;
	boost::associative_property_map<std::map<TypeVDes, TypeVSize>>
		vSizeForDes(vSizeForDesData);
	// ... and fill it
	for (size_t vNdx{0u} ; vNdx < numVerts ; ++vNdx)
	{
		vSizeForDes[boost::vertex(vNdx, graph)] = vNdx;
	}

	// determine connected components
	boost::connected_components
		(graph, vSizeForDes, boost::vertex_index_map(vSizeForDes));

	// report connected components
	std::for_each
		( vSizeForDesData.begin(), vSizeForDesData.end()
		, []
			(std::pair<TypeVDes, TypeVSize> const & compId)
			{ io::out() << "..." << compId.second << std::endl; }
		);

	// Compute minimum spanning tree

	// - Input data stuctures
	std::map<TypeEDes, TypeWgt> wgtMapData;
	boost::associative_property_map<std::map<TypeEDes, TypeWgt> >
		wgtMap(wgtMapData);
	double wgt{ 1. };
	for (TypeGraph::edge_iterator itE{boost::edges(graph).first}
			; boost::edges(graph).second != itE ; ++itE)
	{
		wgtMapData[*itE] = 1.; // wgt;
		io::out() << "adding wgtMapData: " << wgtMapData[*itE] << std::endl;
		wgt += .1;
	}

	std::map<TypeVDes, TypeVNdx> vNdxForDesData;
	boost::associative_property_map<std::map<TypeVDes, TypeVNdx> >
		vNdxForDes(vNdxForDesData);

	// What is TypeVNdx struct to do with vertex sequences
	size_t ndx{ 0u };
	for (TypeGraph::vertex_iterator itV{boost::vertices(graph).first}
		; boost::vertices(graph).second != itV ; ++itV)
	{
		vNdxForDes[vSubs[ndx]] = TypeVNdx(ndx);
		++ndx;
	}

	// - Output data structures
	std::map<TypeVDes, TypeVDes> fromIntoData;
	boost::associative_property_map<std::map<TypeVDes, TypeVDes> >
		fromInto(fromIntoData);

	std::map<TypeVDes, TypeWgt> fromDistData;
	boost::associative_property_map<std::map<TypeVDes, TypeWgt> >
		fromDist(fromDistData);

	// compute MST
	prim_minimum_spanning_tree
		( graph
		// IN: vertex_descriptor - s
// TODO - crashes if not vertex associated with first edge added?
//        ? unless edge weights are all equal?
		, vSubs[4]
		// OUT: PredecessorMap - predecessor
		, fromInto
		// OUT: DistanceMap - distance
		, fromDist
		// IN: WeightMap - weight
		, wgtMap
		// IN: IndexMap - index_map
		, vNdxForDes
		// DijkstraVisitor - vis
		, boost::dijkstra_visitor<boost::null_visitor>()
		);

	for (auto const & fromIntoItem : fromIntoData)
	{
		TypeVDes const & currVert = fromIntoItem.second;
		TypeVDes const & nextVert = fromIntoItem.first;
		TypeVNdx const & currNdx = vNdxForDesData[currVert];
		TypeVNdx const & nextNdx = vNdxForDesData[nextVert];
		TypeWgt const distPredCurr{ fromDistData[nextVert] };

		io::out()
			<< "fromIntoItem: " << fromIntoItem.second
			<< " ("
			<< " " << currNdx
			<< " " << "->"
			<< " " << nextNdx
			<< " }"
			<< " " << distPredCurr
			<< std::endl;
	}
	for (auto const & fromDistItem : fromDistData)
	{
		io::out()
			<< "fromDistItem: " << fromDistItem.second
			<< "  node: " << vNdxForDesData[fromDistItem.first]
			<< std::endl;
	}

	return 0;
}


/*
 * Copyright (c) 2017, The Regents of the University of California (Regents).
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *
 *    3. Neither the name of the copyright holder nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Please contact the author(s) of this library if you have any questions.
 * Authors: David Fridovich-Keil   ( dfk@eecs.berkeley.edu )
 */

///////////////////////////////////////////////////////////////////////////////
//
// Defines the GaussianProcess class.
//
///////////////////////////////////////////////////////////////////////////////

#ifndef GP_PROCESS_GAUSSIAN_PROCESS_H
#define GP_PROCESS_GAUSSIAN_PROCESS_H

#include "../kernels/kernel.hpp"
#include "../utils/types.hpp"

#include <Eigen/Cholesky>
#include <glog/logging.h>
#include <vector>

namespace gp {

  class GaussianProcess {
  public:
    ~GaussianProcess() {}

    // Constructors. By default picks 10% of the maximum number of points
    // randomly within the unit box [-1, 1]^d.
    explicit GaussianProcess(const Kernel::Ptr& kernel, double noise,
                             size_t dimension, size_t max_points = 100);
    explicit GaussianProcess(const Kernel::Ptr& kernel, double noise,
                             const PointSet& points,
                             size_t max_points = 100);
    explicit GaussianProcess(const Kernel::Ptr& kernel, double noise,
                             const PointSet& points,
                             const VectorXd& targets,
                             size_t max_points = 100);

    // Evaluate mean and variance at a point.
    void Evaluate(const VectorXd& x, double& mean, double& variance) const;
    void EvaluateTrainingPoint(size_t ii, double& mean, double& variance) const;

    // Add new point(s). Returns whether or not points were added (points will
    // only be added until 'max_points' is reached).
    bool Add(const VectorXd& x, double target);
    bool Add(const std::vector<VectorXd>& points, const VectorXd& targets);

    // Update the training targets in the direction of the gradient of the
    // mean squared error at the given points. Returns the mean squared error.
    // If 'finalize' is set, computes regressed targets - only set to false if
    // you are doing repeated updates, and be sure to set true on final update.
    double UpdateTargets(const std::vector<VectorXd>& points,
                         const std::vector<double>& targets,
                         double step_size, bool finalize = true);

    // Learn kernel hyperparameters by maximizing log-likelihood of the
    // training data.
    bool LearnHyperparams();

    // Immutable accessors.
    const MatrixXd& ImmutableCovariance() const { return covariance_; }
    const VectorXd& ImmutableRegressedTargets() const { return regressed_; }
    const VectorXd& ImmutableTargets() const { return targets_; }
    const ConstPointSet ImmutablePoints() const { return points_; }
    const Eigen::LLT<MatrixXd>& ImmutableCholesky() const { return llt_; }
    size_t Dimension() const { return dimension_; }

  private:
    // Compute the covariance and cross covariance against the training points.
    void Covariance();
    void CrossCovariance(const VectorXd& x, VectorXd& cross) const;

    // Kernel.
    const Kernel::Ptr kernel_;

    // Noise variance.
    const double noise_;

    // Training points, targets, and regressed targets (inv(cov) * targets).
    const PointSet points_;
    size_t dimension_;
    VectorXd targets_;
    VectorXd regressed_;

    // Maximum number of points.
    const size_t max_points_;

    // Covariance matrix, with Cholesky decomposition.
    MatrixXd covariance_;
    Eigen::LLT<MatrixXd> llt_;
  }; //\class GaussianProcess

}  //\namespace gp

#endif


#include <bg/bondgeek.hpp>

#include <iostream>
#include <algorithm>

#include <boost/timer.hpp>

using namespace std;
using namespace QuantLib;
using namespace bondgeek;

#if defined(QL_ENABLE_SESSIONS)
namespace QuantLib {
    Integer sessionId() { return 0; }
}
#endif

/* TODO: Instrument interface
 - swaps: euro swaps, usd
 - bonds:  UST, corp, tax-exempt munis
 */
int main ()
{
    cout << "QuantLib Version #: " << QL_VERSION << endl ;

    Calendar calendar = TARGET();
//    Date todaysDate = TARGET().adjust( Date(20, September, 2004) );
    Date todaysDate = TARGET().adjust( Date(28, May, 2012) );

    Settings::instance().evaluationDate() = todaysDate;
    cout << "settings: " << &Settings::instance() << endl;
    cout << "\n\nToday: " << todaysDate << endl;

    string futtenors[] = {"ED1", "ED2", "ED3", "ED4", "ED5", "ED6", "ED7", "ED8"};
    double futspots[] = {96.2875, 96.7875, 96.9875, 96.6875, 96.4875, 96.3875, 96.2875, 96.0875};

    string depotenors[] = {"1W", "1M", "3M", "6M", "9M", "1y"};
    double depospots[] = {.0382, 0.0372, 0.0363, 0.0353, 0.0348, 0.0345};
    string swaptenors[] = {"2y", "3y", "5y", "10Y", "15Y"};
    double swapspots[] = {0.037125, 0.0398, 0.0443, 0.05165, 0.055175};

    cout << "test sc1" << endl;
    RateHelperCurve acurve = RateHelperCurve(EURiborCurve("6M"));
    acurve.update(depotenors, depospots, 6,
                  swaptenors, swapspots, 5,
                  todaysDate);

    cout << "quote:  " << acurve.tenorquote("10Y") << endl;

    cout << "test tenors\n" << Period(6, Months) << " | " << Tenor("6M") << endl;
    cout << "fixing calendar: " << acurve.calendar() << endl;

    /*********************
     * SWAPS TO BE PRICED *
     **********************/

    RelinkableHandle<YieldTermStructure>forecastingTermStructure = acurve.forecastingTermStructure();

    // constant nominal 1,000,000 Euro
    Real nominal = 1000000.0;
    // fixed leg
    Frequency fixedLegFrequency = Annual;
    BusinessDayConvention fixedLegConvention = Unadjusted;
    BusinessDayConvention floatingLegConvention = ModifiedFollowing;
    DayCounter fixedLegDayCounter = Thirty360(Thirty360::European);
    Rate fixedRate = 0.04;
    DayCounter floatingLegDayCounter = Actual360();

    // floating leg
    Frequency floatingLegFrequency = Semiannual;

    boost::shared_ptr<IborIndex> euriborIndex(new Euribor6M(forecastingTermStructure));

    Spread spread = 0.0;

    Integer lenghtInYears = 5;
    VanillaSwap::Type swapType = VanillaSwap::Payer;

    Date settlementDate = acurve.referenceDate();
    Date maturity = acurve.referenceDate() + lenghtInYears*Years;
    Schedule fixedSchedule(settlementDate, maturity,
                           Period(fixedLegFrequency),
                           calendar, fixedLegConvention,
                           fixedLegConvention,
                           DateGeneration::Forward, false);
    Schedule floatSchedule(settlementDate, maturity,
                           Period(floatingLegFrequency),
                           calendar, floatingLegConvention,
                           floatingLegConvention,
                           DateGeneration::Forward, false);
    VanillaSwap spot5YearSwap(swapType, nominal,
                              fixedSchedule, fixedRate, fixedLegDayCounter,
                              floatSchedule, euriborIndex, spread,
                              floatingLegDayCounter);

    Date fwdStart = calendar.advance(settlementDate, 1, Years);
    Date fwdMaturity = fwdStart + lenghtInYears*Years;
    Schedule fwdFixedSchedule(fwdStart, fwdMaturity,
                              Period(fixedLegFrequency),
                              calendar, fixedLegConvention,
                              fixedLegConvention,
                              DateGeneration::Forward, false);
    Schedule fwdFloatSchedule(fwdStart, fwdMaturity,
                              Period(floatingLegFrequency),
                              calendar, floatingLegConvention,
                              floatingLegConvention,
                              DateGeneration::Forward, false);
    VanillaSwap oneYearForward5YearSwap(swapType, nominal,
                                        fwdFixedSchedule, fixedRate, fixedLegDayCounter,
                                        fwdFloatSchedule, euriborIndex, spread,
                                        floatingLegDayCounter);

    cout << "swap." << endl;

    boost::shared_ptr<PricingEngine> swapEngine = createPriceEngine<DiscountingSwapEngine>(
                                                                                acurve.discountingTermStructure()
                                                                                );

    spot5YearSwap.setPricingEngine(swapEngine);
    oneYearForward5YearSwap.setPricingEngine(swapEngine);

    Real NPV;
    Rate fairRate;
    Spread fairSpread;

    cout << "spot " << endl;
    NPV = spot5YearSwap.NPV();
    fairSpread = spot5YearSwap.fairSpread();
    fairRate = spot5YearSwap.fairRate();

    cout << std::setprecision(2) << std::setw(12) << std::fixed <<
    "NPV : " << NPV <<
    "   | Fair Spread: " << io::rate(fairSpread) <<
    "   | Fair Rate: " << io::rate(fairRate) << endl;

    cout << std::setprecision(2) << std::setw(12) << std::fixed <<
    "fx NPV : " << spot5YearSwap.fixedLegNPV() <<
    "   | fx NPV : " << spot5YearSwap.floatingLegNPV() << endl;

    cout << "forward " << endl;
    NPV = oneYearForward5YearSwap.NPV();
    fairSpread = oneYearForward5YearSwap.fairSpread();
    fairRate = oneYearForward5YearSwap.fairRate();

    cout << std::setprecision(2) << std::setw(12) << std::fixed <<
    "NPV : " << NPV <<
    "   | Fair Spread: " << io::rate(fairSpread) <<
    "   | Fair Rate: " << io::rate(fairRate) << endl;

    cout << std::setprecision(2) << std::setw(12) << std::fixed <<
    "fx NPV : " << oneYearForward5YearSwap.fixedLegNPV() <<
    "   | fx NPV : " << oneYearForward5YearSwap.floatingLegNPV() << endl;

    cout << "test libor clone" << endl;
    RelinkableHandle<YieldTermStructure> indexTermStructure;
    boost::shared_ptr<IborIndex> libor3m(new USDLibor(Period(3, Months),
                                                      indexTermStructure));

    Handle<YieldTermStructure>testTS = acurve.forecastingTermStructure();
    boost::shared_ptr<IborIndex> newlib = libor3m->clone(testTS);

    // If one wanted a USD Libor index....
    USDLiborBase testIndex;

    cout << "\n\nSwap to compare to first swap " << endl;
    cout << "Qswap" << endl;
    cout << "mty: " << maturity << " | cpn: " << io::rate(fixedRate) << endl;
    EuriborBase euribor(6, Months);

    FixedFloatSwap qswp(settlementDate,
                        maturity,
                        fixedRate,
                        euribor(acurve.yieldTermStructurePtr()),
                        FixedPayer,
                        0.0,
                        1000000.0,
                        Annual,
                        Thirty360(Thirty360::European),
                        Unadjusted,
                        Semiannual,
                        Actual360(),
                        ModifiedFollowing,
                        TARGET()
                        );

    qswp.setPricingEngine(swapEngine);

    cout << std::setprecision(2) << std::setw(12) << std::fixed <<
    "NPV : " << qswp.NPV() <<
    "   | Fair Spread: " << io::rate(qswp.fairSpread()) <<
    "   | Fair Rate: " << io::rate(qswp.fairRate()) << endl;

    cout << std::setprecision(2) << std::setw(12) << std::fixed <<
    "fx NPV : " << qswp.fixedLegNPV() <<
    "   | fl NPV : " << qswp.floatingLegNPV() << endl;

    cout << "Inspect Legs" << endl << endl;
    Leg fixedLeg = qswp.fixedLeg();
    Leg floatingLeg = qswp.floatingLeg();

    cout << "Fixed: " << endl;
    Leg::iterator fxIt;
    int cfCount =0;
    Date cfDate;
    double cfAmt;
    double cfDF;
    double cfNPV = 0.0;
    for (fxIt=fixedLeg.begin(); fxIt < fixedLeg.end(); fxIt++) {
        cfDate = (*fxIt)->date();
        cfAmt = (*fxIt)->amount();
        cfDF = acurve.discount((*fxIt)->date());
        cfNPV += cfAmt*cfDF;

        cout << cfCount++ << ") "
        << std::setw(24) << cfDate << " | "  << std::setw(12)
        << cfAmt << " | "
        << std::setprecision(6) << cfDF << " | "
        << std::setprecision(2) << cfNPV
        << endl;
    }

    cout << "Floating: " << endl;
    Leg::iterator flIt;
    cfCount =0;
    cfNPV = 0.0;
    for (flIt=floatingLeg.begin(); flIt < floatingLeg.end(); flIt++) {
        cfDate = (*flIt)->date();
        cfAmt = (*flIt)->amount();
        cfDF = acurve.discount((*flIt)->date());
        cfNPV += cfAmt*cfDF;

        cout << cfCount++ << ") "
        << std::setw(24) << cfDate << " | "  << std::setw(12)
        << cfAmt << " | "
        << std::setprecision(6) << cfDF << " | "
        << std::setprecision(2) << cfNPV
        << endl;
    }

    cout << "swp2 " << endl;

    SwapType<Euribor> euriborswaps(Annual,
                                   Thirty360(Thirty360::European),
                                   Unadjusted,
                                   Semiannual,
                                   Actual360(),
                                   ModifiedFollowing,
                                   TARGET()
                                   );

    cout << "create" << endl;
    boost::shared_ptr<FixedFloatSwap> qswp2 = euriborswaps.create(settlementDate,
                                                                  maturity,
                                                                  fixedRate);

    cout << "pricing" << endl;
    qswp2->setEngine(acurve);

    cout << "link" << endl;
    euriborswaps.linkIndex(acurve);

    cout << std::setprecision(2) << std::setw(12) << std::fixed <<
    "NPV : " << qswp2->NPV() <<
    "   | Fair Spread: " << io::rate(qswp2->fairSpread()) <<
    "   | Fair Rate: " << io::rate(qswp2->fairRate()) << endl;

    cout << std::setprecision(2) << std::setw(12) << std::fixed <<
    "fx NPV : " << qswp2->fixedLegNPV() <<
    "   | fx NPV : " << qswp2->floatingLegNPV() << endl;

    cout << "\nSwap 3: forward" << endl;
    boost::shared_ptr<FixedFloatSwap> qswp3 = euriborswaps.create(fwdStart,
                                                                  fwdMaturity,
                                                                  fixedRate);
    cout << "fixed rate: " << qswp3->fixedRate() << endl;
    qswp3->setPricingEngine(swapEngine);
    euriborswaps.linkIndexTo(acurve.yieldTermStructurePtr());

    cout << std::setprecision(2) << std::setw(12) << std::fixed <<
    "NPV : " << qswp3->NPV() <<
    "   | Fair Spread: " << io::rate(qswp3->fairSpread()) <<
    "   | Fair Rate: " << io::rate(qswp3->fairRate()) << endl;

    cout << std::setprecision(2) << std::setw(12) << std::fixed <<
    "fx NPV : " << qswp3->fixedLegNPV() <<
    "   | fx NPV : " << qswp3->floatingLegNPV() << endl;

    cout << "\n\nBonds" << endl;

    boost::shared_ptr<BulletBond> bond1(new BulletBond(.045,
                                                       Date(15, May, 2017),
                                                       Date(15, May, 2003))
                                        );

    cout << "mty: " << bond1->maturityDate() << endl;
    cout << "stl: " << bond1->settlementDate() << endl;

    cout << "test" << endl;
    bond1->setEngine(acurve);

    cout << "bondprice: " ;
    double prc = bond1->cleanPrice();
    cout << std::setprecision(3) << prc << endl;

    cout << "Yield: " ;
    double yld = bond1->yield(prc, bond1->dayCounter(), Compounded, bond1->frequency());
    cout << io::rate(yld) << endl;

    cout << "\n\nIMM Stuff\n";
    cout << "settle: " << settlementDate << endl;
    Date  imm = IMM::nextDate(settlementDate);
    string immcode = IMM::code(imm);

    Date imm2 = imm_nextDate(imm);
    string immcode2 = imm_nextCode(immcode);

    cout << "date: " << imm << " | code: " << immcode << endl;
    cout << "date: " << imm2 << " | code: " << immcode2 << endl;

    cout << "ED3: " << FuturesTenor("ED3") << endl;

    CurveMap depocurve;
    CurveMap futscurve;
    CurveMap swapcurve;
    RateHelperCurve rhcurve( EURiborCurve("6M", Annual) );

    if (futscurve.empty()) {
        cout << "futscurve empty " << endl;
    } else {
        cout << "futscurve not empty: " << endl;
    }

    for (int i=0; i<2; i++)
        depocurve[depotenors[i]] = depospots[i];

    for (int i=0; i<6; i++)
        futscurve[futtenors[i]] = futspots[i];

    for (int i=0; i<5; i++)
        swapcurve[swaptenors[i]] = swapspots[i];

    rhcurve.update(depocurve, futscurve, swapcurve);

    cout << "\n10Y: " << io::rate(rhcurve.tenorquote("10Y")) << endl;
    cout << "discount: " << rhcurve.discount(10.0) << endl;
    return 0;
}


#include <iostream>
#include <NTL/ZZ.h>
#include <gmp.h>
#include <gmpxx.h>
#include "../bernmm-1.1/bern_modp_util.h"
#include "../bernmm-1.1/bern_modp.h"
#include "../bernmm-1.1/bern_rat.h"
#include "berrb.h"

Berrb::Berrb()
{

}

Berrb::~Berrb()
{

}

std::string Berrb::ber(long k, long thread)
{
	mpq_t r;
	mpq_init(r);
	bernmm::bern_rat(r, k, thread);

    mpq_class q(r);

	mpq_clear(r);

    return q.get_str();
}

long Berrb::ber_modp(long p, long k)
{
    long r = bernmm::bern_modp(p, k);

    return r;
}


#include "sparse.h"

#define USE_EIGEN
//#define USE_SUITESPARSE

#ifdef USE_EIGEN
#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET
//#define EIGEN_CHOLMOD_SUPPORT
//#define EIGEN_UMFPACK_SUPPORT
#include <Eigen/Cholesky>
#include <Eigen/LU>
#include <Eigen/Sparse>
#include <Eigen/SparseExtra>
using namespace Eigen;
#endif  // USE_EIGEN

#ifdef USE_SUITESPARSE
#include <suitesparse/SuiteSparseQR.hpp>
#endif // USE_SUITESPARSE

namespace LinAlg
{

//----( sparse solvers )------------------------------------------------------

#ifdef USE_EIGEN

// conversions between k nearest neighbor & Eigen sparse matrix formats

void knn_to_eigen (
    const size_t dim,
    const size_t degree,
    const Vector<uint16_t> & nbhd,
    const Vector<float> & knn_A,
    SparseMatrix<double> & eigen_A)
{
  DynamicSparseMatrix<double> A(dim,dim);

  for (size_t i = 0; i < dim; ++i) {
    for (size_t n = 0; n < degree; ++n) {
      size_t knn_pos = degree * i + n;
      size_t j = nbhd[knn_pos];

      A.coeffRef(i,j) = knn_A[knn_pos];
    }
  }

  eigen_A = A;
}

void symmetric_knn_to_eigen (
    const size_t dim,
    const size_t degree,
    const Vector<uint16_t> & nbhd,
    const Vector<float> & knn_A,
    SparseMatrix<double> & eigen_A)
{
  DynamicSparseMatrix<double> A(dim,dim);

  for (size_t i = 0; i < dim; ++i) {
    for (size_t n = 0; n < degree; ++n) {
      size_t knn_pos = degree * i + n;
      size_t j = nbhd[knn_pos];

      A.coeffRef(i,j) = A.coeffRef(j,i) = knn_A[knn_pos];
    }
  }

  eigen_A = A;
}

void eigen_to_knn (
    const size_t dim,
    const size_t degree,
    const Vector<uint16_t> & nbhd,
    const SparseMatrix<double> & eigen_A,
    Vector<float> & knn_A)
{
  for (size_t i = 0; i < dim; ++i) {
    for (size_t n = 0; n < degree; ++n) {
      size_t knn_pos = degree * i + n;
      size_t j = nbhd[knn_pos];

      knn_A[knn_pos] = eigen_A.coeff(i,j);
    }
  }
}

void sparse_symmetric_solve (
    const size_t dim,
    const size_t degree,
    const Vector<uint16_t> & nbhd,
    const Vector<float> & knn_Pxx,
    const Vector<float> & knn_Pxy,
    Vector<float> & knn_Fxy,
    double tol,
    bool debug)
{
  ASSERT_SIZE(nbhd, dim * degree);
  ASSERT_SIZE(knn_Pxx, dim * degree);
  ASSERT_SIZE(knn_Pxy, dim * degree);
  ASSERT_SIZE(knn_Fxy, dim * degree);

  LOG("solving sparse " << dim << " x " << dim << " matrix problem");
  SparseMatrix<double> Pxx;
  symmetric_knn_to_eigen(dim, degree, nbhd, knn_Pxx, Pxx);

  //typedef SparseLLT<SparseMatrix<double>, Cholmod> Solver;
  typedef SparseLLT<SparseMatrix<double> > Solver;
  Solver solver(Pxx);

  VectorXd Fxy_i(dim);
  VectorXd Pxy_i(dim);
  float residual = 0;

  for (size_t i = 0; i < dim; ++i) {

    Pxy_i.setZero();
    for (size_t n = 0; n < degree; ++n) {
      size_t knn_pos = degree * i + n;
      Pxy_i(nbhd[knn_pos]) = knn_Pxy[knn_pos];
    }

    Fxy_i = Pxy_i;
    solver.solveInPlace(Fxy_i);
    // eigen does not set the succeeded flag, otherwise we could check this
    //ASSERT(solver.succeeded(), "sparse linear solver failed");

    for (size_t n = 0; n < degree; ++n) {
      size_t knn_pos = degree * i + n;
      knn_Fxy[knn_pos] = Fxy_i(nbhd[knn_pos]);
    }

    if (debug) {
      Pxy_i -= Pxx * Fxy_i;
      residual += Pxy_i.norm();
    }
  }

  if (debug) {

    LOG(" 2-norm of dense residual = " << residual);

    SparseMatrix<double> Pxy, Fxy;
    knn_to_eigen(dim, degree, nbhd, knn_Pxy, Pxy);
    knn_to_eigen(dim, degree, nbhd, knn_Fxy, Fxy);

    Pxy -= Pxx * Fxy;
    LOG(" 2-norm of sparse residual = " << Pxy.norm());
  }
}

#endif // USE_EIGEN

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

#ifdef USE_SUITESPARSE

// This uses the GLP's SuiteSparseQR function
// http://www.cise.ufl.edu/research/sparse/SPQR/SPQR/Doc/spqr_user_guide.pdf

// conversions between k nearest neighbor & cholmod formats for sparse matrices

static cholmod_sparse * knn_to_cholmod (
    const size_t dim,
    const size_t degree,
    const Vector<uint16_t> & nbhd,
    const Vector<float> & A,
    cholmod_common * cc)
{
  TODO("implement conversion");
}

static cholmod_sparse * symmetric_knn_to_cholmod (
    const size_t dim,
    const size_t degree,
    const Vector<uint16_t> & nbhd,
    const Vector<float> & A,
    cholmod_common * cc)
{
  TODO("implement conversion");
}

static void cholmod_to_knn (
    const size_t dim,
    const size_t degree,
    const Vector<uint16_t> & nbhd,
    const cholmod_sparse * a,
    Vector<float> & A,
    cholmod_common * cc)
{
  TODO("implement conversion");
}

void sparse_symmetric_solve (
    const size_t dim,
    const size_t degree,
    const Vector<uint16_t> & nbhd,
    const Vector<float> & Pxx,
    const Vector<float> & Pxy,
    Vector<float> & Fxy,
    double tol,
    bool debug)
{
  ASSERT_SIZE(Pxx, dim * degree);
  ASSERT_SIZE(Pxy, dim * degree);
  ASSERT_SIZE(Fxy, dim * degree);

  TODO("implement sparse symmetric linear solver");

  LOG("solving sparse " << dim << " x " << dim << " matrix problem");

  // start CHOLMOD
  cholmod_common Common, *cc = &Common;
  cholmod_l_start(cc);

  cholmod_sparse * A = symmetric_knn_to_cholmod(dim, degree, nbhd, Pxx, cc);
  cholmod_sparse * B = knn_to_cholmod(dim, degree, nbhd, Pxy, cc);

  // matlab equivalent: X = A\B
  int ordering = CHOLMOD_NATURAL;
  cholmod_sparse * X = SuiteSparseQR<double>(ordering, tol, A, B, cc);

  cholmod_to_knn(dim, degree, nbhd, X, Fxy, cc);

  if (debug) {
    cholmod_sparse * B2 = cholmod_l_ssmult(A, B, 0, true, true, cc);

    Vector<float> Pxy2(Pxy.size);
    cholmod_to_knn(dim, degree, nbhd, B2, Pxy2, cc);

    cholmod_l_free_sparse(&B2, cc);

    LOG(" 2-norm of residual = " << dist_squared(Pxy, Pxy2));
  }

  // free everything and finish CHOLMOD
  cholmod_l_free_sparse(&A, cc);
  cholmod_l_free_sparse(&B, cc);
  cholmod_l_free_sparse(&X, cc);
  cholmod_l_finish(cc);
}

#endif // USE_SUITESPARSE

} // namespace LinAlg


#include <deal.II/base/utilities.h>
#include <deal.II/base/quadrature_lib.h>
#include <deal.II/base/function.h>
#include <deal.II/base/logstream.h>
#include <deal.II/base/timer.h>
#include <deal.II/base/conditional_ostream.h>
#include <deal.II/base/index_set.h>
#include <deal.II/base/parameter_handler.h>

#include <deal.II/lac/generic_linear_algebra.h>
namespace LA
{
    using namespace dealii::LinearAlgebraPETSc;
#  define USE_PETSC_LA
}

#include <deal.II/lac/vector.h>
#include <deal.II/lac/full_matrix.h>
#include <deal.II/lac/dynamic_sparsity_pattern.h>
//#include <deal.II/lac/sparse_matrix.h>
#include <deal.II/lac/solver_cg.h>
#include <deal.II/lac/precondition.h>
#include <deal.II/lac/constraint_matrix.h>
#include <deal.II/lac/sparsity_tools.h>

//#include <deal.II/grid/tria.h>
#include <deal.II/grid/grid_generator.h>
#include <deal.II/grid/grid_refinement.h>
#include <deal.II/grid/grid_out.h>
#include <deal.II/grid/tria_accessor.h>
#include <deal.II/grid/tria_iterator.h>

#include <deal.II/dofs/dof_handler.h>
#include <deal.II/dofs/dof_accessor.h>
#include <deal.II/dofs/dof_tools.h>

#include <deal.II/fe/fe_q.h>
#include <deal.II/fe/fe_values.h>

#include <deal.II/numerics/data_out.h>
#include <deal.II/numerics/vector_tools.h>
#include <deal.II/numerics/error_estimator.h>
//#include <deal.II/numerics/solution_transfer.h>
//#include <deal.II/numerics/matrix_tools.h>

#include <deal.II/lac/petsc_parallel_sparse_matrix.h>
#include <deal.II/lac/petsc_parallel_vector.h>
#include <deal.II/lac/petsc_solver.h>
#include <deal.II/lac/petsc_precondition.h>

#include <deal.II/distributed/tria.h>
#include <deal.II/distributed/grid_refinement.h>
#include <deal.II/grid/filtered_iterator.h>

#include <fstream>
#include <iostream>

#include "my_utility_functions.h"
#include "parameters.h"

namespace CPPLS
{
	using namespace dealii;

	template <int dim>
	class LayerMovementProblem
	{
	public:
		LayerMovementProblem (const CPPLS::Parameters &parameters);
		~LayerMovementProblem ();
		void run();

	private:
	//Member Data
		//runtime parameters
		const CPPLS::Parameters parameters;
		//mpi communication
  		MPI_Comm mpi_communicator;
		const unsigned int n_mpi_processes;
		const unsigned int this_mpi_process;
		//mesh
		parallel::distributed::Triangulation<dim> triangulation;
		//FE basis spaces
		//pressure
		int                  degree_P;
	  	DoFHandler<dim>      dof_handler_P;
	  	FE_Q<dim>            fe_P;
	  	IndexSet             locally_owned_dofs_P;
	  	IndexSet             locally_relevant_dofs_P;
	  	//temperature
	  	int                  degree_T;
	  	DoFHandler<dim>      dof_handler_T;
	  	FE_Q<dim>            fe_T;
	  	IndexSet             locally_owned_dofs_T;
	  	IndexSet             locally_relevant_dofs_T;
	  	// level set (can multiple level sets use the same of below? probably not IndexSets)std::vector<IndexSets>
	  	int                  degree_LS;
	  	DoFHandler<dim>      dof_handler_LS;
	  	FE_Q<dim>            fe_LS;
	  	IndexSet             locally_owned_dofs_LS;
	  	IndexSet             locally_relevant_dofs_LS;

	  	//output stream where only mpi rank 0 output gets to stdout
	  	ConditionalOStream                pcout;

		const double time_step;
  		double current_time;

  		//FE Field Solution Vectors

		  LA::MPI::Vector locally_relevant_solution_u; //ls
		  LA::MPI::Vector locally_relevant_solution_p;
		  LA::MPI::Vector locally_relevant_solution_t;
		  LA::MPI::Vector locally_relevant_solution_f; //speed function
		  LA::MPI::Vector completely_distributed_solution_u;
		  LA::MPI::Vector completely_distributed_solution_p;
		  LA::MPI::Vector completely_distributed_solution_t;
		  LA::MPI::Vector completely_distributed_solution_f;

		  LA::MPI::Vector overburden;
		  LA::MPI::Vector bulkdensity;
		  LA::MPI::Vector porosity;

  		// for boundary conditions
	  	std::vector<unsigned int> boundary_values_id_u;
	  	std::vector<unsigned int> boundary_values_id_p;
	  	std::vector<unsigned int> boundary_values_id_t;
	  	std::vector<double> boundary_values_u;
	  	std::vector<double> boundary_values_p;
	  	std::vector<double> boundary_values_t;

  		//Physical Vectors

  	//Member Functions

	  //create mesh
	  void setup_geometry();

	  void setup_material_configuration(LA::MPI::Vector/*std::vector<LA::MPI::Vector>*/);
	  // initialize vectors
	  void setup_dofs_P();
	  void setup_dofs_T();

	  void assemble_system_P();
	  void assemble_system_T();

	  void compute_bulkdensity();
	  void compute_overburden();
	  void compute_porosity();
	  void compute_speed_function();

	  void output_vectors();

	};

//Constructor

	template<int dim>
 	LayerMovementProblem<dim>::LayerMovementProblem (const CPPLS::Parameters &parameters)
 	:
	parameters(parameters),
	mpi_communicator (MPI_COMM_WORLD),
 	n_mpi_processes {Utilities::MPI::n_mpi_processes(mpi_communicator)},
 	this_mpi_process {Utilities::MPI::this_mpi_process(mpi_communicator)},
 	{};

//
template<int dim>
void
	LayerMovementProblem<dim>::setup_geometry()
	{
		  //GridGenerator::hyper_cube(triangulation, 0, parameters.box_size);
		  //GridGenerator::subdivided_hyper_rectangle(triangulation, 0, parameters.box_size);
   		  triangulation.refine_global(parameters.initial_refinement_level);

	}

template <int dim>
	void
	LayerMovementProblem<dim>::compute_bulkdensity()
	{
		for (auto cell : filter_iterators(triangulation.active_cell_iterators(),
													IteratorFilters::LocallyOwnedCell()))
		  {
		    fe_values.reinit (cell);
		    //query the local porosity

		  }
	}

template <int dim>
	void
	LayerMovementProblem<dim>::compute_overburden()
	{

	    const QGauss<dim>  quadrature_formula(3);

	    FEValues<dim> fe_values (fe, quadrature_formula,
	                             update_values    |  update_gradients |
	                             update_quadrature_points |
	                             update_JxW_values);

	    const unsigned int   dofs_per_cell = fe.dofs_per_cell;
	    const unsigned int   n_q_points    = quadrature_formula.size();

	    FullMatrix<double>   cell_matrix (dofs_per_cell, dofs_per_cell);
	    Vector<double>       cell_rhs (dofs_per_cell);

	    std::vector<types::global_dof_index> local_dof_indices (dofs_per_cell);
		for (auto cell : filter_iterators(triangulation.active_cell_iterators(),
													IteratorFilters::LocallyOwnedCell())
		  {
		    fe_values.reinit (cell);

		  }
	}

template <int dim>
	void LayerMovementProblem<dim>::assemble_system_P()
	{

    TimerOutput::Scope t(computing_timer, "assembly_P");
     const QGauss<dim>  quadrature_formula(3);

     // RightHandSide<dim> right_hand_side;
     // //set time too
     // right_hand_side.set_time(time);
     // DiffusionCoefficient<dim> diffusion_coeff;

    FEValues<dim> fe_values (fe_P, quadrature_formula,
                             update_values    |  update_gradients |
                             update_quadrature_points |
                             update_JxW_values);

    const unsigned int   dofs_per_cell = fe.dofs_per_cell;
    const unsigned int   n_q_points    = quadrature_formula.size();

    FullMatrix<double>   cell_laplace_matrix (dofs_per_cell, dofs_per_cell);
    FullMatrix<double>   cell_mass_matrix (dofs_per_cell, dofs_per_cell);
    Vector<double>       cell_rhs (dofs_per_cell);

    std::vector<types::global_dof_index> local_dof_indices (dofs_per_cell);

    std::vector<double> u_at_quad(n_q_points);

    // typename DoFHandler<dim>::active_cell_iterator
    // cell = dof_handler.begin_active(),
    // endc = dof_handler.end();
    // for (; cell!=endc; ++cell)
    //   if (cell->is_locally_owned())
    //     {

      for (auto cell : filter_iterators(dof_handler.active_cell_iterators(),
										IteratorFilters::LocallyOwnedCell())
  		{
          cell_laplace_matrix = 0;
          cell_mass_matrix = 0;
          cell_rhs = 0;

          fe_values.reinit (cell);
          fe_values.get_function_values(interface_phi,phi_at_quad);
          fe_values.get_function_values(locally_relevant_solution_p,pressure_at_quad);

          for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
            {
              poro = get_poro(phi_at_quad[q_point]);
	          press = get_pressure(pressure_at_quad[q_point]);
              poro = exp(-k*press);

            }

          cell->get_dof_indices (local_dof_indices);
          constraints.distribute_local_to_global (cell_laplace_matrix,
                                                  cell_rhs,
                                                  local_dof_indices,
                                                  laplace_matrix,
                                                  forcing);
          constraints.distribute_local_to_global (cell_mass_matrix,
                                                  local_dof_indices,
                                                  mass_matrix);

        }

    // Notice that the assembling above is just a local operation. So, to
    // form the "global" linear system, a synchronization between all
    // processors is needed. This could be done by invoking the function
    // compress(). See @ref GlossCompress  "Compressing distributed objects"
    // for more information on what is compress() designed to do.
    laplace_matrix.compress (VectorOperation::add);
    mass_matrix.compress (VectorOperation::add);
    forcing.compress (VectorOperation::add);

  }

	}

template <int dim>
void
LayerMovementProblem<dim>::output_vectors()
{
  DataOut<dim> data_out;
  data_out.attach_dof_handler (dof_handler_LS);
  data_out.add_data_vector (locally_relevant_solution_u, "u");
  data_out.build_patches ();

  const std::string filename = ("sol_vectors-" +
				Utilities::int_to_string (output_number, 3) +
				"." +
				Utilities::int_to_string
				(triangulation.locally_owned_subdomain(), 4));
  std::ofstream output ((filename + ".vtu").c_str());
  data_out.write_vtu (output);

  if (Utilities::MPI::this_mpi_process(mpi_communicator) == 0)
    {
      std::vector<std::string> filenames;
      for (unsigned int i=0;
	   i<Utilities::MPI::n_mpi_processes(mpi_communicator);
	   ++i)
	filenames.push_back ("sol_vectors-" +
			     Utilities::int_to_string (output_number, 3) +
			     "." +
			     Utilities::int_to_string (i, 4) +
			     ".vtu");

      std::ofstream master_output ((filename + ".pvtu").c_str());
      data_out.write_pvtu_record (master_output, filenames);
    }
}

template<int dim>
	LayerMovementProblem<dim>::run()
	{
		//common mesh
		setup_geometry();
		//
		//initialize level set solver
		LevelSetSolver<dim> level_set_solver;

		//initialize pressure solver
		PressureEquation<dim> pressure_solver;
		//initialize temperature solver
		//TemperatureEquation<dim> temperature_solver;

  // TIME STEPPING
  for (timestep_number=1, time=time_step; time<=final_time;
       time+=time_step,++timestep_number)
    {
      pcout << "Time step " << timestep_number
	    << " at t=" << time
	    << std::endl;
      // GET NAVIER STOKES VELOCITY
      navier_stokes.set_phi(locally_relevant_solution_phi);
      navier_stokes.nth_time_step();
      navier_stokes.get_velocity(locally_relevant_solution_u,locally_relevant_solution_v);
      transport_solver.set_velocity(locally_relevant_solution_u,locally_relevant_solution_v);
      // GET LEVEL SET SOLUTION
      transport_solver.nth_time_step();
      transport_solver.get_unp1(locally_relevant_solution_phi);
      if (get_output && time-(output_number)*output_time>0)
	output_results();
    }

	}

} //end namespace CPPLS

constexpr int dim {3};

int main(int argc, char *argv[])
{
  // One of the new features in C++11 is the chrono component of
  // the standard library. This gives us an easy way to time the output.
  try
    {
	    using namespace dealii;
	    using namespace CPPLS;

	    auto t0 = std::chrono::high_resolution_clock::now();

	  	Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv, 1);

	  	CPPLS::Parameters parameters;
	  	parameters.read_parameter_file("parameters.prm");

	  	LayerMovementProblem<dim> run_layers(parameters);
	  	run_layers.run();

	  	auto t1 = std::chrono::high_resolution_clock::now();
	  	if (Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0)
	    {
	      std::cout << "time elapsed: "
	                << std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0).count()
	                << " milliseconds."
	                << std::endl;
	    }

    }
  catch (std::exception &exc)
    {
      std::cerr << std::endl << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Exception on processing: " << std::endl << exc.what()
                << std::endl << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;

      return 1;
    }
  catch (...)
    {
      std::cerr << std::endl << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Unknown exception!" << std::endl << "Aborting!"
                << std::endl
                << "----------------------------------------------------"
                << std::endl;
      return 1;
    }

  return 0;

}


#include <boost/mpl/vector_c.hpp>
#include <boost/mpl/plus.hpp>
#include <boost/mpl/minus.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/equal.hpp>
#include <boost/mpl/placeholders.hpp>

#include <cassert>

namespace mpl = boost::mpl;
using namespace mpl::placeholders;

typedef mpl::vector_c<int, 1, 0, 0, 0, 0, 0, 0> mass;
typedef mpl::vector_c<int, 0, 1, 0, 0, 0, 0, 0> length;
typedef mpl::vector_c<int, 0, 0, 1, 0, 0, 0, 0> time;
typedef mpl::vector_c<int, 0, 0, 0, 1, 0, 0, 0> charge;
typedef mpl::vector_c<int, 0, 0, 0, 0, 1, 0, 0> temperature;
typedef mpl::vector_c<int, 0, 0, 0, 0, 0, 1, 0> intensity;
typedef mpl::vector_c<int, 0, 0, 0, 0, 0, 0, 1> substance;

/**
 * Wrapper for int
 */
template <int n>
struct Int {
    const static int value = n;
};

template <class T, class Dimensions>
struct quantity {
    explicit quantity(T x): value_(x) {}

    template <class Other>
        quantity(const quantity<T, Other>& rhs): value_(rhs.value()) {
            static_assert(mpl::equal<Other, Dimensions>::value, "They must has same dimensions");
        }

    T value() const { return value_; }
    private:
        T value_;
};

template <class T, class D>
quantity<T, D> operator + (const quantity<T,D>& lhs, const quantity<T, D>& rhs) {
    return quantity<T, D>(lhs.value() + rhs.value());
}

template <class T, class D>
quantity<T, D> operator - (const quantity<T, D>& lhs, const quantity<T, D>& rhs) {
    return quantity<T, D>(lhs.value() - rhs.value());
}

/**
 * wrapper for plus
 */
struct plus_f {
    template <class T1, class T2>
        struct apply: mpl::plus<T1, T2> {};
};

/**
 * wrapper for minus
 */
struct minus_f {
    template <class T1, class T2>
        struct apply: mpl::minus<T1, T2> {};
};

/**
 * operator * overloading, using placeholder to compute type
 */
template <class T, class D1, class D2>
quantity<T, typename mpl::transform<D1, D2, mpl::plus<_1, _2>>::type>
    operator * (const quantity<T, D1>& lhs, const quantity<T, D2>& rhs) {
        typedef typename mpl::transform<D1, D2, mpl::plus<_1, _2>>::type dim;
        return quantity<T, dim>(lhs.value() * rhs.value());
    }

template <class T, class D1, class D2>
quantity<T, typename mpl::transform<D1, D2, mpl::minus<_1, _2>>::type>
    operator / (const quantity<T, D1>& lhs, const quantity<T, D2>& rhs) {
        typedef typename mpl::transform<D1, D2, mpl::minus<_1, _2>>::type dim;
        return quantity<T, dim>(lhs.value() / rhs.value());
    }

int main()
{
    quantity<float, length> la(1.0);
    quantity<float, length> lb(2.0);
    quantity<float, mass> m(1.0);

    quantity<float, length> ls = la + lb;
    assert(ls.value() == 3.0 && "la + lb");

    auto res = la * m;

    return 0;
}


#pragma once

#include <Eigen/Core>

#include <ipc/broad_phase/collision_candidate.hpp>

void brute_force_comparison(
    const Eigen::MatrixXd& V0,
    const Eigen::MatrixXd& V1,
    const Eigen::MatrixXi& E,
    const Eigen::MatrixXi& F,
    const Eigen::VectorXi& group_ids,
    ipc::Candidates& candidates,
    const double inflation_radius);


//
// Created by erik on 9/11/17.
//

#ifndef BRANCHEDFLOWSIM_UTILS_H
#define BRANCHEDFLOWSIM_UTILS_H

#include <boost/test/test_tools.hpp>
#include <fstream>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics.hpp>

using namespace boost::accumulators;

///! \todo there should be a test for this utility
struct UniformityCheck
{
    UniformityCheck(bool enabled, std::string name) :
            counts(NUM_BINS, 0.0),
            mEnabled(enabled),
            mName( std::move(name) )
    {

    }

    ~UniformityCheck() {
        if(!mEnabled)
            return;

        std::string message = "Checking uniformity of distribution of " + mName;
        BOOST_TEST_CHECKPOINT(message);

        // evaluate
        using sum_accumulator_t = accumulator_set<double, stats<tag::sum> >;
        using max_accumulator_t = accumulator_set<double, stats<tag::max> >;

        sum_accumulator_t s_acc;
        max_accumulator_t m_acc;

        for(unsigned i = 0; i < NUM_BINS; ++i)
        {
            s_acc(counts[i]);
        }

        // Kolmogorov–Smirnov
        double partial_sum = 0;
        double total = extract::sum(s_acc);

        for(unsigned i = 0; i < NUM_BINS; ++i) {
            partial_sum += counts[i] / total;
            m_acc( std::abs(partial_sum - (i + 0.5) / NUM_BINS) );
        }

        double KS = extract::max(m_acc);

        // value taken from
        // https://de.wikipedia.org/wiki/Kolmogorow-Smirnow-Test
        double critical_value = 1.628 / sqrt(total);

        // log the test statistics
        BOOST_TEST_MESSAGE( boost::format("Kolmogorov–Smirnov statistics %1% for %2%") % KS % mName);

        BOOST_CHECK_LE(KS, critical_value);
    }

    void put_in_bin(double pos) {
        double bin_pos = pos * NUM_BINS;
        if(bin_pos < 0) {
            BOOST_FAIL("Invalid position for binning");
        }
        auto pos_min = static_cast<unsigned>(floor(bin_pos));
        double part = bin_pos - pos_min;
        // this does a little smoothing.
        counts.at((pos_min) % NUM_BINS) += 1 - part;
        counts.at((pos_min + 1) % NUM_BINS) += part;
    }

    void save(std::string filename)
    {
        std::fstream file(filename, std::fstream::out);
        for(auto     c : counts) {
            file << c << "\n";
        }
    }

    void enable() { mEnabled = true; }

    std::vector<double> counts;
    static constexpr std::size_t NUM_BINS = 1000;
    bool                mEnabled = false;
    std::string mName;
};

#include <boost/test/unit_test.hpp>
#include <boost/numeric/ublas/io.hpp>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/max.hpp>
#include <boost/accumulators/statistics/sum.hpp>
#include <fstream>

#endif //BRANCHEDFLOWSIM_UTILS_H


//==================================================================================================
/**
  EVE - Expressive Vector Engine
  Copyright : EVE Contributors & Maintainers
  SPDX-License-Identifier: MIT
**/
//==================================================================================================
#include "test.hpp"
#include <eve/module/bessel.hpp>
#include <boost/math/special_functions/bessel.hpp>
#include <boost/math/special_functions/bessel_prime.hpp>
#include <eve/detail/diff_div.hpp>

//==================================================================================================
//== Types tests
//==================================================================================================
EVE_TEST_TYPES( "Check return types of sph_bessel_jn"
              , eve::test::simd::ieee_reals
              )
<typename T>(eve::as<T>)
{
  using v_t = eve::element_type_t<T>;
  using i_t = eve::as_integer_t<v_t>;
  using I_t = eve::wide<i_t, eve::cardinal_t<T>>;
  TTS_EXPR_IS( eve::sph_bessel_jn(T(), T())  ,  T);
  TTS_EXPR_IS( eve::sph_bessel_jn(v_t(),v_t()), v_t);
  TTS_EXPR_IS( eve::sph_bessel_jn(i_t(),T()),   T);
  TTS_EXPR_IS( eve::sph_bessel_jn(I_t(),T()),   T);
  TTS_EXPR_IS( eve::sph_bessel_jn(i_t(),v_t()), v_t);
//  TTS_EXPR_IS( eve::sph_bessel_jn(I_t(),v_t()), T);
 };

//==================================================================================================
//== integral orders
//==================================================================================================
EVE_TEST( "Check behavior of sph_bessel_jn on wide with integral order"
        , eve::test::simd::ieee_reals
        , eve::test::generate(eve::test::ramp(0), eve::test::randoms(0.0, 20000.0))
        )
  <typename T>(T n, T a0)
{
  using v_t = eve::element_type_t<T>;
  auto eve__sph_bessel_jn =  [](auto n, auto x) { return eve::sph_bessel_jn(n, x); };
  auto std__sph_bessel_jn =  [](auto n, auto x)->v_t { return boost::math::sph_bessel(unsigned(n), double(x)); };
  if constexpr( eve::platform::supports_invalids )
  {
    TTS_ULP_EQUAL(eve__sph_bessel_jn(2, eve::inf(eve::as<v_t>())), v_t(0), 0);
    TTS_ULP_EQUAL(eve__sph_bessel_jn(3, eve::nan(eve::as<v_t>())), eve::nan(eve::as<v_t>()), 0);
  }

  TTS_ULP_EQUAL(eve__sph_bessel_jn(3, v_t(1500)), std__sph_bessel_jn(3u, v_t(1500)), 2.0);
  TTS_ULP_EQUAL(eve__sph_bessel_jn(2, v_t(500)), std__sph_bessel_jn(2u, v_t(500)), 2.0);

  TTS_ULP_EQUAL(eve__sph_bessel_jn(2, v_t(10)), std__sph_bessel_jn(2u, v_t(10))  , 4.0);
  TTS_ULP_EQUAL(eve__sph_bessel_jn(3, v_t(5)),  std__sph_bessel_jn(3u, v_t(5))   , 2.0);

  TTS_ULP_EQUAL(eve__sph_bessel_jn(2, v_t(0.1)), std__sph_bessel_jn(2u, v_t(0.1))  , 2.0);
  TTS_ULP_EQUAL(eve__sph_bessel_jn(3, v_t(0.2)),  std__sph_bessel_jn(3u, v_t(0.2))   , 2.0);

  TTS_ULP_EQUAL(eve__sph_bessel_jn(10, v_t(8)), std__sph_bessel_jn(10u, v_t(8))  , 2.0);
  TTS_ULP_EQUAL(eve__sph_bessel_jn(10, v_t(8)),  std__sph_bessel_jn(10u, v_t(8))   , 2.0);

  if constexpr( eve::platform::supports_invalids )
  {
    TTS_ULP_EQUAL(eve__sph_bessel_jn(2, eve::inf(eve::as<T>())), T(0), 0);
    TTS_ULP_EQUAL(eve__sph_bessel_jn(3, eve::nan(eve::as<T>())), eve::nan(eve::as<T>()), 0);
  }

  TTS_ULP_EQUAL(eve__sph_bessel_jn(3, T(1500)),  T(std__sph_bessel_jn(3u, v_t(1500))),  2.0);
  TTS_ULP_EQUAL(eve__sph_bessel_jn(2, T(500)),   T(std__sph_bessel_jn(2u, v_t(500))),   2.0);

  TTS_ULP_EQUAL(eve__sph_bessel_jn(2, T(10)),    T(std__sph_bessel_jn(2u, v_t(10)))   , 4.0);
  TTS_ULP_EQUAL(eve__sph_bessel_jn(3, T(5)),     T(std__sph_bessel_jn(3u, v_t(5)))    , 2.0);

  TTS_ULP_EQUAL(eve__sph_bessel_jn(2, T(0.1)),   T(std__sph_bessel_jn(2u, v_t(0.1)))  , 2.0);
  TTS_ULP_EQUAL(eve__sph_bessel_jn(3, T(0.2)),   T(std__sph_bessel_jn(3u, v_t(0.2)))  , 2.0);

  TTS_ULP_EQUAL(eve__sph_bessel_jn(10, T(8)),   T(std__sph_bessel_jn(10u, v_t(8)))   , 2.0);
  TTS_ULP_EQUAL(eve__sph_bessel_jn(10, T(8)),   T(std__sph_bessel_jn(10u, v_t(8)))   , 2.0);

  if constexpr( eve::platform::supports_invalids )
  {
    TTS_ULP_EQUAL(eve__sph_bessel_jn(T(2), eve::inf(eve::as<T>())), T(0), 0);
    TTS_ULP_EQUAL(eve__sph_bessel_jn(T(3), eve::nan(eve::as<T>())), eve::nan(eve::as<T>()), 0);
  }

  TTS_ULP_EQUAL(eve__sph_bessel_jn(T(3), T(1500)),  T(std__sph_bessel_jn(3u, v_t(1500))),  2.0);
  TTS_ULP_EQUAL(eve__sph_bessel_jn(T(2), T(500)),   T(std__sph_bessel_jn(2u, v_t(500))),   2.0);

  TTS_ULP_EQUAL(eve__sph_bessel_jn(T(2), T(10)),    T(std__sph_bessel_jn(2u, v_t(10)))   , 4.0);
  TTS_ULP_EQUAL(eve__sph_bessel_jn(T(3), T(5)),     T(std__sph_bessel_jn(3u, v_t(5)))    , 2.0);

  TTS_ULP_EQUAL(eve__sph_bessel_jn(T(2), T(0.1)),   T(std__sph_bessel_jn(2u, v_t(0.1)))  , 2.0);
  TTS_ULP_EQUAL(eve__sph_bessel_jn(T(3), T(0.2)),   T(std__sph_bessel_jn(3u, v_t(0.2)))  , 2.0);

  TTS_ULP_EQUAL(eve__sph_bessel_jn(T(10), T(8)),   T(std__sph_bessel_jn(10u, v_t(8)))   , 2.0);
  TTS_ULP_EQUAL(eve__sph_bessel_jn(T(10), T(8)),   T(std__sph_bessel_jn(10u, v_t(8)))   , 2.0);

  using i_t = eve::as_integer_t<v_t>;
  using I_t = eve::wide<i_t, eve::cardinal_t<T>>;

  if constexpr( eve::platform::supports_invalids )
  {
    TTS_ULP_EQUAL(eve__sph_bessel_jn(I_t(2), eve::inf(eve::as<T>())), T(0), 0);
    TTS_ULP_EQUAL(eve__sph_bessel_jn(I_t(3), eve::nan(eve::as<T>())), eve::nan(eve::as<T>()), 0);
  }

  TTS_ULP_EQUAL(eve__sph_bessel_jn(I_t(3), T(1500)),  T(std__sph_bessel_jn(3u, v_t(1500))),  2.0);
  TTS_ULP_EQUAL(eve__sph_bessel_jn(I_t(2), T(500)),   T(std__sph_bessel_jn(2u, v_t(500))),   2.0);

  TTS_ULP_EQUAL(eve__sph_bessel_jn(I_t(2), T(10)),    T(std__sph_bessel_jn(2u, v_t(10)))   , 4.0);
  TTS_ULP_EQUAL(eve__sph_bessel_jn(I_t(3), T(5)),     T(std__sph_bessel_jn(3u, v_t(5)))    , 2.0);

  TTS_ULP_EQUAL(eve__sph_bessel_jn(I_t(2), T(0.1)),   T(std__sph_bessel_jn(2u, v_t(0.1)))  , 2.0);
  TTS_ULP_EQUAL(eve__sph_bessel_jn(I_t(3), T(0.2)),   T(std__sph_bessel_jn(3u, v_t(0.2)))  , 2.0);

  TTS_ULP_EQUAL(eve__sph_bessel_jn(I_t(10), T(8)),   T(std__sph_bessel_jn(10u, v_t(8)))   , 2.0);
  TTS_ULP_EQUAL(eve__sph_bessel_jn(I_t(10), T(8)),   T(std__sph_bessel_jn(10u, v_t(8)))   , 2.0);

  TTS_RELATIVE_EQUAL(eve__sph_bessel_jn(n, a0),   map(std__sph_bessel_jn, n, a0)   , 0.0035);
};

EVE_TEST( "Check behavior of diff(sph_bessel_j1) on wide"
        , eve::test::simd::ieee_reals
        , eve::test::generate(eve::test::randoms(1.0, 10.0))
        )
  <typename T>(T a0 )
{
  auto eve__diff_bessel_jn =  [](auto n, auto x) { return eve::diff(eve::sph_bessel_jn)(n, x); };
  auto j3 = [](auto x){ return eve::sph_bessel_jn(3, x); };
  auto df = [j3](auto x){return eve::detail::centered_diffdiv(j3, x); };

  TTS_RELATIVE_EQUAL(eve__diff_bessel_jn(3, a0),   df(a0), 1.0e-2);
  TTS_RELATIVE_EQUAL(eve__diff_bessel_jn(1, a0),   eve::diff(eve::sph_bessel_j1)(a0), 1.0e-2);
};


//////////////////////////////////////////////////////////////////////////////
///   Copyright 2003 and onward LASMEA UMR 6602 CNRS/U.B.P Clermont-Ferrand
///   Copyright 2009 and onward LRI    UMR 8623 CNRS/Univ Paris Sud XI
///
///          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 NT2_TOOLBOX_ARITHMETIC_FUNCTION_SCALAR_REMQUO_HPP_INCLUDED
#define NT2_TOOLBOX_ARITHMETIC_FUNCTION_SCALAR_REMQUO_HPP_INCLUDED
#include <nt2/sdk/meta/adapted_traits.hpp>
#include <nt2/sdk/meta/as_integer.hpp>
#include <nt2/sdk/meta/adapted_traits.hpp>
#include <boost/fusion/tuple.hpp>
#include <boost/mpl/vector.hpp>
#include <nt2/include/functions/remainder.hpp>
#include <nt2/include/functions/idivround.hpp>

namespace nt2 { namespace details
{
  template <class T,class Dummy> struct remquo;

  template <class Dummy> struct remquo<double,Dummy>{
    typedef double                                          rem;
    typedef typename meta::as_integer<double,signed>::type  quo;
    typedef boost::fusion::tuple<rem,quo>                 rtype;

    static inline rtype eval(const double& a0, const double& a1)
    {
      rtype res;
      int rt;
      boost::fusion::at_c<0>(res) = ::remquo(a0, a1, &rt);
      boost::fusion::at_c<1>(res) = rt;
      return res;
    }
  };

  template <class Dummy> struct remquo<float,Dummy>{
    typedef float                                           rem;
    typedef typename meta::as_integer<float,signed>::type  quo;
    typedef boost::fusion::tuple<rem,quo>                 rtype;

    static inline rtype eval(const float& a0, const float& a1)
    {
      rtype res;
      int rt;
      boost::fusion::at_c<0>(res) = ::remquof(a0, a1, &rt);
      boost::fusion::at_c<1>(res) = rt;
      return res;
    }
  };
} }

/////////////////////////////////////////////////////////////////////////////
// Implementation when A0 and A1 types are fundamental_
/////////////////////////////////////////////////////////////////////////////
NT2_REGISTER_DISPATCH(tag::remquo_, tag::cpu_,
                        (A0)(A1),
                        (fundamental_<A0>)(fundamental_<A1>)
                       )

namespace nt2 { namespace ext
{
  template<class Dummy>
  struct call<tag::remquo_(tag::fundamental_,tag::fundamental_),
              tag::cpu_, Dummy> : callable
  {
    template<class Sig> struct result;
    template<class This,class A0,class A1>
    struct result<This(A0,A1)>
    {
      typedef typename std::tr1::result_of<meta::floating(A0,A1)>::type rem;
      typedef typename meta::as_integer<rem,signed>::type            quo;
      typedef boost::fusion::tuple<rem,quo>                         type;
    };

    NT2_FUNCTOR_CALL(2)
    {
      typedef typename std::tr1::result_of<meta::floating(A0,A1)>::type ftype;
      typedef typename NT2_RETURN_TYPE(2)::type rtype;
      rtype res;
      boost::fusion::at_c<0>(res) = nt2::remainder(ftype(a0), ftype(a1));
      boost::fusion::at_c<1>(res) = nt2::idivround(ftype(a0), ftype(a1));
      return res;
    }

  };
} }

#endif
// modified by jt the 26/12/2010
// manually modified  by jt the 28/12/2010


#include "../../include/waveO1/observer.hpp"
#include "../../include/waveO1/utilities.hpp"
#include "../../include/mymfem/utilities.hpp"

#include <iostream>
#include <Eigen/Core>

// Computes DG error on time-like face at a specified time
double WaveO1Observer
:: eval_dgFtimeError (std::shared_ptr<GridFunction>& p,
                      std::shared_ptr<GridFunction>& v,
                      double t) const
{
    double errp=0;
    double errv=0;

    // assumes that all elements have the same geometry
    auto faceElGeomType = p->FESpace()->GetMesh()
                ->GetFaceGeometryType(0);

    // L2 error in pressure
    // set integration rules
    IntegrationRules rule1{};
    const IntegrationRule *ir1;
    int order1 = 2*p->FESpace()->GetFE(0)->GetOrder()+2;
    ir1 = &rule1.Get(faceElGeomType, order1);
    m_pE_coeff->SetTime(t);
    m_assembleDgError->SetIntegrationRule(ir1);
    errp = m_assembleDgError->Ftime(p.get(),
                                    m_pE_coeff.get());

    // L2 error in velocity
    // set integration rules
    IntegrationRules rule2{};
    const IntegrationRule *ir2;
    int order2 = 2*v->FESpace()->GetFE(0)->GetOrder()+2;
    ir2 = &rule2.Get(faceElGeomType, order2);
    m_vE_coeff->SetTime(t);
    m_assembleDgError->SetIntegrationRule(ir2);
    errv = m_assembleDgError->Ftime(v.get(),
                                    m_vE_coeff.get());

    return (errp + errv);
}

//! Computes DG error for a space-like face
//! at a specified time
double WaveO1Observer
:: eval_dgFspaceError
(std::shared_ptr<GridFunction>& p,
 std::shared_ptr<GridFunction>& v,
 double t,
 std::shared_ptr<WaveO1InvSqMediumCoeff>&
 invSqMed) const
{
    double errp=0;
    double errv=0;

    // assumes that all elements have the same geometry
    auto elGeomType = p->FESpace()
                ->GetFE(0)->GetGeomType();

    IntegrationRules rule1{}, rule2{};
    const IntegrationRule *ir1, *ir2;

    m_pE_coeff->SetTime(t);
    m_vE_coeff->SetTime(t);

    // pressure
    int order1 = 2*p->FESpace()->GetFE(0)->GetOrder()+2;
    ir1 = &rule1.Get(elGeomType, order1);
    m_assembleDgError->SetIntegrationRule(ir1);
    errp = m_assembleDgError->Fspace(p.get(),
                                     m_pE_coeff.get(),
                                     invSqMed.get());

    // velocity
    int order2 = 2*v->FESpace()->GetFE(0)->GetOrder()+2;
    ir2 = &rule2.Get(elGeomType, order2);
    m_assembleDgError->SetIntegrationRule(ir2);
    m_vE->ProjectCoefficient(*m_vE_coeff);
    errv = m_assembleDgError->Fspace(v.get(),
                                     m_vE_coeff.get());

    return (errp + errv);
}

double WaveO1Observer
:: eval_dgFspaceError
(std::shared_ptr<GridFunction>& p1,
 std::shared_ptr<GridFunction>& p2,
 std::shared_ptr<GridFunction>& v1,
 std::shared_ptr<GridFunction>& v2,
 double t,
 std::shared_ptr<WaveO1InvSqMediumCoeff>&
 invSqMed) const
{
    double errp=0;
    double errv=0;

    // assumes that all elements have the same geometry
    auto elGeomType = p1->FESpace()
                ->GetFE(0)->GetGeomType();

    IntegrationRules rule1{}, rule2{};
    const IntegrationRule *ir1, *ir2;

    m_pE_coeff->SetTime(t);
    m_vE_coeff->SetTime(t);

    // pressure
    int order1 = 2*p1->FESpace()->GetFE(0)->GetOrder()+2;
    ir1 = &rule1.Get(elGeomType, order1);
    m_assembleDgError->SetIntegrationRule(ir1);
    errp = m_assembleDgError->Fspace(p1.get(), p2.get(),
                                     m_pE_coeff.get(),
                                     invSqMed.get());

    // velocity
    int order2 = 2*v1->FESpace()->GetFE(0)->GetOrder()+2;
    ir2 = &rule2.Get(elGeomType, order2);
    m_assembleDgError->SetIntegrationRule(ir2);
    errv = m_assembleDgError->Fspace(v1.get(), v2.get(),
                                     m_vE_coeff.get());

    return (errp + errv);
}

// Computes DG error
std::tuple <double, double> WaveO1Observer
:: eval_xtDgError (BlockVector& W) const
{
    double errDgFtime=0;
    double errDgFspace=0;

    int Nt = m_tWspace->GetNE();
    int xdimW1 = m_xW1space->GetTrueVSize();
    int xdimW2 = m_xW2space->GetTrueVSize();

    Vector& p = W.GetBlock(0);
    Vector& v = W.GetBlock(1);

    Vector pSol1(xdimW1), pSol2(xdimW1);
    std::shared_ptr<GridFunction> pGSol1
            = std::make_shared<GridFunction>
            (m_xW1space, pSol1);
    std::shared_ptr<GridFunction> pGSol2
            = std::make_shared<GridFunction>
            (m_xW1space, pSol2);

    Vector vSol1(xdimW2), vSol2(xdimW2);
    std::shared_ptr<GridFunction> vGSol1
            = std::make_shared<GridFunction>
            (m_xW2space, vSol1);
    std::shared_ptr<GridFunction> vGSol2
            = std::make_shared<GridFunction>
            (m_xW2space, vSol2);

    // medium
    auto invSqMed = std::make_shared<WaveO1InvSqMediumCoeff>
            (m_testCase);

    ElementTransformation *tTrans = nullptr;
    const FiniteElement *tFe = nullptr;
    Vector tShape;
    Array<int> tVdofs;

    // DG error for time-like faces
    Eigen::VectorXd bufErrFtime(Nt);
    bufErrFtime.setZero();

    double errFtime;
    for (int n=0; n<Nt; n++)
    {
        m_tWspace->GetElementVDofs(n, tVdofs);
        tTrans = m_tWspace->GetElementTransformation(n);

        tFe = m_tWspace->GetFE(n);
        int tNdofs = tFe->GetDof();
        tShape.SetSize(tNdofs);

        int order = 2*tFe->GetOrder()+2;
        const IntegrationRule *ir
                = &IntRules.Get(tFe->GetGeomType(), order);

        for (int i = 0; i < ir->GetNPoints(); i++)
        {
            const IntegrationPoint &ip = ir->IntPoint(i);
            tTrans->SetIntPoint(&ip);
            tFe->CalcShape(ip, tShape);

            // build solution at time t
            Vector t;
            tTrans->Transform(ip, t);
            build_xSol_FG(p, tShape, tVdofs, pSol1);
            build_xSol_FG(v, tShape, tVdofs, vSol1);

            errFtime = eval_dgFtimeError
                    (pGSol1, vGSol1, t(0));

            double w = ip.weight*tTrans->Weight();
            bufErrFtime(n) += w*errFtime;
        }
    }
    errDgFtime = std::sqrt(bufErrFtime.sum());

    // DG error for space-like faces
    Eigen::VectorXd bufErrFspace(Nt+1);
    bufErrFspace.setZero();

    Vector t;
    IntegrationPoint ip0, ip1;
    ip0.Set1w(0, 1);
    ip1.Set1w(1, 1);

    // for t=0
    {
        int n=0;
        int np = n;

        // build solution at tn^{+}
        m_tWspace->GetElementVDofs(np, tVdofs);
        tTrans = m_tWspace->GetElementTransformation(np);
        tFe = m_tWspace->GetFE(np);
        int tNdofs = tFe->GetDof();
        tShape.SetSize(tNdofs);

        tTrans->SetIntPoint(&ip0);
        tFe->CalcShape(ip0, tShape);
        tTrans->Transform(ip0, t);
        build_xSol_FG(p, tShape, tVdofs, pSol2);
        build_xSol_FG(v, tShape, tVdofs, vSol2);

        // compute error
        bufErrFspace(0) = eval_dgFspaceError
                (pGSol2, vGSol2, t(0), invSqMed);
    }

    // for 0 < t < T
    for (int n=1; n<Nt; n++)
    {
        int nm = n-1;
        int np = n;

        // build solution at tn^{-}
        m_tWspace->GetElementVDofs(nm, tVdofs);
        tTrans = m_tWspace->GetElementTransformation(nm);
        tFe = m_tWspace->GetFE(nm);
        int tNdofs = tFe->GetDof();
        tShape.SetSize(tNdofs);

        tTrans->SetIntPoint(&ip1);
        tFe->CalcShape(ip1, tShape);
        tTrans->Transform(ip1, t);
        build_xSol_FG(p, tShape, tVdofs, pSol1);
        build_xSol_FG(v, tShape, tVdofs, vSol1);

        // build solution at tn^{+}
        m_tWspace->GetElementVDofs(np, tVdofs);
        tTrans = m_tWspace->GetElementTransformation(np);
        tFe = m_tWspace->GetFE(np);
        tNdofs = tFe->GetDof();
        tShape.SetSize(tNdofs);

        tTrans->SetIntPoint(&ip0);
        tFe->CalcShape(ip0, tShape);
        tTrans->Transform(ip0, t);
        build_xSol_FG(p, tShape, tVdofs, pSol2);
        build_xSol_FG(v, tShape, tVdofs, vSol2);

        // compute error
        bufErrFspace(n) = eval_dgFspaceError
                (pGSol1, pGSol2, vGSol1, vGSol2,
                 t(0), invSqMed);
    }

    // for t=T
    {
        int n = Nt;
        int nm = n-1;

        // build solution at tn^{-}
        m_tWspace->GetElementVDofs(nm, tVdofs);
        tTrans = m_tWspace->GetElementTransformation(nm);
        tFe = m_tWspace->GetFE(nm);
        int tNdofs = tFe->GetDof();
        tShape.SetSize(tNdofs);

        tTrans->SetIntPoint(&ip1);
        tFe->CalcShape(ip1, tShape);
        tTrans->Transform(ip1, t);
        build_xSol_FG(p, tShape, tVdofs, pSol1);
        build_xSol_FG(v, tShape, tVdofs, vSol1);

        // compute error
        bufErrFspace(n) = eval_dgFspaceError
                (pGSol1, vGSol1, t(0), invSqMed);
    }
    errDgFspace = std::sqrt(0.5*bufErrFspace.sum());

    return {errDgFtime, errDgFspace};
}

// End of file


#include <ocpl_tsr/task_space_regions.h>

#include <vector>
#include <Eigen/Geometry>
#include <memory>

namespace ocpl
{
TSR::TSR(Transform tf, TSRBounds bounds) : tf_nominal(tf), bounds(bounds)
{
}

Transform TSR::valuesToPose(const std::vector<double>& values) const
{
    using Translation = Eigen::Translation3d;
    using AngleAxis = Eigen::AngleAxisd;
    using Vector = Eigen::Vector3d;

    // clang-format off
    Transform t;
      t = Translation(values[0], values[1], values[2]) *
          AngleAxis(values[3], Vector::UnitX()) *
          AngleAxis(values[4], Vector::UnitY()) *
          AngleAxis(values[5], Vector::UnitZ());
    // clang-format on
    if (local_)
    {
        return tf_nominal * t;  // post-multiply = local frame transformation
    }
    else
    {
        Transform tf_pos(Translation(values[0], values[1], values[2]));
        Transform tf_rot(AngleAxis(values[3], Vector::UnitX()) * AngleAxis(values[4], Vector::UnitY()) *
                         AngleAxis(values[5], Vector::UnitZ()));
        return tf_pos * tf_nominal * tf_rot;  // pre-multiply = global frame transformation
    }
}

std::vector<double> TSR::poseToValues(const Transform& tf) const
{
    // values are calculated in nominal frame of this tsr
    Eigen::Isometry3d tf_diff = tf_nominal.inverse() * tf;
    Eigen::Vector3d pos = tf_diff.translation();
    // Eigen documentation:
    //    The returned angles are in the ranges [0:pi]x[-pi:pi]x[-pi:pi].
    Eigen::Vector3d angles = tf_diff.rotation().eulerAngles(0, 1, 2);
    angles = minNormEquivalent(angles);

    std::vector<double> values{ pos.x(), pos.y(), pos.z(), angles.x(), angles.y(), angles.z() };
    return values;
}

double TSR::volume(double angle_weight) const
{
    double volume{ 0.0 };
    // position part
    volume += bounds.x.range();
    volume += bounds.y.range();
    volume += bounds.z.range();
    // angular part
    volume += angle_weight * bounds.rx.range();
    volume += angle_weight * bounds.ry.range();
    volume += angle_weight * bounds.rz.range();
    return volume;
}

Eigen::Vector3d minNormEquivalent(const Eigen::Vector3d& angles)
{
    Eigen::Matrix<double, 9, 3> m;
    double x(angles.x()), y(angles.y()), z(angles.z());
    // clang-format off
   m <<  x,  y, z,
        x - M_PI, -y - M_PI, z - M_PI,
        x - M_PI, -y - M_PI, z + M_PI,
        x - M_PI, -y + M_PI, z - M_PI,
        x - M_PI, -y + M_PI, z + M_PI,
        x + M_PI, -y - M_PI, z - M_PI,
        x + M_PI, -y - M_PI, z + M_PI,
        x + M_PI, -y + M_PI, z - M_PI,
        x + M_PI, -y + M_PI, z + M_PI;
    // clang-format on
    // get the index of the row with the lowest norm
    Eigen::VectorXd::Index index;
    m.rowwise().norm().minCoeff(&index);
    return m.row(index);
}

Eigen::Matrix<double, 6, 1> poseDistance(const Transform& tf_ref, const Transform& tf)
{
    const Transform tf_diff = tf_ref.inverse() * tf;
    Eigen::Vector3d angles = minNormEquivalent(tf_diff.rotation().eulerAngles(0, 1, 2));
    Eigen::Matrix<double, 6, 1> d;
    d << tf_diff.translation(), angles;
    return d;
}
}  // namespace ocpl


#include "davidson_util.h"

#include <Eigen/Dense>
#include <algorithm>
#include <cmath>

std::pair<double, std::vector<double>> DavidsonUtil::diagonalize(
    const std::vector<double>& initial_vector,
    const std::vector<double>& diagonal,
    const std::function<std::vector<double>(
        const std::vector<double>&, const bool)>& apply_hamiltonian,
    const int max_iterations_in,
    const bool verbose) {
  const double TOLERANCE = 1.0e-10;
  const double EPSILON = 1.0e-10;
  const int n_dets = initial_vector.size();
  double lowest_eigenvalue = 0.0;
  std::vector<double> lowest_eigenvector;

  if (n_dets == 1) {
    lowest_eigenvalue = diagonal[0];
    lowest_eigenvector.resize(1);
    lowest_eigenvector[0] = 1.0;
    return std::make_pair(lowest_eigenvalue, std::move(lowest_eigenvector));
  }

  const int max_iterations = std::min(n_dets, max_iterations_in);
  double lowest_eigenvalue_prev = 0.0;
  double residual_norm = 0.0;

  Eigen::MatrixXd v = Eigen::MatrixXd::Zero(n_dets, max_iterations);
  for (int i = 0; i < n_dets; i++) v(i, 0) = initial_vector[i];
  v.col(0).normalize();

  Eigen::MatrixXd Hv = Eigen::MatrixXd::Zero(n_dets, max_iterations);
  Eigen::VectorXd w = Eigen::VectorXd::Zero(n_dets);
  Eigen::VectorXd Hw = Eigen::VectorXd::Zero(n_dets);
  Eigen::MatrixXd h_krylov =
      Eigen::MatrixXd::Zero(max_iterations, max_iterations);
  Eigen::MatrixXd h_overwrite;
  Eigen::VectorXd eigenvalues = Eigen::VectorXd::Zero(max_iterations);
  int len_work = 3 * max_iterations - 1;
  Eigen::VectorXd work(len_work);
  bool converged = false;
  std::vector<double> tmp_v(n_dets);

  // Get diagonal elements.
  Eigen::VectorXd diag_elems(n_dets);
  for (int i = 0; i < n_dets; i++) diag_elems[i] = diagonal[i];

  // First iteration.
  for (int i = 0; i < n_dets; i++) tmp_v[i] = v(i, 0);
  const auto& tmp_Hv = apply_hamiltonian(tmp_v, true);
  for (int i = 0; i < n_dets; i++) Hv(i, 0) = tmp_Hv[i];
  lowest_eigenvalue = v.col(0).dot(Hv.col(0));
  h_krylov(0, 0) = lowest_eigenvalue;
  w = v.col(0);
  Hw = Hv.col(0);
  if (verbose) print_intermediate_result(0, lowest_eigenvalue);

  for (int it = 1; it < max_iterations; it++) {
    // Compute residual.
    for (int j = 0; j < n_dets; j++) {
      const double diff_to_diag = lowest_eigenvalue - diag_elems[j];
      if (std::abs(diff_to_diag) < EPSILON) {
        v(j, it) = -1.0;
      } else {
        v(j, it) = (Hw(j, 0) - lowest_eigenvalue * w(j, 0)) / diff_to_diag;
      }
    }

    // If residual is small, converge.
    residual_norm = v.col(it).norm();
    if (residual_norm < TOLERANCE) converged = true;

    // Orthogonalize and normalize.
    for (int i = 0; i < it; i++) {
      double norm = v.col(it).dot(v.col(i));
      v.col(it) -= norm * v.col(i);
    }
    v.col(it).normalize();

    // Apply H once.
    for (int i = 0; i < n_dets; i++) tmp_v[i] = v(i, it);
    const auto& tmp_Hv2 = apply_hamiltonian(tmp_v, false);
    for (int i = 0; i < n_dets; i++) Hv(i, it) = tmp_Hv2[i];

    // Construct subspace matrix.
    for (int i = 0; i <= it; i++) {
      h_krylov(i, it) = v.col(i).dot(Hv.col(it));
      h_krylov(it, i) = h_krylov(i, it);
    }

    // Diagonalize subspace matrix.
    len_work = 3 * it + 2;
    h_overwrite = h_krylov.leftCols(it + 1).topRows(it + 1);
    Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> eigenSolver(
        h_krylov.leftCols(it + 1).topRows(it + 1));
    const auto& eigenvalues = eigenSolver.eigenvalues();
    const auto& eigenvectors = eigenSolver.eigenvectors();
    lowest_eigenvalue = eigenvalues[0];
    int lowest_id = 0;
    for (int i = 1; i <= it; i++) {
      if (eigenvalues[i] < lowest_eigenvalue) {
        lowest_eigenvalue = eigenvalues[i];
        lowest_id = i;
      }
    }
    w = v.leftCols(it) * eigenvectors.col(lowest_id).topRows(it);
    Hw = Hv.leftCols(it) * eigenvectors.col(lowest_id).topRows(it);

    if (verbose) print_intermediate_result(it, lowest_eigenvalue);
    if (std::abs(lowest_eigenvalue - lowest_eigenvalue_prev) < TOLERANCE) {
      converged = true;
      break;
    } else {
      lowest_eigenvalue_prev = lowest_eigenvalue;
    }

    if (converged) break;
  }

  lowest_eigenvector.resize(n_dets);
  for (int i = 0; i < n_dets; i++) lowest_eigenvector[i] = w(i);

  return std::make_pair(lowest_eigenvalue, std::move(lowest_eigenvector));
}

void DavidsonUtil::print_intermediate_result(
    const int iteration, const double lowest_eigenvalue) {
  printf(
      "Davidson Iteration #%d. Eigenvalue: %#.12f\n",
      iteration,
      lowest_eigenvalue);
}