#include <metaSMT/frontend/QF_BV.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/dynamic_bitset.hpp>
#include <string>

using namespace std;
using namespace metaSMT;
using namespace metaSMT::logic;
using namespace metaSMT::logic::QF_BV;
namespace proto = boost::proto;
using boost::dynamic_bitset;

BOOST_FIXTURE_TEST_SUITE(QF_BV, Solver_Fixture )

BOOST_AUTO_TEST_CASE( negative_t )
{
   const char w = 32;

   bitvector x = new_bitvector(w);
   BOOST_REQUIRE( solve(ctx) );

   assertion( ctx, equal(bvsint(-1, w), x) );

   BOOST_REQUIRE( solve(ctx) );

   int xd = read_value(ctx, x);
   BOOST_CHECK_EQUAL(xd, -1);
}

BOOST_AUTO_TEST_CASE( negative_multiply_t )
{
  using namespace boost::logic;

  const char w = 8;

  bitvector x = new_bitvector(w);

  BOOST_REQUIRE( solve(ctx) );

  assertion( ctx, equal( x, bvmul( bvsint(1, w), bvsint(-1, w) ) ) );

  BOOST_REQUIRE( solve(ctx) );

  signed char xd = read_value(ctx, x);
  BOOST_CHECK_EQUAL( (int) xd, (int)-1 );
}

BOOST_AUTO_TEST_CASE( simple_sat )
{
  BOOST_REQUIRE( solve(ctx) );
  // sat
  assertion( ctx, equal(bit1,bit1));
  BOOST_REQUIRE( solve(ctx) );

  assertion( ctx, equal(bit1, bvuint(1,1)) );
  BOOST_REQUIRE( solve(ctx) );
  assertion( ctx, equal(bit1, bvsint(-1,1)) );
  BOOST_REQUIRE( solve(ctx) );

  assertion( ctx, equal(bit1, bvbin("1")) );
  BOOST_REQUIRE( solve(ctx) );

  // unsat
  assumption( ctx, equal(bit1, bit0) );
  BOOST_REQUIRE( !solve(ctx) );

  assumption( ctx, equal(bit1, bvuint(0,1)) );
  BOOST_REQUIRE( !solve(ctx) );
  assumption( ctx, equal(bit1, bvsint(0,1)) );
  BOOST_REQUIRE( !solve(ctx) );

  assumption( ctx, equal(bit1, bvbin("0")) );
  BOOST_REQUIRE( !solve(ctx) );

}

BOOST_AUTO_TEST_CASE( incremental )
{
  BOOST_REQUIRE( solve(ctx) );
  assumption( ctx, equal(bit0, bit1));
  BOOST_REQUIRE( !solve(ctx) );
  BOOST_REQUIRE( solve(ctx) );
}

BOOST_AUTO_TEST_CASE( equal_t )
{
  bitvector x = new_bitvector(1);
  bitvector y = new_bitvector(1);
  // equal
  assumption( ctx, equal(x, x));
  BOOST_REQUIRE( solve(ctx) );
  assumption( ctx, equal(x, x));
  BOOST_REQUIRE( solve(ctx) );
  assumption( ctx, equal(x, bit1));
  BOOST_REQUIRE( solve(ctx) );
  assumption( ctx, equal(x, bit0));
  BOOST_REQUIRE( solve(ctx) );
  assumption( ctx, equal(x, y));
  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, equal(bit1, bvnot(bit0)) );
  BOOST_REQUIRE( solve(ctx) );
  assumption( ctx, equal(bit0, bvnot(bit1)) );
  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, equal(bit0, bvnot(bvnot(bit0))) );
  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, equal(bit1, bvnot(bvnot(bit1))) );
  BOOST_REQUIRE( solve(ctx) );

  // nequal
  assumption( ctx, equal(bit1, bit0));
  BOOST_REQUIRE( !solve(ctx) );
  assumption( ctx, equal(bit0, bit1));
  BOOST_REQUIRE( !solve(ctx) );
  assumption( ctx, equal(x, bvnot(x)));
  BOOST_REQUIRE( !solve(ctx) );
  assumption( ctx, equal(bvnot(x), x));
  BOOST_REQUIRE( !solve(ctx) );
}

BOOST_AUTO_TEST_CASE( nequal_t )
{
  bitvector x = new_bitvector(1);
  bitvector y = new_bitvector(1);
  // equal
  assumption( ctx, nequal(x, x));
  BOOST_REQUIRE( !solve(ctx) );

  assumption( ctx, nequal(bit1, bvnot(bit0)) );
  BOOST_REQUIRE( !solve(ctx) );
  assumption( ctx, nequal(bit0, bvnot(bit1)) );
  BOOST_REQUIRE( !solve(ctx) );

  assumption( ctx, nequal(bit0, bvnot(bvnot(bit0))) );
  BOOST_REQUIRE( !solve(ctx) );

  assumption( ctx, nequal(bit1, bvnot(bvnot(bit1))) );
  BOOST_REQUIRE( !solve(ctx) );

  assumption( ctx, nequal(x, y));
  BOOST_REQUIRE( solve(ctx) );

  // nequal
  assumption( ctx, nequal(x, bit1));
  BOOST_REQUIRE( solve(ctx) );
  assumption( ctx, nequal(x, bit0));
  BOOST_REQUIRE( solve(ctx) );
  assumption( ctx, nequal(bit1, bit0));
  BOOST_REQUIRE( solve(ctx) );
  assumption( ctx, nequal(bit0, bit1));
  BOOST_REQUIRE( solve(ctx) );
  assumption( ctx, nequal(x, bvnot(x)));
  BOOST_REQUIRE( solve(ctx) );
  assumption( ctx, nequal(bvnot(x), x));
  BOOST_REQUIRE( solve(ctx) );
}

BOOST_AUTO_TEST_CASE( bin_constant_t )
{

  assumption(ctx, equal( bvbin("0"), bvuint(0, 1) ) );
  BOOST_REQUIRE( solve(ctx) );

  assumption(ctx, equal( bvbin("1"), bvuint(1, 1) ) );
  BOOST_REQUIRE( solve(ctx) );

  assumption(ctx, equal( bvbin("0000"), bvuint(0, 4) ) );
  BOOST_REQUIRE( solve(ctx) );

  assumption(ctx, equal( bvbin("0011"), bvuint(3, 4) ) );
  BOOST_REQUIRE( solve(ctx) );

  assumption(ctx, equal( bvbin("1010"), bvuint(10, 4) ) );
  BOOST_REQUIRE( solve(ctx) );

  assumption(ctx, equal( bvbin("1111"), bvuint(15, 4) ) );
  BOOST_REQUIRE( solve(ctx) );

  assumption(ctx, equal( bvbin("11111111"), bvuint(255,8) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, nequal( bvbin("11111111"), bvuint(255,8) ) );
  BOOST_REQUIRE( !solve(ctx) );

  assumption(ctx, equal( bvbin("11110000"), bvuint(240,8) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, nequal( bvbin("11110000"), bvuint(240,8) ) );
  BOOST_REQUIRE( !solve(ctx) );

  bitvector x = new_bitvector(8);

  assumption(ctx, equal( x,  bvbin("10100011") ) );
  BOOST_REQUIRE( solve(ctx) );
  unsigned u = read_value(ctx, x);
  BOOST_REQUIRE_EQUAL( u, 163u);
  assumption(ctx, nequal( bvbin("10100011"), bvuint(163,8) ) );
  BOOST_REQUIRE( !solve(ctx) );
}

BOOST_AUTO_TEST_CASE( hex_constant_t )
{

  assumption(ctx, equal( bvhex("0"), bvuint(0, 4) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, equal( bvhex("1"), bvuint(1, 4) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, equal( bvhex("2"), bvuint(2, 4) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, equal( bvhex("3"), bvuint(3, 4) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, equal( bvhex("4"), bvuint(4, 4) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, equal( bvhex("5"), bvuint(5, 4) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, equal( bvhex("6"), bvuint(6, 4) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, equal( bvhex("7"), bvuint(7, 4) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, equal( bvhex("8"), bvuint(8, 4) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, equal( bvhex("9"), bvuint(9, 4) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, equal( bvhex("a"), bvuint(10, 4) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, equal( bvhex("A"), bvuint(10, 4) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, equal( bvhex("b"), bvuint(11, 4) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, equal( bvhex("B"), bvuint(11, 4) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, equal( bvhex("c"), bvuint(12, 4) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, equal( bvhex("C"), bvuint(12, 4) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, equal( bvhex("d"), bvuint(13, 4) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, equal( bvhex("D"), bvuint(13, 4) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, equal( bvhex("e"), bvuint(14, 4) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, equal( bvhex("E"), bvuint(14, 4) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, equal( bvhex("f"), bvuint(15, 4) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, equal( bvhex("F"), bvuint(15, 4) ) );
  BOOST_REQUIRE( solve(ctx) );

  bitvector x = new_bitvector(8);
  assumption(ctx, equal( bvhex("FF"), bvbin("11111111") ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, nequal( bvhex("FF"), bvbin("11111111") ) );
  BOOST_REQUIRE( !solve(ctx) );

  assumption(ctx, equal( bvhex("F0"), bvuint(240,8) ) );
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, nequal( bvhex("F0"), bvuint(240,8) ) );
  BOOST_REQUIRE( !solve(ctx) );

  assumption(ctx, equal( x,  bvhex("A3") ) );
  BOOST_REQUIRE( solve(ctx) );
  unsigned u = read_value(ctx, x);
  BOOST_REQUIRE_EQUAL( u, 163u);
  assumption(ctx, nequal( bvhex("A3"), bvuint(163,8) ) );
  BOOST_REQUIRE( !solve(ctx) );
}

BOOST_AUTO_TEST_CASE( read_value_eq_t )
{
  using namespace boost::logic;

  bitvector x = new_bitvector(1);
  bitvector y = new_bitvector(1);

  assertion( ctx, equal(x, y) );

  BOOST_REQUIRE( solve(ctx) );

  // bool value
  bool xb = read_value (ctx, x);
  bool yb = read_value (ctx, y);

  BOOST_CHECK_EQUAL( xb, yb );

  // vector bool
  vector<bool> xvb = read_value(ctx, x);
  vector<bool> yvb = read_value(ctx, y);

  BOOST_CHECK( xvb.size() == 1u );
  BOOST_CHECK( yvb.size() == 1u );
  BOOST_CHECK_EQUAL( xvb.at(0), yvb.at(0) );

  // vector tribool
  vector<tribool> xvt = read_value(ctx, x);
  vector<tribool> yvt = read_value(ctx, y);

  BOOST_CHECK_EQUAL( xvt.size(), 1u );
  BOOST_CHECK_EQUAL( yvt.size(), 1u );
  BOOST_CHECK_EQUAL( xvt.at(0), yvt.at(0) );

  // dynamic_bitset
  dynamic_bitset<> xd = read_value(ctx, x);
  dynamic_bitset<> yd = read_value(ctx, y);
  BOOST_CHECK_EQUAL( xd.size(), 1u );
  BOOST_CHECK_EQUAL( yd.size(), 1u );
  BOOST_CHECK_EQUAL( xd[0], yvt[0] );

  // string
  string xs = read_value(ctx, x);
  string ys = read_value(ctx, y);
  BOOST_CHECK_EQUAL( xs, ys );
}

BOOST_AUTO_TEST_CASE( read_value_t )
{
  using namespace boost::logic;

  bitvector x = new_bitvector(1);
  bitvector y = new_bitvector(1);

  assertion( ctx, nequal(x, y) );
  // add some constraints so that it will not be optimized away
  assertion( ctx, equal(equal(x,x),equal(y,y)) );

  BOOST_REQUIRE( solve(ctx) );

  // bool value
  bool xb = read_value (ctx, x);
  bool yb = read_value (ctx, y);

  BOOST_CHECK_NE( xb, yb );

  // vector bool
  vector<bool> xvb = read_value(ctx, x);
  vector<bool> yvb = read_value(ctx, y);

  BOOST_CHECK_EQUAL( xvb.size(), 1u );
  BOOST_CHECK_EQUAL( yvb.size(), 1u );
  BOOST_CHECK_NE( xvb.at(0), yvb.at(0) );

  // vector tribool
  vector<tribool> xvt = read_value(ctx, x);
  vector<tribool> yvt = read_value(ctx, y);

  BOOST_CHECK_EQUAL( xvt.size(), 1u );
  BOOST_CHECK_EQUAL( yvt.size(), 1u );
  BOOST_CHECK_NE( xvt.at(0), yvt.at(0) );

  // dynamic_bitset
  dynamic_bitset<> xd = read_value(ctx, x);
  dynamic_bitset<> yd = read_value(ctx, y);
  BOOST_CHECK_EQUAL( xd.size(), 1u );
  BOOST_CHECK_EQUAL( yd.size(), 1u );
  BOOST_CHECK_NE( xd[0], yvt[0] );

  // string
  string xs = read_value(ctx, x);
  string ys = read_value(ctx, y);
  BOOST_CHECK_NE( xs, ys );
}

BOOST_AUTO_TEST_CASE( bvand_t )
{
  using namespace boost::logic;

  bitvector x = new_bitvector(2);
  bitvector y = new_bitvector(2);
  bitvector z = new_bitvector(2);
  short xd, yd, zd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, equal( bvand(x,y), z) );
  assumption( ctx, equal( z, bvbin("01")) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  zd = read_value(ctx, z);

  BOOST_CHECK_EQUAL( (xd&yd), zd);

}

BOOST_AUTO_TEST_CASE( bvneg_t )
{
  using namespace boost::logic;

  bitvector x = new_bitvector(8);

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, equal( bvneg(bvneg(x)), x) );
  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, nequal( bvneg(bvneg(x)), x) );
  BOOST_REQUIRE( !solve(ctx) );

  assumption( ctx, equal( bvneg(x), bvadd(bvnot(x), bvuint(1,8))) );
  BOOST_REQUIRE( solve(ctx) );
  assumption( ctx, nequal( bvneg(x), bvadd(bvnot(x), bvuint(1,8))) );
  BOOST_REQUIRE( !solve(ctx) );

  assumption(ctx, equal( bvneg( bvuint(87, 8)), bvsint(-87, 8)));
  BOOST_REQUIRE( solve(ctx) );
  assumption(ctx, nequal( bvneg( bvuint(87, 8)), bvsint(-87, 8)));
  BOOST_REQUIRE( !solve(ctx) );

}

BOOST_AUTO_TEST_CASE( bvnand_t )
{
  using namespace boost::logic;

  const unsigned w = sizeof(short)*8;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  bitvector z = new_bitvector(w);
  dynamic_bitset<> xd, yd, zd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, equal( bvnand(x,y), z) );
  assumption( ctx, equal( z, bvuint(1, w)) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  zd = read_value(ctx, z);

  BOOST_CHECK_EQUAL( ~(xd&yd), zd);
}

BOOST_AUTO_TEST_CASE( bvor_t )
{
  using namespace boost::logic;

  bitvector x = new_bitvector(2);
  bitvector y = new_bitvector(2);
  bitvector z = new_bitvector(2);
  dynamic_bitset<> xd, yd, zd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, equal( bvor(x,y), z) );
  assumption( ctx, equal( z, bvbin("01")) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  zd = read_value(ctx, z);

  BOOST_CHECK_EQUAL( (xd|yd), zd);
}

BOOST_AUTO_TEST_CASE( bvxor_t )
{
  using namespace boost::logic;

  bitvector x = new_bitvector(2);
  bitvector y = new_bitvector(2);
  bitvector z = new_bitvector(2);
  dynamic_bitset<> xd, yd, zd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, equal( bvxor(x,y), z) );
  assumption( ctx, equal( z, bvbin("10")) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  zd = read_value(ctx, z);

  BOOST_CHECK_EQUAL( (xd^yd), zd);

}

BOOST_AUTO_TEST_CASE( bvnor_t )
{
  using namespace boost::logic;

  bitvector x = new_bitvector(2);
  bitvector y = new_bitvector(2);
  bitvector z = new_bitvector(2);
  dynamic_bitset<> xd, yd, zd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, equal( bvnor(x,y), z) );
  assumption( ctx, equal( z, bvbin("10")) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  zd = read_value(ctx, z);

  BOOST_CHECK_EQUAL( ~(xd|yd), zd);

}

BOOST_AUTO_TEST_CASE( bvxnor_t )
{
  using namespace boost::logic;

  bitvector x = new_bitvector(2);
  bitvector y = new_bitvector(2);
  bitvector z = new_bitvector(2);
  dynamic_bitset<> xd, yd, zd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, equal( bvxnor(x,y), z) );
  assumption( ctx, equal( z, bvbin("10")) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  zd = read_value(ctx, z);

  BOOST_CHECK_EQUAL( (~xd^yd), zd);

}

BOOST_AUTO_TEST_CASE( bvadd_t )
{
  using namespace boost::logic;

  const char w = 8;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  bitvector z = new_bitvector(w);
  unsigned char xd, yd, zd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, equal( bvadd(x,y), z) );
  assumption( ctx, equal( z, bvuint(135, w)) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  zd = read_value(ctx, z);

  unsigned char r = xd+yd;

  BOOST_REQUIRE_EQUAL( 135, zd);
  BOOST_CHECK_EQUAL( r, zd);

}

BOOST_AUTO_TEST_CASE( bvsub_t )
{
  using namespace boost::logic;

  const char w = 8;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  bitvector z = new_bitvector(w);
  unsigned char xd, yd, zd, r;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, equal( bvsub(x,y), z) );
  assumption( ctx, equal( z, bvuint(135, w)) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  zd = read_value(ctx, z);

  r = xd-yd;
  BOOST_REQUIRE_EQUAL( 135, zd);
  BOOST_CHECK_EQUAL( r, zd);

}

BOOST_AUTO_TEST_CASE( bvsub_2 )
{
  using namespace boost::logic;

  const char w = 8;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);

  unsigned xd, yd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, equal( bvadd(bvsub(x,y),y), x) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);

  BOOST_CHECK_EQUAL( (xd-yd)+yd, xd );

  assumption( ctx, nequal( bvadd(bvsub(x,y),y), x) );
  BOOST_REQUIRE( !solve(ctx) );
}

BOOST_AUTO_TEST_CASE( bvmul_t )
{
  using namespace boost::logic;

  const char w = 8;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  bitvector z = new_bitvector(w);
  unsigned char xd, yd, zd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, equal( bvmul(x,y), z) );
  assumption( ctx, equal( z, bvuint(135, w)) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  zd = read_value(ctx, z);

  unsigned char r = xd*yd;

  BOOST_REQUIRE_EQUAL( 135, zd);
  BOOST_CHECK_EQUAL( r, zd);
}

BOOST_AUTO_TEST_CASE( bvudiv_t )
{
  using namespace boost::logic;

  const char w = 8;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  bitvector z = new_bitvector(w);
  unsigned char xd, yd, zd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, equal( bvudiv(x,y), z) );
  assumption( ctx, nequal( bvuint(0,w), y) );
  assumption( ctx, equal( z, bvuint(7, w)) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  zd = read_value(ctx, z);

  BOOST_REQUIRE_NE( yd, 0);
  unsigned char r = xd/yd;

  BOOST_REQUIRE_EQUAL( 7, zd);
  BOOST_CHECK_EQUAL( r, zd);
}

BOOST_AUTO_TEST_CASE( bvudiv_250_1 )
{
  using namespace boost::logic;

  const char w = 8;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  bitvector z = new_bitvector(w);
  unsigned char xd, yd, zd;

  BOOST_REQUIRE( solve(ctx) );
  assumption( ctx, equal( bvudiv(x,y), z) );
  assumption( ctx, equal( bvuint(1,w), y) );
  assumption( ctx, equal( bvuint(250,w), x) );
  BOOST_REQUIRE( solve(ctx) );
  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  zd = read_value(ctx, z);

  BOOST_REQUIRE_NE( yd, 0);
  unsigned char r = xd/yd;

  BOOST_CHECK_EQUAL( (unsigned)r, (unsigned)zd);
}

BOOST_AUTO_TEST_CASE( bvudiv_2 )
{
  using namespace boost::logic;

  const char w = 8;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  bitvector z = new_bitvector(w);
  unsigned char xd, yd, zd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, equal( bvudiv(x,y), z) );

  assumption( ctx, nequal( bvuint(0,w), y) );
 // assumption( ctx, equal( bvuint(7,w), x) );
  assumption( ctx, nequal( bvuint(0,w), y) );
//  assumption( ctx, equal( bvuint(35,w), y) );
  //assumption( ctx, equal( bvuint(2,w), y) );

  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);

  yd = read_value(ctx, y);
  zd = read_value(ctx, z);

  BOOST_REQUIRE_NE( yd, 0);
  unsigned char r = xd/yd;

  //BOOST_REQUIRE_EQUAL( 7, zd);

  BOOST_CHECK_EQUAL( (unsigned)r, (unsigned)zd);
}

BOOST_AUTO_TEST_CASE( bvudiv_3 )
{
  using namespace boost::logic;

  const char w = 6;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  bitvector z = new_bitvector(w);
  unsigned char xd, yd, zd;

  BOOST_REQUIRE( solve(ctx) );
  assumption( ctx, equal( bvudiv(x,y), z) );
  assumption( ctx, nequal( bvuint(0,w), y) );
  BOOST_REQUIRE( solve(ctx) );
  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  zd = read_value(ctx, z);

  BOOST_REQUIRE_NE( yd, 0);
  unsigned char r = xd/yd;

  BOOST_CHECK_EQUAL( (unsigned)r, (unsigned)zd);
}

BOOST_AUTO_TEST_CASE( bvsdiv_t )
{
  using namespace boost::logic;

  const char w = 8;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  bitvector z = new_bitvector(w);
  signed char xd, yd, zd, r;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, equal( bvsdiv(x,y), z) );
  assumption( ctx, nequal( bvsint(0,w), y) );
  assumption( ctx, equal( z, bvsint(7, w)) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  zd = read_value(ctx, z);

  BOOST_REQUIRE_NE( yd, 0);
  r = xd/yd;

  BOOST_REQUIRE_EQUAL( 7, zd);
  BOOST_CHECK_EQUAL( static_cast<int>(r), static_cast<int>(zd) );

  assumption( ctx, equal( bvsdiv(x,y), z) );
  assumption( ctx, nequal( bvsint(0,w), y) );
  assumption( ctx, equal( z, bvsint(-7, w)) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  zd = read_value(ctx, z);

  r = xd/yd;

  BOOST_REQUIRE_EQUAL( -7, zd);
  BOOST_CHECK_EQUAL( r, zd);
}

BOOST_AUTO_TEST_CASE( bvsdiv_39_5 )
{
  using namespace boost::logic;

  const char w = 8;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  bitvector z = new_bitvector(w);
  signed char xd, yd, zd, r;

  BOOST_REQUIRE( solve(ctx) );

  assertion ( ctx, equal( bvsdiv(x,y), z) );

  assumption( ctx, equal( bvsint(39, w), x) );
  assumption( ctx, equal( bvsint( 5, w), y) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  zd = read_value(ctx, z);

  BOOST_REQUIRE_NE( yd, 0);
  r = xd/yd;

  BOOST_CHECK_EQUAL(  5, static_cast<int>(yd) );
  BOOST_CHECK_EQUAL( 39, static_cast<int>(xd) );

  BOOST_REQUIRE_EQUAL( 7, static_cast<int>(r ) );
  BOOST_REQUIRE_EQUAL( 7, static_cast<int>(zd) );
  BOOST_CHECK_EQUAL( static_cast<int>(r), static_cast<int>(zd) );

  assumption( ctx, equal( bvsint(-39, w), x) );
  assumption( ctx, equal( bvsint( -5, w), y) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  zd = read_value(ctx, z);

  BOOST_REQUIRE_NE( yd, 0);
  r = xd/yd;

  BOOST_REQUIRE_EQUAL( 7, zd);
  BOOST_CHECK_EQUAL( static_cast<int>(r), static_cast<int>(zd) );

  assumption( ctx, equal( bvsint(39,w), x) );
  assumption( ctx, equal( bvsint(-5, w), y) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  zd = read_value(ctx, z);

  BOOST_REQUIRE_NE( yd, 0);
  r = xd/yd;

  BOOST_REQUIRE_EQUAL( -7, static_cast<int>(zd));
  BOOST_CHECK_EQUAL( static_cast<int>(r), static_cast<int>(zd) );

  assumption( ctx, equal( bvsint(-39,w), x) );
  assumption( ctx, equal( bvsint(5, w), y) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  zd = read_value(ctx, z);

  BOOST_REQUIRE_NE( yd, 0);
  r = xd/yd;

  BOOST_REQUIRE_EQUAL( -7, zd);
  BOOST_CHECK_EQUAL( static_cast<int>(r), static_cast<int>(zd) );
}

BOOST_AUTO_TEST_CASE( bvurem_t )
{
  using namespace boost::logic;

  const char w = 8;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  bitvector z = new_bitvector(w);
  unsigned char xd, yd, zd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, equal( bvurem(x,y), z) );
  assumption( ctx, nequal( bvuint(0,w), y) );
  assumption( ctx, equal( z, bvuint(17, w)) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  zd = read_value(ctx, z);

  BOOST_REQUIRE_NE( yd, 0);
  unsigned char r = xd%yd;

  BOOST_REQUIRE_EQUAL( 17, zd);
  BOOST_CHECK_EQUAL( r, zd);
}

BOOST_AUTO_TEST_CASE( bvsrem_t )
{
  using namespace boost::logic;

  const char w = 8;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  bitvector z = new_bitvector(w);
  signed char xd, yd, zd, rd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, equal(x, bvuint(3,w)) );
  assumption( ctx, equal(y, bvuint(2,w)) );
  assumption( ctx, equal(z, bvuint(1,w)) );
  assumption( ctx, equal(bvsrem(x, y), z) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  zd = read_value(ctx, z);

  BOOST_REQUIRE_NE( yd, 0);
  rd = xd % yd;
  BOOST_REQUIRE_EQUAL(zd, rd);

  assumption( ctx, equal(x, bvuint(-1,w)) );
  assumption( ctx, equal(y, bvuint(2,w)) );
  assumption( ctx, equal(z, bvuint(-1,w)) );
  assumption( ctx, equal(bvsrem(x, y), z) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  zd = read_value(ctx, z);

  BOOST_REQUIRE_NE( yd, 0);
  rd = xd % yd;
  BOOST_REQUIRE_EQUAL(zd, rd);
}

BOOST_AUTO_TEST_CASE( bvurem_2 )
{
  using namespace boost::logic;

  const char w = 8;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  bitvector z = new_bitvector(w);
  unsigned char xd, yd, zd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, equal( bvurem(x,y), z) );
  assumption( ctx, nequal( bvuint(0,w), y) );
  assumption( ctx, equal( z, bvuint(17, w)) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  zd = read_value(ctx, z);

  BOOST_REQUIRE_NE( yd, 0);
  unsigned char r = xd%yd;

  BOOST_REQUIRE_EQUAL( 17, zd);
  BOOST_CHECK_EQUAL( r, zd);
}

BOOST_AUTO_TEST_CASE( bvcomp_t )
{
  using namespace boost::logic;

  const char w = 8;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  char xd, yd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, equal( bvcomp(x,y), bit1) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);

  BOOST_CHECK_EQUAL(xd, yd);

  assumption( ctx, equal( bvcomp(x,y), bit0) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  BOOST_CHECK_NE(xd, yd);
}

BOOST_AUTO_TEST_CASE( bvslt_2 )
{
  using namespace boost::logic;

  const char w = 8;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  signed char xd, yd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, bvslt(x,y) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);

  BOOST_CHECK_LT(xd, yd);

  assumption( ctx, Not( bvslt(x,y) ) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);

  BOOST_CHECK_GE(xd, yd);
}

BOOST_AUTO_TEST_CASE( bvslt_t )
{
  using namespace boost::logic;

  const char w = 8;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  signed char xd, yd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, bvslt(x,y) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);

  BOOST_CHECK_LT(xd, yd);

  assumption( ctx, Not( bvslt(x,y) ) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  BOOST_CHECK_GE(xd, yd);
}

BOOST_AUTO_TEST_CASE( bvsgt_t )
{
  using namespace boost::logic;

  const char w = 8;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  signed char xd, yd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, bvsgt(x,y) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);

  BOOST_CHECK_GT(xd, yd);

  assumption( ctx, Not( bvsgt(x,y) ) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  BOOST_CHECK_LE(xd, yd);
}

BOOST_AUTO_TEST_CASE( bvsle_t )
{
  using namespace boost::logic;

  const char w = 8;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  signed char xd, yd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, bvsle(x,y) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);

  BOOST_CHECK_LE(xd, yd);

  assumption( ctx, Not( bvsle(x,y) ) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  BOOST_CHECK_GT(xd, yd);
}

BOOST_AUTO_TEST_CASE( bvsge_t )
{
  using namespace boost::logic;

  const char w = 8;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  signed char xd, yd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, bvsge(x,y) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);

  BOOST_CHECK_GE(xd, yd);

  assumption( ctx, Not( bvsge(x,y) ) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  BOOST_CHECK_LT(xd, yd);
}

BOOST_AUTO_TEST_CASE( bvult_t )
{
  using namespace boost::logic;

  const char w = 8;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  unsigned char xd, yd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, bvult(x,y) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);

  //printf("%u %u\n",xd, yd);
  BOOST_CHECK_LT(xd, yd);

  assumption( ctx, Not( bvult(x,y) ) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  BOOST_CHECK_GE(xd, yd);
  //printf("%u %u\n",xd, yd);
}

BOOST_AUTO_TEST_CASE( bvult_all )
{
  using namespace boost::logic;

  const char w = 4;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  unsigned xd, yd;

  BOOST_REQUIRE( solve(ctx) );

  assertion( ctx, bvult(x,y) );

  unsigned limit = 257;

  while( solve(ctx) ) {
    if( --limit == 0) break;
    xd = read_value(ctx, x);
    yd = read_value(ctx, y);
    //printf("%u %u\n",xd, yd);
    BOOST_REQUIRE_LT(xd, yd);

    assertion( ctx, Nand( equal(x, bvuint(xd,w)), equal(y, bvuint(yd, w)) ) );

  }
}

BOOST_AUTO_TEST_CASE( bvugt_t )
{
  using namespace boost::logic;

  const char w = 8;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  unsigned char xd, yd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, bvugt(x,y) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);

  BOOST_CHECK_GT(xd, yd);

  assumption( ctx, Not( bvugt(x,y) ) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  BOOST_CHECK_LE(xd, yd);
}

BOOST_AUTO_TEST_CASE( bvule_t )
{
  using namespace boost::logic;

  const char w = 8;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  unsigned char xd, yd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, bvule(x,y) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);

  BOOST_CHECK_LE(xd, yd);

  assumption( ctx, Not( bvule(x,y) ) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  BOOST_CHECK_GT(xd, yd);
}

BOOST_AUTO_TEST_CASE( bvule_70 )
{
  using namespace boost::logic;

  const char w = 8;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, bvule( bvuint(70,w), bvuint(70,w)) );
  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, Not(bvule( bvuint(70,w), bvuint(70,w)) ) );
  BOOST_REQUIRE( !solve(ctx) );

  assumption( ctx, bvule( bvuint(69,w), bvuint(70,w)) );
  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, Not(bvule( bvuint(69,w), bvuint(70,w)) ) );
  BOOST_REQUIRE( !solve(ctx) );

  assumption( ctx, bvule( bvuint(71,w), bvuint(70,w)) );
  BOOST_REQUIRE( !solve(ctx) );

  assumption( ctx, Not(bvule( bvuint(71,w), bvuint(70,w)) ) );
  BOOST_REQUIRE( solve(ctx) );
}

BOOST_AUTO_TEST_CASE( bvule_1 )
{
  using namespace boost::logic;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, bvule( bit1, bit1) );
  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, Not(bvule( bit1,bit1) ) );
  BOOST_REQUIRE( !solve(ctx) );

  assumption( ctx, bvule( bit0, bit1) );
  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, Not(bvule( bit0, bit1) ) );
  BOOST_REQUIRE( !solve(ctx) );

  assumption( ctx, bvule( bit1, bit0) );
  BOOST_REQUIRE( !solve(ctx) );

  assumption( ctx, Not(bvule( bit1, bit0) ));
  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, bvule( bit0, bit0) );
  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, Not(bvule( bit0, bit0) ));
  BOOST_REQUIRE( !solve(ctx) );

}

BOOST_AUTO_TEST_CASE( bvule_2 )
{
  using namespace boost::logic;

  const char w = 2;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, bvule( bvuint(2,w), bvuint(2,w)) );
  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, Not(bvule( bvuint(2,w), bvuint(2,w)) ) );
  BOOST_REQUIRE( !solve(ctx) );

  assumption( ctx, bvule( bvuint(1,w), bvuint(2,w)) );
  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, Not(bvule( bvuint(1,w), bvuint(2,w)) ) );
  BOOST_REQUIRE( !solve(ctx) );

  assumption( ctx, bvule( bvuint(3,w), bvuint(2,w)) );
  BOOST_REQUIRE( !solve(ctx) );

  assumption( ctx, Not(bvule( bvuint(3,w), bvuint(2,w)) ) );
  BOOST_REQUIRE( solve(ctx) );
}

BOOST_AUTO_TEST_CASE( bvuge_t )
{
  using namespace boost::logic;

  const char w = 8;

  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  unsigned char xd, yd;

  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, bvuge(x,y) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);

  BOOST_CHECK_GE(xd, yd);

  assumption( ctx, Not( bvuge(x,y) ) );
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  yd = read_value(ctx, y);
  BOOST_CHECK_LT(xd, yd);
}

BOOST_AUTO_TEST_CASE( predicate_bvslt )
{
   const char w = 8;

   bitvector x = new_bitvector(w);
   bitvector y = new_bitvector(w);
   predicate p = new_variable();
   predicate q = new_variable();
   bool pd;
   bool qd;

   BOOST_REQUIRE( solve(ctx) );

   assertion( ctx, equal(p, bvslt(x,y)) );
   assertion( ctx, equal(p, True) );
   BOOST_REQUIRE( solve(ctx) );

   pd = read_value(ctx, p);

   BOOST_CHECK_EQUAL(pd, true);

   assertion( ctx, equal(q, Not( bvslt(x,y) ) ) );
   BOOST_REQUIRE( solve(ctx) );

   qd = read_value(ctx, q);

   BOOST_CHECK_EQUAL(qd, false);
}

BOOST_AUTO_TEST_CASE( uninitialized_readt )
{
   const char w = 8;

   bitvector x = new_bitvector(w);
   signed char xd;

   BOOST_REQUIRE( solve(ctx) );

   xd = read_value(ctx, x);

   BOOST_CHECK_EQUAL(xd, xd);
}

/**
 * this tests triggers an invalid optimization
 * is SWORD, the constrained is preprocessed,
 * found to constant true and replaced with
 * true. Therefore no assignment for the
 * variables is generated.
 */
BOOST_AUTO_TEST_CASE( keeps_tiny_assertions )
{
   const char w = 32;

   bitvector retval = new_bitvector(w);
   assertion( ctx, equal(retval, retval));

   BOOST_REQUIRE( solve(ctx) );

   bitvector tmp0 = new_bitvector(w);
   assertion( ctx, equal(tmp0, tmp0));

   BOOST_REQUIRE( solve(ctx) );

   bitvector x = new_bitvector(w);
   assertion( ctx, equal(x, x));

   assertion( ctx, equal(bvuint(3, w), x));

   BOOST_REQUIRE( solve(ctx) );

   bitvector tmp1 = new_bitvector(w);
   assertion( ctx, equal(tmp1, x));

   assertion( ctx, equal(tmp1, tmp0));

   BOOST_REQUIRE( solve(ctx) );

   unsigned xd = read_value(ctx, x);
   unsigned tmp0d = read_value(ctx, tmp0);
   unsigned tmp1d = read_value(ctx, tmp1);

   BOOST_CHECK_EQUAL(xd, 3u);
   BOOST_CHECK_EQUAL(tmp0d, 3u);
   BOOST_CHECK_EQUAL(tmp1d, 3u);
}

BOOST_AUTO_TEST_CASE( keeps_small_assertions )
{
   const char w = 32;

   bitvector tmp0   = new_bitvector(w);
   bitvector tmp1   = new_bitvector(w);
   bitvector x      = new_bitvector(w);
   BOOST_REQUIRE( solve(ctx) );

   assertion( ctx, And(And( equal(bvuint(3, w), x),
                   equal(tmp1, tmp0)),
                   equal(tmp1, x)));

   BOOST_REQUIRE( solve(ctx) );

   unsigned xd = read_value(ctx, x);
   unsigned tmp0d = read_value(ctx, tmp0);
   unsigned tmp1d = read_value(ctx, tmp1);

   BOOST_CHECK_EQUAL(xd, 3u);
   BOOST_CHECK_EQUAL(tmp0d, 3u);
   BOOST_CHECK_EQUAL(tmp1d, 3u);
}

BOOST_AUTO_TEST_CASE( negative_multiply_t2 )
{
   using namespace boost::logic;

   const unsigned w = 8;

   bitvector x = new_bitvector(w);
   bitvector y = new_bitvector(w);
   bitvector z = new_bitvector(w);

   BOOST_REQUIRE( solve(ctx) );

   assertion( ctx, equal( y, bvsint( -1, w) ));
   assertion( ctx, equal( z, bvsint(  1, w) ));
   assertion( ctx, equal( x, bvmul (  z, y) ));
   BOOST_REQUIRE( solve(ctx) );

   signed char xd = read_value(ctx, x);
   signed char yd = read_value(ctx, y);
   signed char zd = read_value(ctx, z);
   BOOST_CHECK_EQUAL( yd, -1 );
   BOOST_CHECK_EQUAL( zd,  1 );
   BOOST_CHECK_EQUAL( xd, -1 );
}

BOOST_AUTO_TEST_CASE( ite_t )
{
   using namespace boost::logic;

   const unsigned w = 8;

   predicate c = new_variable();
   bitvector x = new_bitvector(w);
   bitvector y = new_bitvector(w);
   bitvector z = new_bitvector(w);

   BOOST_REQUIRE( solve(ctx) );

   assertion( ctx, equal( x, Ite( c, y, z) ));

   assumption( ctx, c);

   BOOST_REQUIRE( solve(ctx) );

   bool b = read_value(ctx, c);
   int xd = read_value(ctx, x);
   int yd = read_value(ctx, y);
   int zd = read_value(ctx, z);

   BOOST_CHECK_EQUAL( b, true );
   BOOST_CHECK_EQUAL( xd, yd );

   assumption( ctx, Not(c) );

   BOOST_REQUIRE( solve(ctx) );

   b  = read_value(ctx, c);
   xd = read_value(ctx, x);
   yd = read_value(ctx, y);
   zd = read_value(ctx, z);

   BOOST_CHECK_EQUAL( b, false );
   BOOST_CHECK_EQUAL( xd, zd );
}

BOOST_AUTO_TEST_CASE( concat_extract_x )
{
   const unsigned w = 8;

   bitvector x = new_bitvector(w);
   bitvector y = new_bitvector(w);

   BOOST_REQUIRE( solve(ctx) );

   assertion( ctx, equal( x, extract( 7, 0, concat(x, y) )) );

   BOOST_REQUIRE( solve(ctx) );

   assertion( ctx, nequal( x, extract( 7, 0, concat(x, y) )) );

   BOOST_REQUIRE( !solve(ctx) );
}

BOOST_AUTO_TEST_CASE( concat_extract_y )
{
   const unsigned w = 8;

   bitvector x = new_bitvector(w);
   bitvector y = new_bitvector(w);
   assertion( ctx, equal( y, extract( 15, 8, concat(x, y) )) );

   BOOST_REQUIRE( solve(ctx) );

   assertion( ctx, nequal( y, extract( 15, 8, concat(x, y) )) );

   BOOST_REQUIRE( !solve(ctx) );
}

BOOST_AUTO_TEST_CASE( bvshl_t )
{
  const unsigned w = 128u;

  bitvector x = new_bitvector(w);
  BOOST_REQUIRE( solve(ctx) );

  assertion( ctx, equal( bvmul(x, bvuint(2,w)), bvshl(x, bvuint(2,w))) );
  BOOST_REQUIRE( solve(ctx) );

  assertion( ctx, nequal( bvmul(x, bvuint(2,w)), bvshl(x, bvuint(2,w))) );
  BOOST_REQUIRE( !solve(ctx) );
}

BOOST_AUTO_TEST_CASE( bvshl_all )
{
  const unsigned w = 16;
  bitvector x = new_bitvector(w);
  bitvector y = new_bitvector(w);
  bitvector z = new_bitvector(w);

  const unsigned xlimit = 256;
  const unsigned ylimit = w;
  BOOST_REQUIRE( solve(ctx) );
  assertion(ctx, equal( z, bvshl( x, y)) );
  assertion(ctx, bvult(x,bvuint(xlimit,w)));
  assertion(ctx, bvult(y,bvuint(ylimit,w)));

  unsigned limit = xlimit*ylimit+1;

  while( solve(ctx) ) {
    if( --limit == 0) break;
    unsigned xd = read_value(ctx, x);
    unsigned yd = read_value(ctx, y);
    unsigned zd = read_value(ctx,z);

    //printf("before shifting: %d %u %u %u\n",limit, xd, yd, zd);

    unsigned shifted = (xd << yd) %(1ul << w);

    //printf("after shifting: %d %u %u %u\n",limit, xd, yd, zd);

    //std::cout << "x: " <<read_value(ctx,x) << std::endl;
    //std::cout << "y: " <<read_value(ctx,y) << std::endl;
    //std::cout << "z: " <<read_value(ctx,z) << std::endl;
    BOOST_REQUIRE_EQUAL(shifted, zd);

    //printf("test: %d %u %u %u\n",limit, xd, yd, zd);

    assertion( ctx, Nand( equal(x, bvuint(xd,w)), equal(y, bvuint(yd, w)) ) );

  }
  BOOST_REQUIRE_EQUAL(limit,1u);
}

BOOST_AUTO_TEST_CASE( bvshl_128_30 )
{
   const unsigned w = 32;
   bitvector x = new_bitvector(w);
   bitvector y = new_bitvector(w);
   bitvector z = new_bitvector(w);

   BOOST_REQUIRE( solve(ctx) );

   assumption(ctx, equal( z, bvshl( x, y)) );
   assumption(ctx, equal(x, bvuint(128,w)));
   assumption(ctx, equal(y, bvuint( 30,w)));
   assumption(ctx, equal(z, bvuint(  0,w)));
   BOOST_REQUIRE( solve(ctx) );

   assumption(ctx, nequal( z, bvshl( x, y)) );
   assumption(ctx, equal(x, bvuint(128,w)));
   assumption(ctx, equal(y, bvuint( 30,w)));
   assumption(ctx, equal(z, bvuint(  0,w)));
   BOOST_REQUIRE( !solve(ctx) );
}

BOOST_AUTO_TEST_CASE( bvshr_t )
{
  const unsigned w = 128u;
  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, equal( bvuint(1,w), bvshr(bvuint(8,w), bvuint(3, w))) );
  BOOST_REQUIRE( solve(ctx) );

  assumption( ctx, nequal( bvuint(1,w), bvshr(bvuint(8,w), bvuint(3, w))) );
  BOOST_REQUIRE( !solve(ctx) );
}

BOOST_AUTO_TEST_CASE( bvashr_t )
{
   const unsigned w = 32;
   bitvector x = new_bitvector(w);
   BOOST_REQUIRE( solve(ctx) );

   assumption( ctx, equal( bvsint(-1,w), bvashr(bvsint(-1,w), x)) );
   BOOST_REQUIRE( solve(ctx) );

   assumption( ctx, nequal( bvsint(-1,w), bvashr(bvsint(-1,w), x)) );
   BOOST_REQUIRE( !solve(ctx) );
}

BOOST_AUTO_TEST_CASE( zero_extend_t )
{
   const unsigned w = 8;
   BOOST_REQUIRE( solve(ctx) );

   assertion( ctx, equal( zero_extend(w, bvuint(255,w)), bvuint(255,2*w)));
   BOOST_REQUIRE( solve(ctx) );
}

BOOST_AUTO_TEST_CASE( sign_extend_t )
{
   const unsigned w = 8;
   BOOST_REQUIRE( solve(ctx) );

   assertion( ctx, equal( sign_extend(w, bvsint(-1,w)), bvsint(-1,2*w)));
   BOOST_REQUIRE( solve(ctx) );
}

BOOST_AUTO_TEST_CASE( evaluate_concat )
{
  const unsigned w = 8;
  BOOST_REQUIRE( solve(ctx) );

  bitvector x = new_bitvector(w);
  bitvector x2 = new_bitvector(2*w);

  ContextType::result_type cc = evaluate( ctx, concat(x,x) );

  metaSMT::assertion( ctx, metaSMT::logic::equal( x2, cc) );
  BOOST_REQUIRE( solve(ctx) );
}

BOOST_AUTO_TEST_CASE( read_value_constant )
{
  const unsigned w = 32;
  bitvector x = new_bitvector(w);

  BOOST_REQUIRE( solve(ctx) );

  metaSMT::assumption( ctx, metaSMT::logic::equal( x, bvuint(13,w) ) );
  BOOST_REQUIRE( solve(ctx) );
  unsigned i = read_value(ctx, x);
  BOOST_CHECK_EQUAL( i, 13u);

  std::string s = read_value(ctx, x);
  BOOST_CHECK_EQUAL( s, "00000000000000000000000000001101");

}

BOOST_AUTO_TEST_CASE( bvint_t )
{
  const unsigned w = 32;

  BOOST_REQUIRE( solve(ctx) );
  metaSMT::assumption( ctx, metaSMT::logic::equal( bvuint(13,w), bvint(13u,w) ) );
  BOOST_REQUIRE( solve(ctx) );

  metaSMT::assumption( ctx, metaSMT::logic::nequal( bvuint(13,w), bvint(13u,w) ) );
  BOOST_REQUIRE( !solve(ctx) );

  metaSMT::assumption( ctx, metaSMT::logic::equal( bvsint(13,w), bvint(13,w) ) );
  BOOST_REQUIRE( solve(ctx) );

  metaSMT::assumption( ctx, metaSMT::logic::nequal( bvsint(13,w), bvint(13,w) ) );
  BOOST_REQUIRE( !solve(ctx) );

  metaSMT::assumption( ctx, metaSMT::logic::equal( bvsint(-13,w), bvint(-13,w) ) );
  BOOST_REQUIRE( solve(ctx) );

  metaSMT::assumption( ctx, metaSMT::logic::nequal( bvsint(-13,w), bvint(-13,w) ) );
  BOOST_REQUIRE( !solve(ctx) );
}

BOOST_AUTO_TEST_CASE( bvuint_t )
{
  BOOST_REQUIRE( solve(ctx) );

  metaSMT::assumption( ctx, metaSMT::logic::equal( bvuint(123,8),
    bvbin("01111011")
  ));
  BOOST_REQUIRE( solve(ctx) );

  metaSMT::assumption( ctx, metaSMT::logic::equal( bvuint(0x5F55,16),
    bvbin("01011111" "01010101")
  ));
  BOOST_REQUIRE( solve(ctx) );

  metaSMT::assumption( ctx, metaSMT::logic::equal( bvuint(0x555F55F5,32),
    bvbin("01010101" "01011111" "01010101" "11110101")
  ));
  BOOST_REQUIRE( solve(ctx) );

  metaSMT::assumption( ctx, metaSMT::logic::equal( bvuint(0x555F55555F55F555ul,64),
    bvbin("01010101" "01011111" "01010101" "01010101"
          "01011111" "01010101" "11110101" "01010101")
  ));
  BOOST_REQUIRE( solve(ctx) );

  metaSMT::assumption( ctx, metaSMT::logic::equal( bvuint(0xF5555F555555F555ul,128),
    bvbin("00000000" "00000000" "00000000" "00000000"
          "00000000" "00000000" "00000000" "00000000"
          "11110101" "01010101" "01011111" "01010101"
          "01010101" "01010101" "11110101" "01010101")
  ));
  BOOST_REQUIRE( solve(ctx) );
}

BOOST_AUTO_TEST_CASE( bvsint_t )
{
  BOOST_REQUIRE( solve(ctx) );

  metaSMT::assumption( ctx, metaSMT::logic::equal( bvsint(123,8),
    bvbin("01111011")
  ));
  BOOST_REQUIRE( solve(ctx) );

  metaSMT::assumption( ctx, metaSMT::logic::equal( bvsint(0x5F55,16),
    bvbin("01011111" "01010101")
  ));
  BOOST_REQUIRE( solve(ctx) );

  metaSMT::assumption( ctx, metaSMT::logic::equal( bvsint(0x555F55F5,32),
    bvbin("01010101" "01011111" "01010101" "11110101")
  ));
  BOOST_REQUIRE( solve(ctx) );

  metaSMT::assumption( ctx, metaSMT::logic::equal( bvsint(0x555F55555F55F555ul,64),
    bvbin("01010101" "01011111" "01010101" "01010101"
          "01011111" "01010101" "11110101" "01010101")
  ));
  BOOST_REQUIRE( solve(ctx) );

  metaSMT::assumption( ctx, metaSMT::logic::equal( bvsint(0xF5555F555555F555ul,128),
    bvbin("11111111" "11111111" "11111111" "11111111"
          "11111111" "11111111" "11111111" "11111111"
          "11110101" "01010101" "01011111" "01010101"
          "01010101" "01010101" "11110101" "01010101")
  ));
  BOOST_REQUIRE( solve(ctx) );

  metaSMT::assumption( ctx, metaSMT::logic::equal( bvsint(-123,8),
    bvbin("10000101")
  ));
  BOOST_REQUIRE( solve(ctx) );

  metaSMT::assumption( ctx, metaSMT::logic::equal( bvsint(-123,16),
    bvbin("11111111" "10000101")
  ));
  BOOST_REQUIRE( solve(ctx) );

  metaSMT::assumption( ctx, metaSMT::logic::equal( bvsint(-123,32),
    bvbin("11111111" "11111111" "11111111" "10000101")
  ));
  BOOST_REQUIRE( solve(ctx) );

  metaSMT::assumption( ctx, metaSMT::logic::equal( bvsint(-123,64),
    bvbin("11111111" "11111111" "11111111" "11111111"
          "11111111" "11111111" "11111111" "10000101")
  ));
  BOOST_REQUIRE( solve(ctx) );

  metaSMT::assumption( ctx, metaSMT::logic::equal( bvsint(-123,128),
    bvbin("11111111" "11111111" "11111111" "11111111"
          "11111111" "11111111" "11111111" "11111111"
          "11111111" "11111111" "11111111" "11111111"
          "11111111" "11111111" "11111111" "10000101")
  ));
  BOOST_REQUIRE( solve(ctx) );
}

BOOST_AUTO_TEST_CASE( variable_equality )
{
  unsigned const width = 8;
  bitvector x = new_bitvector(width);
  bitvector y = new_bitvector(width);

  bool cmp = (x == x);
  BOOST_CHECK( cmp );

  cmp = (x == y);
  BOOST_CHECK( !cmp );

  cmp = (y == x);
  BOOST_CHECK( !cmp );
}

BOOST_AUTO_TEST_CASE( constant_64bit )
{
  unsigned const w = 64;
  unsigned long const value = std::numeric_limits<unsigned long>::max();
  bitvector x = new_bitvector(w);

  assumption(ctx, equal(x, bvuint(value, w)));
  BOOST_REQUIRE( solve(ctx) );

  unsigned long xd = read_value(ctx, x);
  std::string xs = read_value(ctx, x);
  BOOST_CHECK_EQUAL(xd, value);
  BOOST_CHECK_EQUAL(xs, "1111111111111111111111111111111111111111111111111111111111111111");

  assumption(ctx, equal(x, bvuint(value-123, w)));
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  BOOST_CHECK_EQUAL(xd, value-123);
}

BOOST_AUTO_TEST_CASE( constant_69bit )
{
  bitvector x = new_bitvector(69);

  const std::string longconst ("001111001110010101010100000000000000000000000000000000000000000000000");
  assumption(ctx, equal( x,  bvbin(longconst) ) );
  BOOST_REQUIRE( solve(ctx) );
  std::string longval = read_value(ctx, x);
  BOOST_REQUIRE_EQUAL( longconst, longval);
}

BOOST_AUTO_TEST_CASE( signed_constant_64bit )
{
  unsigned const w = 64;
  long value = std::numeric_limits<long>::max();
  bitvector x = new_bitvector(w);

  assumption(ctx, equal(x, bvsint(value, w)));
  BOOST_REQUIRE( solve(ctx) );

  std::string xs = read_value(ctx, x);
  BOOST_CHECK_EQUAL(xs, "0111111111111111111111111111111111111111111111111111111111111111");

  long xd = read_value(ctx, x);
  BOOST_CHECK_EQUAL(xd, value);

  assumption(ctx, equal(x, bvsint(value-123, w)));
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  BOOST_CHECK_EQUAL(xd, value-123);

  value = std::numeric_limits<long>::min();

  assumption(ctx, equal(x, bvsint(value, w)));
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  BOOST_CHECK_EQUAL(xd, value);

  assumption(ctx, equal(x, bvsint(value+123, w)));
  BOOST_REQUIRE( solve(ctx) );

  xd = read_value(ctx, x);
  BOOST_CHECK_EQUAL(xd, value+123);

}

BOOST_AUTO_TEST_CASE(extended_concat) {

  // b[..] == b
  bitvector b_1   = new_bitvector(8);
  bitvector b_0_1 = new_bitvector(4);
  bitvector b_1_1 = new_bitvector(4);
  assertion(ctx, equal( concat( b_1_1, b_0_1), b_1) );

  bitvector a_1   = new_bitvector(8);
  bitvector a_0_1 = new_bitvector(4);
  bitvector a_1_1 = new_bitvector(4);

  // a[..] == a
  assertion(ctx, equal( concat( a_1_1, a_0_1), a_1) );

  bitvector z_1   = new_bitvector(16);
  bitvector z_0_1 = new_bitvector(4);
  bitvector z_1_1 = new_bitvector(4);
  bitvector z_2_1 = new_bitvector(4);
  bitvector z_3_1 = new_bitvector(4);

  // z[..] == z
  assertion(ctx, equal(
    concat( z_3_1,
    concat( z_2_1,
    concat( z_1_1,
            z_0_1))),
    z_1
  ));

  bitvector buf_3_o_0_1 = new_bitvector(4);
  bitvector buf_3_o_1_1 = new_bitvector(4);
  bitvector buf_3_o_2_1 = new_bitvector(4);
  bitvector buf_3_o_3_1 = new_bitvector(4);

  // (a[..],b[..]) == buf
  assertion(ctx, equal(

    concat( a_1_1,
    concat( a_0_1,
    concat( b_1_1,
            b_0_1))),

    concat( buf_3_o_3_1,
    concat( buf_3_o_2_1,
    concat( buf_3_o_1_1,
            buf_3_o_0_1)))
  ));

  // buf[..] == z[..]
  assertion(ctx, equal(

    concat( buf_3_o_3_1,
    concat( buf_3_o_2_1,
    concat( buf_3_o_1_1,
            buf_3_o_0_1))),

    concat( z_3_1,
    concat( z_2_1,
    concat( z_1_1,
            z_0_1)))

  ));

  BOOST_REQUIRE( solve(ctx) );

  // a = 00
  // b = 00
  assumption(ctx, equal(a_1, bvbin("00000000")));
  assumption(ctx, equal(b_1, bvbin("00000000")));

  BOOST_REQUIRE( solve(ctx) );

  std::string av = read_value(ctx, a_1);
  std::string bv = read_value(ctx, b_1);
  std::string zv = read_value(ctx, z_1);

  BOOST_REQUIRE_EQUAL( av, "00000000" );
  BOOST_REQUIRE_EQUAL( bv, "00000000" );
  BOOST_REQUIRE_EQUAL( zv, "0000000000000000" );

  // a = 00
  // b = 01
  assumption(ctx, equal(a_1, bvbin("00000000")));
  assumption(ctx, equal(b_1, bvbin("00000001")));

  BOOST_REQUIRE( solve(ctx) );

  std::string av2 = read_value(ctx, a_1);
  std::string bv2 = read_value(ctx, b_1);
  std::string zv2 = read_value(ctx, z_1);

  BOOST_REQUIRE_EQUAL( av2, "00000000" );
  BOOST_REQUIRE_EQUAL( bv2, "00000001" );
  BOOST_REQUIRE_EQUAL( zv2, "0000000000000001" );

  // a = 01
  // b = 00
  assumption(ctx, equal(a_1, bvbin("00000001")));
  assumption(ctx, equal(b_1, bvbin("00000000")));

  BOOST_REQUIRE( solve(ctx) );

  std::string av3 = read_value(ctx, a_1);
  std::string bv3 = read_value(ctx, b_1);
  std::string zv3 = read_value(ctx, z_1);

  BOOST_REQUIRE_EQUAL( av3, "00000001" );
  BOOST_REQUIRE_EQUAL( bv3, "00000000" );
  BOOST_REQUIRE_EQUAL( zv3, "0000000100000000" );
}

BOOST_AUTO_TEST_SUITE_END() //QF_BV

//  vim: ft=cpp:ts=2:sw=2:expandtab


// Software License for MTL
//
// Copyright (c) 2007 The Trustees of Indiana University.
//               2008 Dresden University of Technology and the Trustees of Indiana University.
//               2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also license.mtl.txt in the distribution.

// With contributions from Cornelius Steinhardt

#ifndef MTL_MATRIX_HOUSEHOLDER_INCLUDE
#define MTL_MATRIX_HOUSEHOLDER_INCLUDE

#include <cmath>
#include <cassert>
#include <boost/numeric/mtl/mtl_fwd.hpp>
#include <boost/numeric/mtl/vector/parameter.hpp>
#include <boost/numeric/mtl/concept/collection.hpp>
#include <boost/numeric/mtl/interface/vpt.hpp>

namespace mtl { namespace vector {

/// Computes Householder vector v and scalar b for vector \p y
/** such that identity_matrix(size(y))-b*v*v' projects the vector y
    to a positive multiple of the first unit vector. **/
template <typename Vector>
std::pair<typename mtl::vector::dense_vector<typename Collection<Vector>::value_type, parameters<> >, typename Collection<Vector>::value_type>
inline householder(Vector& y)
{
    vampir_trace<2004> tracer;
    assert(size(y) > 0);
    typedef typename  Collection<Vector>::value_type   value_type;
    // typedef typename  Collection<Vector>::size_type    size_type;
    const value_type  zero= math::zero(y[0]), one= math::one(y[0]);

    Vector            v(y);
    v[0]= one;
    irange            tail(1, imax);
    value_type        s( dot(v[tail], v[tail]) ), b, v0;

    //evaluation of v and b
    if (s == zero)
        b= zero;
    else {
	value_type mu= sqrt(y[0] * y[0] + s);
	v0= v[0]= y[0] <= zero ? y[0] - mu : -s / (y[0] + mu); // complex < zero????
	b= 2 * v0 * v0 / (s + v0 * v0);                        // 2* complex???????
	v/= v0;                                                // normalization of the first entry
    }

    return std::make_pair(v,b);
}

/// Computes Householder vector for vector \p y
/** More stable Householder transformation, also for non-square matrices. **/
template <typename Vector>
typename mtl::vector::dense_vector<typename Collection<Vector>::value_type, parameters<> >
inline householder_s(Vector& y)
{
    vampir_trace<2005> tracer;
    typedef typename  Collection<Vector>::value_type   value_type;
    const value_type  zero= math::zero(y[0]);

    Vector            u(y);
    value_type        nu(sqrt( dot(u, u) )), s;

    if (nu != zero){
	if(u[0] < 0){
		s= -1;
	} else {
		s= 1;
	}
	u[0]= u[0] + s * nu;
	u/= sqrt( dot(u, u) );
    }

    return u;
}

}} // namespace mtl::matrix

#endif // MTL_MATRIX_HOUSEHOLDER_INCLUDE


#pragma once
#include <string>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_bind.hpp>

using namespace boost::spirit::qi;
using namespace std;
using namespace boost::spirit;

template <typename Iterator, typename NumType>
struct CalcGrammar : public grammar<Iterator, NumType(), space_type>
{
	CalcGrammar() : CalcGrammar::base_type(exp_)
	{
		real_parser<NumType, real_policies<NumType> > rp_;
		factor_ = rp_[_val = _1]
			| ('-' >> factor_)[_val = -_1]
			| ('+' >> factor_)[_val = _1]
			| ('(' >> exp_[_val = _1] >> ')');

		term_ = factor_[_val = _1] >>
			*(('*' >> factor_[_val *= _1])
			| ('/' >> factor_[_val /= _1]));

		exp_ = term_[_val = _1] >>
			*(('+' >> term_[_val += _1])
			| ('-' >> term_[_val -= _1]));
	}
	typedef rule<Iterator, NumType(), space_type> rule_t;
	rule_t factor_, term_, exp_;
};

template<class _NumType = double>
class Calculator
{
public:
	Calculator(void){}
	virtual ~Calculator(void){}
public:
	typedef _NumType NumType;
public:
	_NumType calc(const std::string& input);
	_NumType calc(const std::string& input,bool& isSuccessed,const char*& lastErr);
	const char* getLastErr(){ return lastErr_.c_str(); }
protected:
	CalcGrammar<std::string::const_iterator, _NumType> gram_;
	std::string lastErr_;
};
template<class _NumType>
_NumType Calculator<_NumType>::calc(const std::string& input)
{
	bool isSuccess;
	const char* lastErr = nullptr;
	return calc(input, isSuccess, lastErr);
}

template<class _NumType>
_NumType Calculator<_NumType>::calc(const std::string& input, bool& isSuccessed, const char*& lastErr)
{
	_NumType val = 0;
	std::string::const_iterator startPos = input.begin();
	auto r = phrase_parse(startPos, input.end(), gram_, space, val);
	if (r && startPos == input.end())
	{
		lastErr_.clear();
		isSuccessed = true;
		lastErr = "";
		return val;
	}
	else
	{
		int num = input.end() - startPos;
		lastErr_ = std::string(num, '-') + "^";
		isSuccessed = false;
		lastErr = lastErr_.c_str();
	}
}

int main()
{
	Calculator<> calc;
	std::string input;
	printf("input here\n");
	while (true)
	{
		getline(cin,input);
		if (input == "")
			break;
		int val = calc.calc(input.c_str());
		if (calc.getLastErr()[0] != 0)
			printf("%s\nWrong\n\n", calc.getLastErr());
		else
			printf("%d\n\n", val);
	}
}


//
// Copyright (c) 2019 CNRS
//

#ifndef __pinocchio_math_rotation_hpp__
#define __pinocchio_math_rotation_hpp__

#include "pinocchio/fwd.hpp"
#include "pinocchio/math/matrix.hpp"
#include <Eigen/Core>

namespace pinocchio
{
  ///
  /// \brief Computes a rotation matrix from a vector and values of sin and cos
  ///        orientations values.
  ///
  /// \remarks This code is issue from Eigen::AxisAngle::toRotationMatrix
  ///
  template<typename Vector3, typename Scalar, typename Matrix3>
  void toRotationMatrix(const Eigen::MatrixBase<Vector3> & axis,
                        const Scalar & cos_value, const Scalar & sin_value,
                        const Eigen::MatrixBase<Matrix3> & res)
  {
    EIGEN_STATIC_ASSERT_VECTOR_SPECIFIC_SIZE(Vector3,3);
    EIGEN_STATIC_ASSERT_MATRIX_SPECIFIC_SIZE(Matrix3,3,3);

    assert(isUnitary(axis) && "The axis is not unitary.");

    Matrix3 & res_ = PINOCCHIO_EIGEN_CONST_CAST(Matrix3,res);
    Vector3 sin_axis  = sin_value * axis;
    Vector3 cos1_axis = (Scalar(1)-cos_value) * axis;

    Scalar tmp;
    tmp = cos1_axis.x() * axis.y();
    res_.coeffRef(0,1) = tmp - sin_axis.z();
    res_.coeffRef(1,0) = tmp + sin_axis.z();

    tmp = cos1_axis.x() * axis.z();
    res_.coeffRef(0,2) = tmp + sin_axis.y();
    res_.coeffRef(2,0) = tmp - sin_axis.y();

    tmp = cos1_axis.y() * axis.z();
    res_.coeffRef(1,2) = tmp - sin_axis.x();
    res_.coeffRef(2,1) = tmp + sin_axis.x();

    res_.diagonal() = (cos1_axis.cwiseProduct(axis)).array() + cos_value;
  }
}

#endif //#ifndef __pinocchio_math_rotation_hpp__


// test_root.cpp
// (c) Tivole

#include <boost/test/unit_test.hpp>
#include "../src/numerary.hpp"

namespace numerary
{

    BOOST_AUTO_TEST_SUITE(TestRootFunctions)

    BOOST_AUTO_TEST_CASE(test_root_bisection)
    {
        const double eps = 1.e-9;
        double answer;
        double expected_answer;
        short int result;
        short int expected_result;

        // Testing sin function
        expected_result = 1;
        expected_answer = 0.0f;
        result = Numerary::root(sin, -3, 3, &answer, "bisection", eps);
        BOOST_CHECK_EQUAL(result, expected_result);
        BOOST_CHECK(fabs(answer - expected_answer) < 1.e-7);

	    // Testing log function
        expected_result = 1;
        expected_answer = 1.0f;
        result = Numerary::root(log, 0, 3, &answer, "bisection", eps);
        BOOST_CHECK_EQUAL(result, expected_result);
        BOOST_CHECK(fabs(answer - expected_answer) < 1.e-7);
    }

    BOOST_AUTO_TEST_CASE(test_root_secant)
    {
        const double eps = 1.e-9;
        double answer;
        double expected_answer;
        short int result;
        short int expected_result;

        // Testing sin function
        expected_result = 1;
        expected_answer = 0.0f;
        result = Numerary::root(sin, -3, 3, &answer, "secant", eps);
        BOOST_CHECK_EQUAL(result, expected_result);
        BOOST_CHECK(fabs(answer - expected_answer) < 1.e-7);

	    // Testing cos function
        expected_result = 0;
        result = Numerary::root(cos, -3, 3, &answer, "secant", eps);
        BOOST_CHECK_EQUAL(result, expected_result);
    }

    BOOST_AUTO_TEST_SUITE_END()
}


#include <NTL/FFT.h>

#include <NTL/new.h>

NTL_START_IMPL

FFTTablesType FFTTables;
// a truly GLOBAL variable, shared among all threads

long IsFFTPrime(long n, long& w)
{
   long  m, x, y, z;
   long j, k;

   if (n <= 1 || n >= NTL_SP_BOUND) return 0;

   if (n % 2 == 0) return 0;

   if (n % 3 == 0) return 0;

   if (n % 5 == 0) return 0;

   if (n % 7 == 0) return 0;

   m = n - 1;
   k = 0;
   while ((m & 1) == 0) {
      m = m >> 1;
      k++;
   }

   for (;;) {
      x = RandomBnd(n);

      if (x == 0) continue;
      z = PowerMod(x, m, n);
      if (z == 1) continue;

      x = z;
      j = 0;
      do {
         y = z;
         z = MulMod(y, y, n);
         j++;
      } while (j != k && z != 1);

      if (z != 1 || y !=  n-1) return 0;

      if (j == k)
         break;
   }

   /* x^{2^k} = 1 mod n, x^{2^{k-1}} = -1 mod n */

   long TrialBound;

   TrialBound = m >> k;
   if (TrialBound > 0) {
      if (!ProbPrime(n, 5)) return 0;

      /* we have to do trial division by special numbers */

      TrialBound = SqrRoot(TrialBound);

      long a, b;

      for (a = 1; a <= TrialBound; a++) {
         b = (a << k) + 1;
         if (n % b == 0) return 0;
      }
   }

   /* n is an FFT prime */

   for (j = NTL_FFTMaxRoot; j < k; j++)
      x = MulMod(x, x, n);

   w = x;
   return 1;
}

static
void NextFFTPrime(long& q, long& w, long index)
{
   static long m = NTL_FFTMaxRootBnd + 1;
   static long k = 0;
   // m and k are truly GLOBAL variables, shared among
   // all threads.  Access is protected by a critical section
   // guarding FFTTables

   static long last_index = -1;
   static long last_m = 0;
   static long last_k = 0;

   if (index == last_index) {
      // roll back m and k...part of a simple error recovery
      // strategy if an exception was thrown in the last
      // invocation of UseFFTPrime...probably of academic
      // interest only

      m = last_m;
      k = last_k;
   }
   else {
      last_index = index;
      last_m = m;
      last_k = k;
   }

   long t, cand;

   for (;;) {
      if (k == 0) {
         m--;
         if (m < 5) ResourceError("ran out of FFT primes");
         k = 1L << (NTL_SP_NBITS-m-2);
      }

      k--;

      cand = (1L << (NTL_SP_NBITS-1)) + (k << (m+1)) + (1L << m) + 1;

      if (!IsFFTPrime(cand, t)) continue;
      q = cand;
      w = t;
      return;
   }
}

long CalcMaxRoot(long p)
{
   p = p-1;
   long k = 0;
   while ((p & 1) == 0) {
      p = p >> 1;
      k++;
   }

   if (k > NTL_FFTMaxRoot)
      return NTL_FFTMaxRoot;
   else
      return k;
}

void InitFFTPrimeInfo(FFTPrimeInfo& info, long q, long w, bool bigtab)
{
   double qinv = 1/((double) q);

   long mr = CalcMaxRoot(q);

   info.q = q;
   info.qinv = qinv;
   info.zz_p_context = 0;

   info.RootTable.SetLength(mr+1);
   info.RootInvTable.SetLength(mr+1);
   info.TwoInvTable.SetLength(mr+1);
   info.TwoInvPreconTable.SetLength(mr+1);

   long *rt = &info.RootTable[0];
   long *rit = &info.RootInvTable[0];
   long *tit = &info.TwoInvTable[0];
   mulmod_precon_t *tipt = &info.TwoInvPreconTable[0];

   long j;
   long t;

   rt[mr] = w;
   for (j = mr-1; j >= 0; j--)
      rt[j] = MulMod(rt[j+1], rt[j+1], q);

   rit[mr] = InvMod(w, q);
   for (j = mr-1; j >= 0; j--)
      rit[j] = MulMod(rit[j+1], rit[j+1], q);

   t = InvMod(2, q);
   tit[0] = 1;
   for (j = 1; j <= mr; j++)
      tit[j] = MulMod(tit[j-1], t, q);

   for (j = 0; j <= mr; j++)
      tipt[j] = PrepMulModPrecon(tit[j], q, qinv);

   if (bigtab)
      info.bigtab.make();
}

#ifndef NTL_WIZARD_HACK
SmartPtr<zz_pInfoT> Build_zz_pInfo(FFTPrimeInfo *info);
#else
SmartPtr<zz_pInfoT> Build_zz_pInfo(FFTPrimeInfo *info) { return 0; }
#endif

void UseFFTPrime(long index)
{
   if (index < 0) LogicError("invalud FFT prime index");
   if (index >= NTL_MAX_FFTPRIMES) ResourceError("FFT prime index too large");

   do {  // NOTE: thread safe lazy init
      FFTTablesType::Builder bld(FFTTables, index+1);
      long amt = bld.amt();
      if (!amt) break;

      long first = index+1-amt;
      // initialize entries first..index

      long i;
      for (i = first; i <= index; i++) {
         UniquePtr<FFTPrimeInfo> info;
         info.make();

         long q, w;
         NextFFTPrime(q, w, i);

         bool bigtab = false;

#ifdef NTL_FFT_BIGTAB
         if (i < NTL_FFT_BIGTAB_LIMIT)
            bigtab = true;
#endif

         InitFFTPrimeInfo(*info, q, w, bigtab);
         info->zz_p_context = Build_zz_pInfo(info.get());
         bld.move(info);
      }

   } while (0);
}

/*
 * Our FFT is based on the routine in Cormen, Leiserson, Rivest, and Stein.
 * For very large inputs, it should be relatively cache friendly.
 * The inner loop has been unrolled and pipelined, to exploit any
 * low-level parallelism in the machine.
 *
 * This version now allows input to alias output.
 */

#define NTL_PIPELINE
//  Define to gets some software pipelining...actually seems
//  to help somewhat

#define NTL_LOOP_UNROLL
//  Define to unroll some loops.  Seems to help a little

// FIXME: maybe the above two should be tested by the wizard

static
long RevInc(long a, long k)
{
   long j, m;

   j = k;
   m = 1L << (k-1);

   while (j && (m & a)) {
      a ^= m;
      m >>= 1;
      j--;
   }
   if (j) a ^= m;
   return a;
}

NTL_THREAD_LOCAL static
Vec<long> brc_mem[NTL_FFTMaxRoot+1];
// FIXME: This could potentially be shared across threads, using
// a "lazy table".

#if 0

static
void BitReverseCopy(long * NTL_RESTRICT A, const long * NTL_RESTRICT a, long k)
{
   long n = 1L << k;
   long* NTL_RESTRICT rev;
   long i, j;

   rev = brc_mem[k].elts();
   if (!rev) {
      brc_mem[k].SetLength(n);
      rev = brc_mem[k].elts();
      for (i = 0, j = 0; i < n; i++, j = RevInc(j, k))
         rev[i] = j;
   }

   for (i = 0; i < n; i++)
      A[rev[i]] = a[i];
}

static
void BitReverseCopy(unsigned long * NTL_RESTRICT A, const long * NTL_RESTRICT a, long k)
{
   long n = 1L << k;
   long* NTL_RESTRICT rev;
   long i, j;

   rev = brc_mem[k].elts();
   if (!rev) {
      brc_mem[k].SetLength(n);
      rev = brc_mem[k].elts();
      for (i = 0, j = 0; i < n; i++, j = RevInc(j, k))
         rev[i] = j;
   }

   for (i = 0; i < n; i++)
      A[rev[i]] = a[i];
}

#else

// A more cache-friendly bit-reverse copy algorithm.
// The "COBRA" algorithm from Carter and Gatlin, "Towards an
// optimal bit-reversal permutation algorithm", FOCS 1998.

// NOTE: for basic poly mul, we could consider not doing any
// bit-reverse copy; however, a number of other operations
// depend on this, so it would not be easy to do.

#define NTL_BRC_THRESH (12)
#define NTL_BRC_Q (5)

// Must have NTL_BRC_THRESH > 2*NTL_BRC_Q
// Should also have (1L << (2*NTL_BRC_Q)) small enough
// so that we can fit that many long's into the cache

static
long *BRC_init(long k)
{
   long n = (1L << k);
   brc_mem[k].SetLength(n);
   long *rev = brc_mem[k].elts();
   long i, j;
   for (i = 0, j = 0; i < n; i++, j = RevInc(j, k))
      rev[i] = j;
   return rev;
}

static
void BasicBitReverseCopy(long * NTL_RESTRICT B,
                         const long * NTL_RESTRICT A, long k)
{
   long n = 1L << k;
   long* NTL_RESTRICT rev;
   long i, j;

   rev = brc_mem[k].elts();
   if (!rev) rev = BRC_init(k);

   for (i = 0; i < n; i++)
      B[rev[i]] = A[i];
}

static
void COBRA(long * NTL_RESTRICT B, const long * NTL_RESTRICT A, long k)
{
   NTL_THREAD_LOCAL static Vec<long> BRC_temp;

   long q = NTL_BRC_Q;
   long k1 = k - 2*q;
   long * NTL_RESTRICT rev_k1, * NTL_RESTRICT rev_q;
   long *NTL_RESTRICT T;
   long a, b, c, a1, b1, c1;
   long i, j;

   rev_k1 = brc_mem[k1].elts();
   if (!rev_k1) rev_k1 = BRC_init(k1);

   rev_q = brc_mem[q].elts();
   if (!rev_q) rev_q = BRC_init(q);

   T = BRC_temp.elts();
   if (!T) {
      BRC_temp.SetLength(1L << (2*q));
      T = BRC_temp.elts();
   }

   for (b = 0; b < (1L << k1); b++) {
      b1 = rev_k1[b];
      for (a = 0; a < (1L << q); a++) {
         a1 = rev_q[a];
         for (c = 0; c < (1L << q); c++)
            T[(a1 << q) + c] = A[(a << (k1+q)) + (b << q) + c];
      }

      for (c = 0; c < (1L << q); c++) {
         c1 = rev_q[c];
         for (a1 = 0; a1 < (1L << q); a1++)
            B[(c1 << (k1+q)) + (b1 << q) + a1] = T[(a1 << q) + c];
      }
   }
}

static
void BitReverseCopy(long * NTL_RESTRICT B, const long * NTL_RESTRICT A, long k)
{
   if (k <= NTL_BRC_THRESH)
      BasicBitReverseCopy(B, A, k);
   else
      COBRA(B, A, k);
}

static
void BasicBitReverseCopy(unsigned long * NTL_RESTRICT B,
                         const long * NTL_RESTRICT A, long k)
{
   long n = 1L << k;
   long* NTL_RESTRICT rev;
   long i, j;

   rev = brc_mem[k].elts();
   if (!rev) rev = BRC_init(k);

   for (i = 0; i < n; i++)
      B[rev[i]] = A[i];
}

static
void COBRA(unsigned long * NTL_RESTRICT B, const long * NTL_RESTRICT A, long k)
{
   NTL_THREAD_LOCAL static Vec<unsigned long> BRC_temp;

   long q = NTL_BRC_Q;
   long k1 = k - 2*q;
   long * NTL_RESTRICT rev_k1, * NTL_RESTRICT rev_q;
   unsigned long *NTL_RESTRICT T;
   long a, b, c, a1, b1, c1;
   long i, j;

   rev_k1 = brc_mem[k1].elts();
   if (!rev_k1) rev_k1 = BRC_init(k1);

   rev_q = brc_mem[q].elts();
   if (!rev_q) rev_q = BRC_init(q);

   T = BRC_temp.elts();
   if (!T) {
      BRC_temp.SetLength(1L << (2*q));
      T = BRC_temp.elts();
   }

   for (b = 0; b < (1L << k1); b++) {
      b1 = rev_k1[b];
      for (a = 0; a < (1L << q); a++) {
         a1 = rev_q[a];
         for (c = 0; c < (1L << q); c++)
            T[(a1 << q) + c] = A[(a << (k1+q)) + (b << q) + c];
      }

      for (c = 0; c < (1L << q); c++) {
         c1 = rev_q[c];
         for (a1 = 0; a1 < (1L << q); a1++)
            B[(c1 << (k1+q)) + (b1 << q) + a1] = T[(a1 << q) + c];
      }
   }
}

static
void BitReverseCopy(unsigned long * NTL_RESTRICT B, const long * NTL_RESTRICT A, long k)
{
   if (k <= NTL_BRC_THRESH)
      BasicBitReverseCopy(B, A, k);
   else
      COBRA(B, A, k);
}

#endif

void FFT(long* A, const long* a, long k, long q, const long* root)
// performs a 2^k-point convolution modulo q

{
   if (k <= 1) {
      if (k == 0) {
	 A[0] = a[0];
	 return;
      }
      if (k == 1) {
	 long a0 = AddMod(a[0], a[1], q);
	 long a1 = SubMod(a[0], a[1], q);
         A[0] = a0;
         A[1] = a1;
	 return;
      }
   }

   // assume k > 1

   NTL_THREAD_LOCAL static Vec<long> wtab_store;
   NTL_THREAD_LOCAL static Vec<mulmod_precon_t> wqinvtab_store;
   NTL_THREAD_LOCAL static Vec<long> AA_store;

   wtab_store.SetLength(1L << (k-2));
   wqinvtab_store.SetLength(1L << (k-2));
   AA_store.SetLength(1L << k);

   long * NTL_RESTRICT wtab = wtab_store.elts();
   mulmod_precon_t * NTL_RESTRICT wqinvtab = wqinvtab_store.elts();
   long *AA = AA_store.elts();

   double qinv = 1/((double) q);

   wtab[0] = 1;
   wqinvtab[0] = PrepMulModPrecon(1, q, qinv);

   BitReverseCopy(AA, a, k);

   long n = 1L << k;

   long s, m, m_half, m_fourth, i, j, t, u, t1, u1, tt, tt1;

   long w;
   mulmod_precon_t wqinv;

   // s = 1

   for (i = 0; i < n; i += 2) {
      t = AA[i + 1];
      u = AA[i];
      AA[i] = AddMod(u, t, q);
      AA[i+1] = SubMod(u, t, q);
   }

   for (s = 2; s < k; s++) {
      m = 1L << s;
      m_half = 1L << (s-1);
      m_fourth = 1L << (s-2);

      w = root[s];
      wqinv = PrepMulModPrecon(w, q, qinv);

      // prepare wtab...

      if (s == 2) {
         wtab[1] = MulModPrecon(wtab[0], w, q, wqinv);
         wqinvtab[1] = PrepMulModPrecon(wtab[1], q, qinv);
      }
      else {
         // some software pipelining

         i = m_half-1; j = m_fourth-1;
         wtab[i-1] = wtab[j];
         wqinvtab[i-1] = wqinvtab[j];
         wtab[i] = MulModPrecon(wtab[i-1], w, q, wqinv);

         i -= 2; j --;

         for (; i >= 0; i -= 2, j --) {
            long wp2 = wtab[i+2];
            long wm1 = wtab[j];
            wqinvtab[i+2] = PrepMulModPrecon(wp2, q, qinv);
            wtab[i-1] = wm1;
            wqinvtab[i-1] = wqinvtab[j];
            wtab[i] = MulModPrecon(wm1, w, q, wqinv);
         }

         wqinvtab[1] = PrepMulModPrecon(wtab[1], q, qinv);
      }

      for (i = 0; i < n; i+= m) {

         long * NTL_RESTRICT AA0 = &AA[i];
         long * NTL_RESTRICT AA1 = &AA[i + m_half];

// #ifdef NTL_LOOP_UNROLL
#if 1

         t = AA1[0];
         u = AA0[0];
         t1 = MulModPrecon(AA1[1], w, q, wqinv);
         u1 = AA0[1];

         for (j = 0; j < m_half-2; j += 2) {
            long a02 = AA0[j+2];
            long a03 = AA0[j+3];
            long a12 = AA1[j+2];
            long a13 = AA1[j+3];
            long w2 = wtab[j+2];
            long w3 = wtab[j+3];
            mulmod_precon_t wqi2 = wqinvtab[j+2];
            mulmod_precon_t wqi3 = wqinvtab[j+3];

            tt = MulModPrecon(a12, w2, q, wqi2);
            long b00 = AddMod(u, t, q);
            long b10 = SubMod(u, t, q);
            t = tt;
            u = a02;

            tt1 = MulModPrecon(a13, w3, q, wqi3);
            long b01 = AddMod(u1, t1, q);
            long b11 = SubMod(u1, t1, q);
            t1 = tt1;
            u1 = a03;

            AA0[j] = b00;
            AA1[j] = b10;
            AA0[j+1] = b01;
            AA1[j+1] = b11;
         }

         AA0[j] = AddMod(u, t, q);
         AA1[j] = SubMod(u, t, q);
         AA0[j + 1] = AddMod(u1, t1, q);
         AA1[j + 1] = SubMod(u1, t1, q);

#else

         t = AA1[0];
         u = AA0[0];

         for (j = 0; j < m_half-1; j++) {
            long a02 = AA0[j+1];
            long a12 = AA1[j+1];
            long w2 = wtab[j+1];
            mulmod_precon_t wqi2 = wqinvtab[j+1];

            tt = MulModPrecon(a12, w2, q, wqi2);
            long b00 = AddMod(u, t, q);
            long b10 = SubMod(u, t, q);
            t = tt;
            u = a02;

            AA0[j] = b00;
            AA1[j] = b10;
         }

         AA0[j] = AddMod(u, t, q);
         AA1[j] = SubMod(u, t, q);

#endif
      }
   }

   // s == k...special case

   m = 1L << s;
   m_half = 1L << (s-1);
   m_fourth = 1L << (s-2);

   w = root[s];
   wqinv = PrepMulModPrecon(w, q, qinv);

   // j = 0, 1

   t = AA[m_half];
   u = AA[0];
   t1 = MulModPrecon(AA[1+ m_half], w, q, wqinv);
   u1 = AA[1];

   A[0] = AddMod(u, t, q);
   A[m_half] = SubMod(u, t, q);
   A[1] = AddMod(u1, t1, q);
   A[1 + m_half] = SubMod(u1, t1, q);

   for (j = 2; j < m_half; j += 2) {
      t = MulModPrecon(AA[j + m_half], wtab[j >> 1], q, wqinvtab[j >> 1]);
      u = AA[j];
      t1 = MulModPrecon(AA[j + 1+ m_half], wtab[j >> 1], q,
                        wqinvtab[j >> 1]);
      t1 = MulModPrecon(t1, w, q, wqinv);
      u1 = AA[j + 1];

      A[j] = AddMod(u, t, q);
      A[j + m_half] = SubMod(u, t, q);
      A[j + 1] = AddMod(u1, t1, q);
      A[j + 1 + m_half] = SubMod(u1, t1, q);

   }
}

#if (!defined(NTL_FFT_LAZYMUL) || (!defined(NTL_SPMM_ULL) && !defined(NTL_SPMM_ASM)))

// FFT with precomputed tables

static
void PrecompFFTMultipliers(long k, long q, const long *root, const FFTMultipliers& tab)
{
   if (k < 1) LogicError("PrecompFFTMultipliers: bad input");

   do {  // NOTE: thread safe lazy init
      FFTMultipliers::Builder bld(tab, k+1);
      long amt = bld.amt();
      if (!amt) break;

      long first = k+1-amt;
      // initialize entries first..k

      double qinv = 1/((double) q);

      for (long s = first; s <= k; s++) {
         UniquePtr<FFTVectorPair> item;

         if (s == 0) {
            bld.move(item); // position 0 not used
            continue;
         }

         if (s == 1) {
            item.make();
            item->wtab_precomp.SetLength(1);
            item->wqinvtab_precomp.SetLength(1);
            item->wtab_precomp[0] = 1;
            item->wqinvtab_precomp[0] = PrepMulModPrecon(1, q, qinv);
            bld.move(item);
            continue;
         }

         item.make();
         item->wtab_precomp.SetLength(1L << (s-1));
         item->wqinvtab_precomp.SetLength(1L << (s-1));

         long m = 1L << s;
         long m_half = 1L << (s-1);
         long m_fourth = 1L << (s-2);

         const long *wtab_last = tab[s-1]->wtab_precomp.elts();
         const mulmod_precon_t *wqinvtab_last = tab[s-1]->wqinvtab_precomp.elts();

         long *wtab = item->wtab_precomp.elts();
         mulmod_precon_t *wqinvtab = item->wqinvtab_precomp.elts();

         for (long i = 0; i < m_fourth; i++) {
            wtab[i] = wtab_last[i];
            wqinvtab[i] = wqinvtab_last[i];
         }

         long w = root[s];
         mulmod_precon_t wqinv = PrepMulModPrecon(w, q, qinv);

         // prepare wtab...

         if (s == 2) {
            wtab[1] = MulModPrecon(wtab[0], w, q, wqinv);
            wqinvtab[1] = PrepMulModPrecon(wtab[1], q, qinv);
         }
         else {
            // some software pipelining
            long i, j;

            i = m_half-1; j = m_fourth-1;
            wtab[i-1] = wtab[j];
            wqinvtab[i-1] = wqinvtab[j];
            wtab[i] = MulModPrecon(wtab[i-1], w, q, wqinv);

            i -= 2; j --;

            for (; i >= 0; i -= 2, j --) {
               long wp2 = wtab[i+2];
               long wm1 = wtab[j];
               wqinvtab[i+2] = PrepMulModPrecon(wp2, q, qinv);
               wtab[i-1] = wm1;
               wqinvtab[i-1] = wqinvtab[j];
               wtab[i] = MulModPrecon(wm1, w, q, wqinv);
            }

            wqinvtab[1] = PrepMulModPrecon(wtab[1], q, qinv);
         }

         bld.move(item);
      }
   } while (0);
}

void FFT(long* A, const long* a, long k, long q, const long* root, const FFTMultipliers& tab)
// performs a 2^k-point convolution modulo q

{
   if (k <= 1) {
      if (k == 0) {
	 A[0] = a[0];
	 return;
      }
      if (k == 1) {
	 long a0 = AddMod(a[0], a[1], q);
	 long a1 = SubMod(a[0], a[1], q);
         A[0] = a0;
         A[1] = a1;
	 return;
      }
   }

   // assume k > 1

   if (k >= tab.length()) PrecompFFTMultipliers(k, q, root, tab);

   NTL_THREAD_LOCAL static Vec<long> AA_store;
   AA_store.SetLength(1L << k);
   long *AA = AA_store.elts();

   BitReverseCopy(AA, a, k);

   long n = 1L << k;

   long s, m, m_half, m_fourth, i, j, t, u, t1, u1, tt, tt1;

   // s = 1

   for (i = 0; i < n; i += 2) {
      t = AA[i + 1];
      u = AA[i];
      AA[i] = AddMod(u, t, q);
      AA[i+1] = SubMod(u, t, q);
   }

   for (s = 2; s < k; s++) {
      m = 1L << s;
      m_half = 1L << (s-1);
      m_fourth = 1L << (s-2);

      const long* wtab = tab[s]->wtab_precomp.elts();
      const mulmod_precon_t *wqinvtab = tab[s]->wqinvtab_precomp.elts();

      for (i = 0; i < n; i+= m) {

         long *AA0 = &AA[i];
         long *AA1 = &AA[i + m_half];

#ifdef NTL_PIPELINE

// pipelining: seems to be faster

         t = AA1[0];
         u = AA0[0];
         t1 = MulModPrecon(AA1[1], wtab[1], q, wqinvtab[1]);
         u1 = AA0[1];

         for (j = 0; j < m_half-2; j += 2) {
            long a02 = AA0[j+2];
            long a03 = AA0[j+3];
            long a12 = AA1[j+2];
            long a13 = AA1[j+3];
            long w2 = wtab[j+2];
            long w3 = wtab[j+3];
            mulmod_precon_t wqi2 = wqinvtab[j+2];
            mulmod_precon_t wqi3 = wqinvtab[j+3];

            tt = MulModPrecon(a12, w2, q, wqi2);
            long b00 = AddMod(u, t, q);
            long b10 = SubMod(u, t, q);

            tt1 = MulModPrecon(a13, w3, q, wqi3);
            long b01 = AddMod(u1, t1, q);
            long b11 = SubMod(u1, t1, q);

            AA0[j] = b00;
            AA1[j] = b10;
            AA0[j+1] = b01;
            AA1[j+1] = b11;

            t = tt;
            u = a02;
            t1 = tt1;
            u1 = a03;
         }

         AA0[j] = AddMod(u, t, q);
         AA1[j] = SubMod(u, t, q);
         AA0[j + 1] = AddMod(u1, t1, q);
         AA1[j + 1] = SubMod(u1, t1, q);
      }
#else
         for (j = 0; j < m_half; j += 2) {
            const long a00 = AA0[j];
            const long a01 = AA0[j+1];
            const long a10 = AA1[j];
            const long a11 = AA1[j+1];

            const long w0 = wtab[j];
            const long w1 = wtab[j+1];
            const mulmod_precon_t wqi0 = wqinvtab[j];
            const mulmod_precon_t wqi1 = wqinvtab[j+1];

            const long tt = MulModPrecon(a10, w0, q, wqi0);
            const long uu = a00;
            const long b00 = AddMod(uu, tt, q);
            const long b10 = SubMod(uu, tt, q);

            const long tt1 = MulModPrecon(a11, w1, q, wqi1);
            const long uu1 = a01;
            const long b01 = AddMod(uu1, tt1, q);
            const long b11 = SubMod(uu1, tt1, q);

            AA0[j] = b00;
            AA0[j+1] = b01;
            AA1[j] = b10;
            AA1[j+1] = b11;
         }
      }
#endif
   }

   // s == k, special case
   {
      m = 1L << s;
      m_half = 1L << (s-1);
      m_fourth = 1L << (s-2);

      const long* wtab = tab[s]->wtab_precomp.elts();
      const mulmod_precon_t *wqinvtab = tab[s]->wqinvtab_precomp.elts();

      for (i = 0; i < n; i+= m) {

         long *AA0 = &AA[i];
         long *AA1 = &AA[i + m_half];
         long *A0 = &A[i];
         long *A1 = &A[i + m_half];

#ifdef NTL_PIPELINE

// pipelining: seems to be faster

         t = AA1[0];
         u = AA0[0];
         t1 = MulModPrecon(AA1[1], wtab[1], q, wqinvtab[1]);
         u1 = AA0[1];

         for (j = 0; j < m_half-2; j += 2) {
            long a02 = AA0[j+2];
            long a03 = AA0[j+3];
            long a12 = AA1[j+2];
            long a13 = AA1[j+3];
            long w2 = wtab[j+2];
            long w3 = wtab[j+3];
            mulmod_precon_t wqi2 = wqinvtab[j+2];
            mulmod_precon_t wqi3 = wqinvtab[j+3];

            tt = MulModPrecon(a12, w2, q, wqi2);
            long b00 = AddMod(u, t, q);
            long b10 = SubMod(u, t, q);

            tt1 = MulModPrecon(a13, w3, q, wqi3);
            long b01 = AddMod(u1, t1, q);
            long b11 = SubMod(u1, t1, q);

            A0[j] = b00;
            A1[j] = b10;
            A0[j+1] = b01;
            A1[j+1] = b11;

            t = tt;
            u = a02;
            t1 = tt1;
            u1 = a03;
         }

         A0[j] = AddMod(u, t, q);
         A1[j] = SubMod(u, t, q);
         A0[j + 1] = AddMod(u1, t1, q);
         A1[j + 1] = SubMod(u1, t1, q);
      }
#else
         for (j = 0; j < m_half; j += 2) {
            const long a00 = AA0[j];
            const long a01 = AA0[j+1];
            const long a10 = AA1[j];
            const long a11 = AA1[j+1];

            const long w0 = wtab[j];
            const long w1 = wtab[j+1];
            const mulmod_precon_t wqi0 = wqinvtab[j];
            const mulmod_precon_t wqi1 = wqinvtab[j+1];

            const long tt = MulModPrecon(a10, w0, q, wqi0);
            const long uu = a00;
            const long b00 = AddMod(uu, tt, q);
            const long b10 = SubMod(uu, tt, q);

            const long tt1 = MulModPrecon(a11, w1, q, wqi1);
            const long uu1 = a01;
            const long b01 = AddMod(uu1, tt1, q);
            const long b11 = SubMod(uu1, tt1, q);

            A0[j] = b00;
            A0[j+1] = b01;
            A1[j] = b10;
            A1[j+1] = b11;
         }
      }
#endif
   }

}

#else

// FFT with precomputed tables and David Harvey's lazy multiplication
// strategy:
//   David Harvey.
//   Faster arithmetic for number-theoretic transforms.
//   J. Symb. Comp. 60 (2014) 113–119.

static inline
unsigned long LazyPrepMulModPrecon(long b, long n, double ninv)
{
   unsigned long q, r;

   q = (long) ( (((double) b) * NTL_SP_FBOUND) * ninv );
   r = (((unsigned long) b) << NTL_SP_NBITS ) - q * ((unsigned long) n);

   if (r >> (NTL_BITS_PER_LONG-1)) {
      q--;
      r += n;
   }
   else if (((long) r) >= n) {
      q++;
      r -=n;
   }

   unsigned long res = q << (NTL_BITS_PER_LONG - NTL_SP_NBITS);
   long qq, rr;

   rr = MulDivRem(qq, (long) r, 4, n, 4*ninv);

   res = res + (qq << (NTL_BITS_PER_LONG - NTL_SP_NBITS-2));

   return res;
}

static inline
unsigned long LazyMulModPrecon(unsigned long a, unsigned long b,
                               unsigned long n, unsigned long bninv)
{
   unsigned long q = MulHiUL(a, bninv);
   unsigned long res = a*b - q*n;
   return res;
}

static inline
unsigned long LazyReduce(unsigned long a, unsigned long q)
{
  unsigned long res;
#if (NTL_ARITH_RIGHT_SHIFT && defined(NTL_AVOID_BRANCHING) && !defined(NTL_CLEAN_INT))
  res = a - q;
  res  += (((long) res) >> (NTL_BITS_PER_LONG-1)) & q;
#elif (defined(NTL_AVOID_BRANCHING))
  res = a - q;
  res  += (-(res >> (NTL_BITS_PER_LONG-1))) & q;
#else
  if (a >= q)
    res = a - q;
  else
    res = a;
#endif

  return res;
}

static
void LazyPrecompFFTMultipliers(long k, long q, const long *root, const FFTMultipliers& tab)
{
   if (k < 1) LogicError("LazyPrecompFFTMultipliers: bad input");

   do { // NOTE: thread safe lazy init
      FFTMultipliers::Builder bld(tab, k+1);
      long amt = bld.amt();
      if (!amt) break;

      long first = k+1-amt;
      // initialize entries first..k

      double qinv = 1/((double) q);

      for (long s = first; s <= k; s++) {
         UniquePtr<FFTVectorPair> item;

         if (s == 0) {
            bld.move(item); // position 0 not used
            continue;
         }

         if (s == 1) {
            item.make();
            item->wtab_precomp.SetLength(1);
            item->wqinvtab_precomp.SetLength(1);
            item->wtab_precomp[0] = 1;
            item->wqinvtab_precomp[0] = LazyPrepMulModPrecon(1, q, qinv);
            bld.move(item);
            continue;
         }

         item.make();
         item->wtab_precomp.SetLength(1L << (s-1));
         item->wqinvtab_precomp.SetLength(1L << (s-1));

         long m = 1L << s;
         long m_half = 1L << (s-1);
         long m_fourth = 1L << (s-2);

         const long *wtab_last = tab[s-1]->wtab_precomp.elts();
         const mulmod_precon_t *wqinvtab_last = tab[s-1]->wqinvtab_precomp.elts();

         long *wtab = item->wtab_precomp.elts();
         mulmod_precon_t *wqinvtab = item->wqinvtab_precomp.elts();

         for (long i = 0; i < m_fourth; i++) {
            wtab[i] = wtab_last[i];
            wqinvtab[i] = wqinvtab_last[i];
         }

         long w = root[s];
         mulmod_precon_t wqinv = LazyPrepMulModPrecon(w, q, qinv);

         // prepare wtab...

         if (s == 2) {
            wtab[1] = LazyReduce(LazyMulModPrecon(wtab[0], w, q, wqinv), q);
            wqinvtab[1] = LazyPrepMulModPrecon(wtab[1], q, qinv);
         }
         else {
            // some software pipelining
            long i, j;

            i = m_half-1; j = m_fourth-1;
            wtab[i-1] = wtab[j];
            wqinvtab[i-1] = wqinvtab[j];
            wtab[i] = LazyReduce(LazyMulModPrecon(wtab[i-1], w, q, wqinv), q);

            i -= 2; j --;

            for (; i >= 0; i -= 2, j --) {
               long wp2 = wtab[i+2];
               long wm1 = wtab[j];
               wqinvtab[i+2] = LazyPrepMulModPrecon(wp2, q, qinv);
               wtab[i-1] = wm1;
               wqinvtab[i-1] = wqinvtab[j];
               wtab[i] = LazyReduce(LazyMulModPrecon(wm1, w, q, wqinv), q);
            }

            wqinvtab[1] = LazyPrepMulModPrecon(wtab[1], q, qinv);
         }

         bld.move(item);
      }
   } while (0);
}

void FFT(long* A, const long* a, long k, long q, const long* root, const FFTMultipliers& tab)

// performs a 2^k-point convolution modulo q

{
   if (k <= 1) {
      if (k == 0) {
	 A[0] = a[0];
	 return;
      }
      if (k == 1) {
	 long a0 = AddMod(a[0], a[1], q);
	 long a1 = SubMod(a[0], a[1], q);
         A[0] = a0;
         A[1] = a1;
	 return;
      }
   }

   // assume k > 1

   if (k >= tab.length()) LazyPrecompFFTMultipliers(k, q, root, tab);

   NTL_THREAD_LOCAL static Vec<unsigned long> AA_store;
   AA_store.SetLength(1L << k);
   unsigned long *AA = AA_store.elts();

   BitReverseCopy(AA, a, k);

   long n = 1L << k;

   /* we work with redundant representations, in the range [0, 4q) */

   long s, m, m_half, m_fourth, i, j;
   unsigned long t, u, t1, u1;

   long two_q = 2 * q;

   // s = 1

   for (i = 0; i < n; i += 2) {
      t = AA[i + 1];
      u = AA[i];
      AA[i] = u + t;
      AA[i+1] = u - t + q;
   }

   // s = 2

   {
      const long * NTL_RESTRICT wtab = tab[2]->wtab_precomp.elts();
      const mulmod_precon_t * NTL_RESTRICT wqinvtab = tab[2]->wqinvtab_precomp.elts();

      for (i = 0; i < n; i += 4) {

         unsigned long * NTL_RESTRICT AA0 = &AA[i];
         unsigned long * NTL_RESTRICT AA1 = &AA[i + 2];

         {
            const long w1 = wtab[0];
            const mulmod_precon_t wqi1 = wqinvtab[0];
            const unsigned long a11 = AA1[0];
            const unsigned long a01 = AA0[0];

            const unsigned long tt1 = LazyMulModPrecon(a11, w1, q, wqi1);
            const unsigned long uu1 = LazyReduce(a01, two_q);
            const unsigned long b01 = uu1 + tt1;
            const unsigned long b11 = uu1 - tt1 + two_q;

            AA0[0] = b01;
            AA1[0] = b11;
         }
         {
            const long w1 = wtab[1];
            const mulmod_precon_t wqi1 = wqinvtab[1];
            const unsigned long a11 = AA1[1];
            const unsigned long a01 = AA0[1];

            const unsigned long tt1 = LazyMulModPrecon(a11, w1, q, wqi1);
            const unsigned long uu1 = LazyReduce(a01, two_q);
            const unsigned long b01 = uu1 + tt1;
            const unsigned long b11 = uu1 - tt1 + two_q;

            AA0[1] = b01;
            AA1[1] = b11;
         }
      }
   }

   //  s = 3..k

   for (s = 3; s <= k; s++) {
      m = 1L << s;
      m_half = 1L << (s-1);
      m_fourth = 1L << (s-2);

      const long* NTL_RESTRICT wtab = tab[s]->wtab_precomp.elts();
      const mulmod_precon_t * NTL_RESTRICT wqinvtab = tab[s]->wqinvtab_precomp.elts();

      for (i = 0; i < n; i += m) {

         unsigned long * NTL_RESTRICT AA0 = &AA[i];
         unsigned long * NTL_RESTRICT AA1 = &AA[i + m_half];

#ifdef NTL_LOOP_UNROLL

         for (j = 0; j < m_half; j += 4) {
            {
               const long w1 = wtab[j+0];
               const mulmod_precon_t wqi1 = wqinvtab[j+0];
               const unsigned long a11 = AA1[j+0];
               const unsigned long a01 = AA0[j+0];

               const unsigned long tt1 = LazyMulModPrecon(a11, w1, q, wqi1);
               const unsigned long uu1 = LazyReduce(a01, two_q);
               const unsigned long b01 = uu1 + tt1;
               const unsigned long b11 = uu1 - tt1 + two_q;

               AA0[j+0] = b01;
               AA1[j+0] = b11;
            }
            {
               const long w1 = wtab[j+1];
               const mulmod_precon_t wqi1 = wqinvtab[j+1];
               const unsigned long a11 = AA1[j+1];
               const unsigned long a01 = AA0[j+1];

               const unsigned long tt1 = LazyMulModPrecon(a11, w1, q, wqi1);
               const unsigned long uu1 = LazyReduce(a01, two_q);
               const unsigned long b01 = uu1 + tt1;
               const unsigned long b11 = uu1 - tt1 + two_q;

               AA0[j+1] = b01;
               AA1[j+1] = b11;
            }
            {
               const long w1 = wtab[j+2];
               const mulmod_precon_t wqi1 = wqinvtab[j+2];
               const unsigned long a11 = AA1[j+2];
               const unsigned long a01 = AA0[j+2];

               const unsigned long tt1 = LazyMulModPrecon(a11, w1, q, wqi1);
               const unsigned long uu1 = LazyReduce(a01, two_q);
               const unsigned long b01 = uu1 + tt1;
               const unsigned long b11 = uu1 - tt1 + two_q;

               AA0[j+2] = b01;
               AA1[j+2] = b11;
            }
            {
               const long w1 = wtab[j+3];
               const mulmod_precon_t wqi1 = wqinvtab[j+3];
               const unsigned long a11 = AA1[j+3];
               const unsigned long a01 = AA0[j+3];

               const unsigned long tt1 = LazyMulModPrecon(a11, w1, q, wqi1);
               const unsigned long uu1 = LazyReduce(a01, two_q);
               const unsigned long b01 = uu1 + tt1;
               const unsigned long b11 = uu1 - tt1 + two_q;

               AA0[j+3] = b01;
               AA1[j+3] = b11;
            }
         }
#else

         for (j = 0; j < m_half; j++) {
            const long w1 = wtab[j];
            const mulmod_precon_t wqi1 = wqinvtab[j];
            const unsigned long a11 = AA1[j];
            const unsigned long a01 = AA0[j];

            const unsigned long tt1 = LazyMulModPrecon(a11, w1, q, wqi1);
            const unsigned long uu1 = LazyReduce(a01, two_q);
            const unsigned long b01 = uu1 + tt1;
            const unsigned long b11 = uu1 - tt1 + two_q;

            AA0[j] = b01;
            AA1[j] = b11;
         }

#endif

      }
   }

   /* need to reduce redundant representations */

   for (i = 0; i < n; i++) {
      unsigned long tmp = LazyReduce(AA[i], two_q);
      A[i] = LazyReduce(tmp, q);
   }
}

#endif

NTL_END_IMPL


#pragma once

/*
 * This file declares and implements a class representing a cubic spline.
 * After instantiating a Spline points (x, y) can be added; call calculate()
 * to compute coefficients of the cubic polynomial for each interval.
 * Spline::operator()(double x) computes the corresponding value of y
 * according to the computed spline.
 *
 * This isn't an elegant or optimal implementation but it gets the job done
 * for my purposes. Eigen is a dependency to do an SVD factorization to
 * compute the spline coefficients (the matrix obtained in my use cases
 * is numerically low-rank).
 */

#include <cassert>
#include <vector>

#include <Eigen/Dense>

namespace {

using Eigen::MatrixXd;
using Eigen::VectorXd;

VectorXd find_spline_coeffs(const std::vector<double>& xs_,
                            const std::vector<double>& ys_)
{
    assert(xs_.size() == ys_.size());
    unsigned nintervals = xs_.size() - 1;
    MatrixXd A = MatrixXd::Zero(nintervals*4, nintervals*4);
    VectorXd B = VectorXd::Zero(nintervals*4);

    A(0, 0) = 3*xs_[0]*xs_[0];
    A(0, 1) = 2*xs_[0];
    A(0, 2) = 1.0;
    A(1, 0) = xs_[0] * xs_[0] * xs_[0];
    A(1, 1) = xs_[0] * xs_[0];
    A(1, 2) = xs_[0];
    A(1, 3) = 1.0;
    B(1) = ys_[0];

    unsigned curr_row = 2;
    for (unsigned node = 1; node < nintervals; ++node) {
        double xn2 = xs_[node] * xs_[node];
        double xn3 = xn2 * xs_[node];
        unsigned j = (node - 1) * 4;
        A(curr_row, j) = xn3;
        A(curr_row, j+1) = xn2;
        A(curr_row, j+2) = xs_[node];
        A(curr_row, j+3) = 1.0;
        B(curr_row) = ys_[node];
        curr_row += 1;
        A(curr_row, j) = 3*xn2;
        A(curr_row, j+1) = 2*xs_[node];
        A(curr_row, j+2) = 1.0;
        A(curr_row, j+4) = -3*xn2;
        A(curr_row, j+5) = -2*xs_[node];
        A(curr_row, j+6) = -1.0;
        curr_row += 1;
        A(curr_row, j) = 6*xs_[node];
        A(curr_row, j+1) = 2.0;
        A(curr_row, j+4) = -6*xs_[node];
        A(curr_row, j+5) = -2.0;
        curr_row += 1;
        A(curr_row, j+4) = xn3;
        A(curr_row, j+5) = xn2;
        A(curr_row, j+6) = xs_[node];
        A(curr_row, j+7) = 1.0;
        B(curr_row) = ys_[node];
        curr_row += 1;
    }

    unsigned j = (nintervals - 1) * 4;
    A(curr_row, j) = xs_[nintervals] * xs_[nintervals] * xs_[nintervals];
    A(curr_row, j+1) = xs_[nintervals] * xs_[nintervals];
    A(curr_row, j+2) = xs_[nintervals];
    A(curr_row, j+3) = 1.0;
    B(curr_row++) = ys_[nintervals];
    A(curr_row, j) = 3*xs_[nintervals]*xs_[nintervals];
    A(curr_row, j+1) = 2 * xs_[nintervals];
    A(curr_row, j+2) = 1.0;

    return A.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(B);
}

}

class Spline
{
private:
    struct Coefficients {
        double a; double b;
        double c; double d;
    };
    std::vector<double> xs_;
    std::vector<double> ys_;
    std::vector<Coefficients> coeffs_;

    template <typename It>
    It get_element_position(double x, It start, It end) const
    {
        if (end == start) {
            return start;
        } else if (end - start == 1) {
            return *start > x ? start : end;
        } else {
            It middle = start + (end - start) / 2;
            if (*middle > x) {
                return get_element_position(x, start, middle);
            } else {
                return get_element_position(x, middle, end);
            }
        }
    }

public:
    typedef std::pair<double, double> Point;

    void add_point(Point p)
    {
        auto it = get_element_position(p.first, xs_.begin(), xs_.end());
        auto offset = it - xs_.begin();
        xs_.insert(it, p.first);
        ys_.insert(ys_.begin() + offset, p.second);
    }

    void reset()
    {
        *this = Spline();
    }

    void calculate()
    {
        assert(xs_.size() == ys_.size());
        // X is a vector of a, b, c, d for each interval in the spline
        // in that order.
        auto X = find_spline_coeffs(xs_, ys_);
        coeffs_.resize(xs_.size() - 1);
        for (unsigned i = 0; i < coeffs_.size()*4; i += 4)
            coeffs_[i/4] = Coefficients{ X(i), X(i + 1), X(i + 2), X(i + 3) };
    }

    double operator()(double x) const
    {
        auto it = get_element_position(x, xs_.begin(), xs_.end());
        assert(!(it == xs_.begin() || it == xs_.end()));
        const auto& coeffs = coeffs_[it - xs_.begin() - 1];
        return coeffs.a * x * x * x + coeffs.b * x * x + coeffs.c * x +
            coeffs.d;
    }
};


#include <CGAL/trace.h>
#include <CGAL/Timer.h>
#include <iostream>
#include <string>
#include <fstream>
#include <CGAL/FPU_extension.h>

#include <Eigen/Eigen>
#include <Eigen/SVD>

double norm_1(Eigen::Matrix3d X)
{
  double sum = 0;
  for ( int i = 0; i < 3; i++ )
  {
    for ( int j = 0; j < 3; j++ )
    {
      sum += abs(X(i,j));
    }
  }
  return sum;
}

double norm_inf(Eigen::Matrix3d X)
{
  double max_abs = abs(X(0,0));
  for ( int i = 0; i < 3; i++ )
  {
    for ( int j = 0; j < 3; j++ )
    {
      double new_abs = abs(X(i,j));
      if ( new_abs > max_abs )
      {
        max_abs = new_abs;
      }
    }
  }
  return max_abs;
}

template<typename Mat>
void polar_eigen(const Mat& A, Mat& R, bool& SVD)
{
  typedef typename Mat::Scalar Scalar;
  typedef Eigen::Matrix<typename Mat::Scalar,3,1> Vec;

  const Scalar th = std::sqrt(Eigen::NumTraits<Scalar>::dummy_precision());

  Eigen::SelfAdjointEigenSolver<Mat> eig;
  feclearexcept(FE_UNDERFLOW);
  eig.computeDirect(A.transpose()*A);

  if(fetestexcept(FE_UNDERFLOW) || eig.eigenvalues()(0)/eig.eigenvalues()(2)/100.0<th)
  {
    // The computation of the eigenvalues might have diverged.
    // Fallback to an accurate SVD based decomposiiton method.
    Eigen::JacobiSVD<Mat> svd;
    svd.compute(A, Eigen::ComputeFullU | Eigen::ComputeFullV );
    const Mat& u = svd.matrixU(); const Mat& v = svd.matrixV(); const Vec& w = svd.singularValues();
    R = u*v.transpose();
    SVD = true;
    return;
  }

  Vec S = eig.eigenvalues().cwiseSqrt();
  R = A  * eig.eigenvectors() * S.asDiagonal().inverse()
    * eig.eigenvectors().transpose();
  SVD = false;
}

int main() {
  std::ifstream file;
  file.open("Polar_benchmark");
  if (!file)
  {
    CGAL_TRACE_STREAM << "Error loading file!\n";
    return 0;
  }

  Eigen::Matrix3d A, U, r;
  bool SVD = false;
  int num_svd = 0;
  int num_mtr = 0;

  CGAL_TRACE_STREAM << "start polar decomposition...\n";
  while (!file.eof())
  {
    for (int j = 0; j < 3; j++)
    {
      for (int k = 0; k < 3; k++)
      {
        file >> A(j, k);
      }
    }
    num_mtr++;
    double det = A.determinant();
    if (A.determinant() > 0)
    {
      polar_eigen<Eigen::Matrix3d> (A, U, SVD);
      if (SVD)
        num_svd++;
      U.transposeInPlace();
      double det_2 = U.determinant();
      if ( abs(det_2-1) > 1e-2 )
      {
        CGAL_TRACE_STREAM << "error matrix: det = " << det_2 << "\n";
      }
    }
  }
  file.close();

  CGAL_TRACE_STREAM << "done. " << num_svd << " SVD decompositions in " << num_mtr << " matrices\n";
  return 0;

  /*int ite = 200000;
  Eigen::JacobiSVD<Eigen::Matrix3d> svd;
  Eigen::Matrix3d A, U, H, r;

  int matrix_idx = rand()%200;
  for (int i = 0; i < matrix_idx; i++)
  {
    for (int j = 0; j < 3; j++)
    {
      for (int k = 0; k < 3; k++)
      {
        file >> A(j, k);
      }
    }
  }
  file.close();

  A(0,0) = 0.0014667022958104185; A(0,1) = 0.0024644551180079627; A(0,2) = 0.0040659871060825092;
  A(1,0) = 0.0028327558016991478; A(1,1) = 0.0054236820249146406; A(1,2) = 0.0079280090866983826;
  A(2,0) = 0.0039090073251031232; A(2,1) = 0.0066744523074963374; A(2,2) = 0.010848552426718550;
  A = A*10000;
  double det = A.determinant();

  CGAL::Timer task_timer;

  CGAL_TRACE_STREAM << "Start SVD decomposition...";
  task_timer.start();
  for (int i = 0; i < ite; i++)
  {
    if (A.determinant() > 0)
    {
      polar_eigen<Eigen::Matrix3d> (A, U);
      U.transposeInPlace();
      double det_2 = U.determinant();
      r = U*U.transpose();
    }
  }
  task_timer.stop();

  CGAL_TRACE_STREAM << "done: " << task_timer.time() << "s\n";

        return 0;*/
}