/*
# Copyright 2018 Daniel Selsam. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
*/
#include <string>
#include <exception>
#include <Eigen/Dense>
#include "z3++.h"
#include <cmath>
#include <ctime>
#include <cstdlib>
#include <unordered_map>
#include <map>
#include <unordered_set>
#include <set>
#include <vector>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <sstream>

using std::vector;
using std::pair;
using std::string;
using std::unordered_map;
using std::unordered_set;
using std::set;

#define MAX_UINT 4294967295

struct z3_expr_hash { unsigned operator()(z3::expr const & e) const { return e.hash(); } };
struct z3_expr_eq { bool operator()(z3::expr const & e1, z3::expr const & e2) const { return z3::eq(e1, e2); } };

template<typename T>
using z3_expr_map = typename std::unordered_map<z3::expr, T, z3_expr_hash, z3_expr_eq>;
using z3_expr_set = typename std::unordered_set<z3::expr, z3_expr_hash, z3_expr_eq>;

class mstream {
  std::ostringstream m_strm;
public:
  std::string str() const { return m_strm.str(); }
  template<typename T> mstream & operator<<(T const & t) { m_strm << t; return *this; }
};

class SolverException : public std::exception {
private:
  std::string _msg;
public:
  explicit SolverException(const std::string & msg): _msg(msg) {}
  explicit SolverException(const mstream & msg): _msg(msg.str()) {}
  virtual const char* what() const throw() override { return _msg.c_str(); }
};

struct Var {
  unsigned _idx;
  Var(unsigned idx): _idx(idx) {}
  unsigned idx() const { return _idx; }

  string repr() const { return string("Var(") + std::to_string(idx()) + ")"; }
  bool operator==(const Var & other) const { return idx() == other.idx(); }
  bool operator<(const Var & other) const { return idx() < other.idx(); }
  unsigned hash() const { return idx(); }
};

namespace std {
  template <> struct hash<Var> { std::size_t operator()(const Var& var) const { return var.hash(); }};
}

struct Lit {
  Var      _var;
  bool     _neg;
  Lit(Var var, bool neg): _var(var), _neg(neg) {}

  Var var() const { return _var; }
  bool neg() const { return _neg; }

  unsigned vidx(unsigned n_vars) const { return neg() ? var().idx() + n_vars : var().idx(); }
  Lit flip() const { return Lit(var(), !neg()); }

  string repr() const { return string("Lit(") + var().repr() + ", " + (neg() ? "1" : "0") + ")"; }
  bool operator==(const Lit & other) const { return var() == other.var() && neg() == other.neg(); }
  bool operator<(const Lit & other) const { return var() < other.var() || (var() == other.var() && neg() < other.neg()); }
  unsigned hash() const { return var().hash() + (neg() ? 100003 : 0); } // smallest 6-digit prime
};

namespace std {
  template <> struct hash<Lit> { std::size_t operator()(const Lit& lit) const { return lit.hash(); }};
}

enum class Assignment { POS, NEG, SKIP };

enum class Status { UNKNOWN, UNSAT, SAT };

static Status check_result_to_Status(z3::check_result const & cr) {
  switch (cr) {
  case z3::unknown: return Status::UNKNOWN;
  case z3::unsat: return Status::UNSAT;
  case z3::sat: return Status::SAT;
  }
  throw SolverException("check_result_to_Status: unexpected status from z3");
}

struct Options {
  unsigned timeout_ms{MAX_UINT};
  unsigned max_conflicts{MAX_UINT};
  unsigned restart_max{MAX_UINT};
  unsigned variable_decay{110};

  bool     override_incremental{false};
  bool     lookahead_simplify{false};
  bool     acce{false};

  unsigned z3_replay_timeout_scale{10};
  unsigned drat_trim_timeout_scale{200};
  string   logfilename{"/tmp/solver.log"};
};

class Context {
  z3::context _zctx;
public:
  Context() {}
  z3::context & z() { return _zctx; }
};

z3::expr mk_not(z3::expr const & lit) {
  if (lit.is_not()) {
    if (!lit.arg(0).is_const()) { throw SolverException(mstream() << "mk_not called on non-literal: " << lit); }
    return lit.arg(0);
  } else {
    if (!lit.is_const()) { throw SolverException(mstream() << "mk_not called on non-literal: " << lit); }
    return !lit;
  }
}

class Expr {
  z3::expr _zexpr;
public:
  Expr(z3::expr const & zexpr): _zexpr(zexpr) {}
  z3::expr const & z() const { return _zexpr; }

  Expr flip() const { return Expr(mk_not(z())); }
  Expr var() const { return z().is_not() ? flip() : Expr(z()); }
  bool is_neg() const { return z().is_not(); }

  int ilit() const {
    z3::expr var = z().is_not() ? z().arg(0) : z();
    if (!var.is_const()) { throw SolverException(mstream() << "ilit() called on non-literal: " << z()); }
    int ivar = var.decl().name().to_int();
    return z().is_not() ? -ivar : ivar;
  }

};

typedef Eigen::Array<bool,Eigen::Dynamic,1> ArrayXb;

struct TFData {
  unsigned         n_vars;
  unsigned         n_clauses;
  Eigen::MatrixXi  CL_idxs;

  TFData(unsigned n_vars, unsigned n_clauses, Eigen::MatrixXi const & CL_idxs):
    n_vars(n_vars), n_clauses(n_clauses), CL_idxs(CL_idxs) {
    if (n_vars == 0) {
      throw SolverException("Creating TFData with no variables");
    } else if (n_clauses == 0) {
      throw SolverException("Creating TFData with no clauses");
    }
  }

  ArrayXb          core_var_mask;
  ArrayXb          core_clause_mask;
};

static void check_exists_and_nonempty(string const & filename) {
  std::ifstream f(filename);
  if (!f.good()) {
    throw SolverException(mstream() << "file '" << filename << "' does not exist");
  } else if (f.peek() == std::ifstream::traits_type::eof()) {
    throw SolverException(mstream() << "file '" << filename << "' is empty");
  }
}

static void system_throw(string const & cmd, string const & finally) {
  int ret = system(cmd.c_str());
  if (WEXITSTATUS(ret) != 0) {
    system(finally.c_str());
    throw SolverException(mstream() << "Executing '" << cmd << "' failed.");
  }
}

class Solver;

struct TFDataManager {
  z3::expr_vector   non_units;
  vector<z3::expr>  pruned_clauses;
  z3_expr_map<Var>  _non_unit_to_var;
  unsigned          n_cells{0};

  TFDataManager(Solver const & s);

  Var non_unit_to_var(z3::expr const & e) const {
    if (_non_unit_to_var.count(e)) {
      return _non_unit_to_var.at(e);
    } else {
      throw SolverException(mstream() << "expr not found in non_unit_to_var: " << e);
    }
  }

  Lit free_expr_to_lit(z3::expr const & e) const {
    if (e.is_not()) {
      return Lit(non_unit_to_var(e.arg(0)), true);
    } else if (!e.is_const()) {
      throw SolverException(mstream() << "zexpr_to_lit called on non-literal: " << e);
    } else {
      return Lit(non_unit_to_var(e), false);
    }
  }

  Eigen::MatrixXi to_CL_idxs() const {
    Eigen::MatrixXi CL_idxs = Eigen::MatrixXi(n_cells, 2);
    unsigned cell_idx = 0;
    for (unsigned c_idx = 0; c_idx < pruned_clauses.size(); ++c_idx) {
      for (unsigned arg_idx = 0; arg_idx < pruned_clauses[c_idx].num_args(); ++arg_idx) {
	CL_idxs(cell_idx, 0) = c_idx;
	CL_idxs(cell_idx, 1) = free_expr_to_lit(pruned_clauses[c_idx].arg(arg_idx)).vidx(non_units.size());
	cell_idx++;
      }
    }
    return CL_idxs;
  }
};

class Solver {
public:

  Options          _opts;
  z3::solver       _zsolver;
  unsigned         _orig_n_vars; // wrt original problem
  unsigned         _total_n_vars;

  void set_options() {
    z().set(":lookahead.reward", "march_cu");
    z().set(":lookahead.cube.cutoff", "depth");
    z().set(":lookahead.cube.depth", (unsigned)1);

    z().set(":max_conflicts", _opts.max_conflicts);
    z().set(":sat.restart.max", _opts.restart_max);
    z().set(":sat.variable_decay", _opts.variable_decay);

    z().set(":override_incremental", _opts.override_incremental);
    z().set(":lookahead_simplify", _opts.lookahead_simplify);
    z().set(":acce", _opts.acce);

    z().set(":sat.force_cleanup", true);
  }

  void init_post_from() {
    _total_n_vars = z().units().size() + z().non_units().size();
    _orig_n_vars = _total_n_vars;
  }

    void init_post_clone() {
      _total_n_vars = z().units().size() + z().non_units().size();
    }

  z3::solver & z() { return _zsolver; }
  z3::solver const & z() const { return _zsolver; }

public:
  Solver(Context & ctx, Options const & opts):
    _opts(opts), _zsolver(ctx.z(), "QF_FD")
  { set_options(); }

  Solver(Context & ctx, Options const & opts, Solver const & s):
    _opts(opts), _zsolver(ctx.z(), s.z(), z3::solver::translate()), _orig_n_vars(s._orig_n_vars)
  { set_options(); init_post_clone(); }

  Solver clone(Context & ctx) const { return Solver(ctx, _opts, *this); }
  void from_string(string const & s) {
    try {
      z().from_string(s.c_str());
    } catch (...) {
      throw SolverException("error in Solver::from_string(...)");
    }
    init_post_from();
  }

  void from_file(string const & filename) {
    try {
      z().from_file(filename.c_str());
    } catch (...) {
      throw SolverException("error in Solver::from_file(...)");
    }
    init_post_from();
  }

  string serialize() { return z().dimacs(); }
  string dimacs() { return z().dimacs(); }

  unsigned total_n_vars() const {
    return _total_n_vars;
  }

  void add(Expr const & expr) {
    z().add(expr.z());
  }

  Status propagate() {
    z().set(":max_conflicts", (unsigned) 0);
    z3::check_result cr = z().check();
    z().set(":max_conflicts", _opts.max_conflicts);
    return check_result_to_Status(cr);
  }

  Status check() {
    return check_with_timeout_ms(_opts.timeout_ms);
  }

  Status check_with_timeout_ms(unsigned timeout_ms) {
    z().set(":timeout", timeout_ms);
    z3::check_result result = z().check();
    z().set(":timeout", (unsigned) MAX_UINT);

    if (result == z3::unknown) {
      return propagate();
    } else {
      return check_result_to_Status(result);
    }
  }

private:
  void validate_cube(z3::expr_vector const & cube) {
    if (cube.size() == 0) {
      throw SolverException("z3::cube() returned empty cube");
    } else if (cube.size() > 1) {
      throw SolverException("unexpected cube of size > 1");
    }
  }

public:
  pair<Status, vector<Expr>> cube() {
    z3::solver::cube_generator cg    = z().cubes();
    z3::solver::cube_iterator  start = cg.begin();
    z3::solver::cube_iterator  end   = cg.end();

    if (start == end) { return { Status::UNSAT, {} }; }

    z3::expr_vector cube1 = *start;
    validate_cube(cube1);

    assert(cube1.size() == 1);
    if (cube1[0].is_true()) { return { Status::SAT, {} }; }

    ++start;
    if (start == end) { /* failed lit */ return { Status::UNKNOWN, { Expr(cube1[0]) } }; }

    z3::expr_vector cube2 = *start;
    validate_cube(cube2);

    if (cube1 == cube2) {
      throw SolverException("Both cubes are the same: did you forget to increment the iterator?");
    }

    ++start;
    if (start != end) {
      throw SolverException("z3::cube() returned more than two cubes");
    }
    return { Status::UNKNOWN, { Expr(cube1[0]), Expr(cube2[0]) } };
  }

  Expr get_free_var(unsigned v_idx) {
    // Warning: slow
    z3::expr_vector non_units = z().non_units();
    if (v_idx >= non_units.size()) { throw SolverException("v_idx too big!"); }
    z3::expr e = non_units[v_idx];
    return Expr(e);
  }

  TFData to_tf_data() const {
    //if (propagate() != Status::UNKNOWN) { throw SolverException("to_tf_data(): propagate does not return UNKNOWN"); }
    TFDataManager dm(*this);
    if (dm.non_units.size() == 0) { throw SolverException("to_tf_data() but no free variables"); }
    if (dm.pruned_clauses.size() == 0) { throw SolverException("to_tf_data() but no pruned clauses"); }
    return TFData(dm.non_units.size(), dm.pruned_clauses.size(), dm.to_CL_idxs());
  }

  TFData to_tf_data_with_core() const {
    TFDataManager dm(*this);
    TFData tfd(dm.non_units.size(), dm.pruned_clauses.size(), dm.to_CL_idxs());
    tfd.core_clause_mask = compute_core_clause_mask(dm.pruned_clauses);
    if ((unsigned) tfd.core_clause_mask.size() != dm.pruned_clauses.size()) {
      throw SolverException(mstream() << "error while computing core_clause_mask: wrong number of clauses: " << tfd.core_clause_mask.size() << " vs " << tfd.n_clauses);
    }

    tfd.core_var_mask = ArrayXb::Constant(tfd.n_vars, false);
    for (unsigned c_idx = 0; c_idx < dm.pruned_clauses.size(); ++c_idx) {
      if (tfd.core_clause_mask[c_idx]) {
	z3::expr const & clause = dm.pruned_clauses[c_idx];
	for (unsigned arg_idx = 0; arg_idx < clause.num_args(); ++arg_idx) {
	  tfd.core_var_mask[dm.free_expr_to_lit(clause.arg(arg_idx)).var().idx()] = true;
	}
      }
    }
    return tfd;
  }

  vector<z3::expr> get_pruned_clauses() const {
    z3::expr_vector zclauses = z().assertions();
    vector<z3::expr> clauses;
    for (unsigned c_idx = 0; c_idx < zclauses.size(); ++c_idx) {
      if (zclauses[c_idx].is_or()) { clauses.push_back(zclauses[c_idx]); }
    }
    return clauses;
  }

  Solver clauses_to_solver(Context & ctx, vector<z3::expr> const & clauses, unsigned n_takes) const {
    if (n_takes > clauses.size()) { throw SolverException("n_takes too high"); }
    Solver s(ctx, _opts);
    for (unsigned i = 0; i < n_takes; ++i) {
      s.add(Expr(clauses[i]));
    }
    return s;
  }

  vector<TFData> get_more_cores(Context & ctx, unsigned max_tries, float percent_to_keep) const {
    // Current solver must be in UNKNOWN state, but must have been determined to be UNSAT
    vector<z3::expr> clauses = get_pruned_clauses();

    std::srand(unsigned(std::time(0)));
    std::random_shuffle(clauses.begin(), clauses.end());

    vector<TFData> results;
    unsigned n_take = clauses.size();

    for (unsigned core_idx = 0; core_idx < max_tries; ++core_idx) {
      n_take               = floor(n_take * percent_to_keep);
      Solver s             = clauses_to_solver(ctx, clauses, n_take);
      Status status        = s.check();
      if (status == Status::UNSAT) {
	Solver s_unsat = clauses_to_solver(ctx, clauses, n_take);
	if (s_unsat.propagate() == Status::UNKNOWN) {
	  results.push_back(s_unsat.to_tf_data_with_core());
	}
      } else {
	break;
      }
    }
    return results;
  }

private:
  static unordered_map<unsigned, unsigned> build_name_map_from_dimacs(string const & filename) {
    unordered_map<unsigned, unsigned> m;
    std::ifstream file(filename);
    string line;
    while (std::getline(file, line)) {
      std::istringstream iss(line);
      string result;
      if (std::getline(iss, result, ' ')) {
	if (result != "c") continue;

	std::getline(iss, result, ' ');
	unsigned dimacs_var = stoi(result);

	std::getline(iss, result, '\n');
	string z3_var_name = result;

	if (z3_var_name.substr(0, 2) != "k!") { throw SolverException(mstream() << "found variable without k!<int> name: " << z3_var_name); }
	m.insert({stoi(z3_var_name.substr(2)), dimacs_var});
      }
    }
    return m;
  }

  vector<vector<unsigned>> pruned_clauses_rename_to_dimacs_vars(vector<z3::expr> const & pruned_clauses,
								unordered_map<unsigned, unsigned> const & name_map) const {
    vector<vector<unsigned>> vs;

    for (z3::expr const & clause : pruned_clauses) {
      if (clause.num_args() < 2) { throw SolverException(mstream() << "or-clause with 1 argument:" << clause); }
      vs.emplace_back();

      for (unsigned arg_idx = 0; arg_idx < clause.num_args(); ++arg_idx) {
	  z3::expr e   = clause.arg(arg_idx);
	  bool     neg = e.is_not();
	  e            = neg ? e.arg(0) : e;
	  if (!e.is_const()) { throw SolverException(mstream() << "expected constant: " << e); }
	  unsigned var = name_map.at(e.decl().name().to_int());
	  unsigned lit = neg ? (var + total_n_vars()) : var;
	  vs.back().push_back(lit);
	}
	std::sort(vs.back().begin(), vs.back().end());
    }
    return vs;
  }

  vector<vector<unsigned>> clauses_prune_to_dimacs_vars(z3::expr_vector const & clauses) const {
    vector<vector<unsigned>> vs;

    for (unsigned c_idx = 0; c_idx < clauses.size(); ++c_idx) {
      z3::expr clause = clauses[c_idx];
      if (clause.is_or()) {
	if (clause.num_args() < 2) { throw SolverException(mstream() << "or-clause with 1 argument: " << clause); }
	vs.emplace_back(); // prune the clauses
	for (unsigned arg_idx = 0; arg_idx < clause.num_args(); ++arg_idx) {
	  z3::expr e   = clause.arg(arg_idx);
	  bool     neg = e.is_not();
	  e            = neg ? e.arg(0) : e;
	  if (!e.is_const()) { throw SolverException(mstream() << "expected constant: " << e); }
	  unsigned var = e.decl().name().to_int();
	  unsigned lit = neg ? (var + total_n_vars()) : var;
	  vs.back().push_back(lit);
	}
	std::sort(vs.back().begin(), vs.back().end());
      }
    }
    return vs;
  }

  string build_z3_drat_cmd(string const & drat_name, string const & dimacs_name) const {
    int buffer_size = 1000;
    char buffer[buffer_size];
    int cx = snprintf(buffer,
		      buffer_size,
		      "z3 -T:%u sat.max_conflicts=%u sat.restart.max=%u sat.variable_decay=%u sat.override_incremental=%s sat.acce=%s sat.lookahead_simplify=%s sat.drat.file=%s %s >> %s",
		      (_opts.z3_replay_timeout_scale * _opts.timeout_ms) / 1000,
		      _opts.max_conflicts,
		      _opts.restart_max,
		      _opts.variable_decay,
		      _opts.override_incremental ? "true" : "false",
		      _opts.acce ? "true" : "false",
		      _opts.lookahead_simplify ? "true" : "false",
		      drat_name.c_str(),
		      dimacs_name.c_str(),
		      _opts.logfilename.c_str());
    if (cx >= 0 && cx < buffer_size) {
      return string(buffer);
    } else {
      throw SolverException("z3 command too big for buffer!");
    }
  }

  string build_drat_trim_cmd(string const & dimacs_name, string const & drat_name, string const & core_name) const {
    int buffer_size = 1000;
    char buffer[buffer_size];
    int cx = snprintf(buffer,
		      buffer_size,
		      "drat-trim %s %s -c %s -t %u >> %s",
		      dimacs_name.c_str(),
		      drat_name.c_str(),
		      core_name.c_str(),
		      (_opts.drat_trim_timeout_scale * _opts.timeout_ms) / 1000,
		      _opts.logfilename.c_str());
    if (cx >= 0 && cx < buffer_size) {
      return string(buffer);
    } else {
      throw SolverException("drat-trim command too big for buffer!");
    }
  }

public:
ArrayXb compute_core_clause_mask(vector<z3::expr> const & pruned_clauses) const {

    string prefix      = std::tmpnam(nullptr);
    string dimacs_name = prefix + ".dimacs";
    string drat_name   = prefix + ".drat";
    string core_name   = prefix + ".core.dimacs";

    std::ofstream dimacs_out(dimacs_name, std::ios_base::out);
    dimacs_out << z().dimacs();
    dimacs_out.close();

    string debug_rm_cmd = string("rm -f ") + drat_name + " " + core_name;
    string rm_cmd = string("rm -f ") + drat_name + " " + core_name + " " + dimacs_name;
    string z3_cmd = build_z3_drat_cmd(drat_name, dimacs_name);

    std::ofstream flog(_opts.logfilename, std::ios_base::app);
    flog << "Executing '" << z3_cmd << "'..." << std::endl;

    system_throw(z3_cmd, debug_rm_cmd);
    check_exists_and_nonempty(drat_name);

    string drat_trim_cmd = build_drat_trim_cmd(dimacs_name, drat_name, core_name);
    flog << "Executing '" << drat_trim_cmd << "'..." << std::endl;
    flog.close();

    system_throw(drat_trim_cmd, debug_rm_cmd);
    check_exists_and_nonempty(core_name);

    z3::solver s_core(z().ctx(), "QF_FD");
    s_core.from_file(core_name.c_str());

    z3::expr_vector clauses_core = s_core.assertions();

    unordered_map<unsigned, unsigned> name_map = build_name_map_from_dimacs(dimacs_name);
    vector<vector<unsigned>>          oclauses = pruned_clauses_rename_to_dimacs_vars(pruned_clauses, name_map); // old
    vector<vector<unsigned>>          nclauses = clauses_prune_to_dimacs_vars(clauses_core); // new

    // oclauses_permutation[k] is the index in the original oclauses of sorted oclauses[k].
    vector<unsigned> oclauses_permutation;
    for (unsigned i = 0 ; i < oclauses.size(); ++i) { oclauses_permutation.push_back(i); }
    std::sort(oclauses_permutation.begin(),
	      oclauses_permutation.end(),
	      [&](const unsigned & i1, const unsigned & i2) { return (oclauses[i1] < oclauses[i2]); });

    std::sort(oclauses.begin(), oclauses.end());
    std::sort(nclauses.begin(), nclauses.end());

    // construct core_clause_mask
    ArrayXb core_clause_mask = ArrayXb::Constant(oclauses.size(), false);

    unsigned o_idx = 0; // old_idx
    for (unsigned n_idx = 0; n_idx < nclauses.size(); ++n_idx) {
      while (oclauses[o_idx] != nclauses[n_idx]) {
	o_idx++;
	if (o_idx >= oclauses.size()) { throw SolverException(mstream() << "core clause not present in original clauses"); }
      }
      assert(oclauses[o_idx] == nclauses[n_idx]);
      core_clause_mask[oclauses_permutation[o_idx]] = true;
    }

    system_throw(rm_cmd, "echo 'no finally'");
    return core_clause_mask;
  }

private:
  vector<z3::expr> collect_pruned_clauses() const {
    // Warning: slow
    z3::expr_vector clauses = z().assertions();
    vector<z3::expr> pruned_clauses;

    for (unsigned c_idx = 0; c_idx < clauses.size(); ++c_idx) {
      z3::expr const & clause = clauses[c_idx];
      if (clause.is_or()) {
	if (clause.num_args() < 2) { throw SolverException(mstream() << "cleaned clause has singleton or: " << clause); }
	pruned_clauses.push_back(clause);
      }
    }
    return pruned_clauses;
  }

public:
  void set_activity_scores(Eigen::ArrayXf const & pi, unsigned pi_scale) {
    z3::expr_vector non_units = z().non_units();
    for (unsigned i = 0; i < non_units.size(); ++i) {
      double act = pi_scale * pi[i];
      z().set_activity(non_units[i], act);
    }
  }

};

Solver deserialize(Context & ctx, Options const & opts, string const & serial) {
  Solver s(ctx, opts);
  s.from_string(serial);
  return s;
}

TFDataManager::TFDataManager(Solver const & s): non_units(s.z().non_units()) {
  for (unsigned i = 0; i < non_units.size(); ++i) {
    _non_unit_to_var.insert({non_units[i], Var(i)});
  }
  pruned_clauses = s.get_pruned_clauses();
  for (z3::expr const & clause : pruned_clauses) {
    n_cells += clause.num_args();
  }
}

// Pybind11
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/eigen.h>

namespace py = pybind11;

PYBIND11_MODULE(solver, m) {
  py::register_exception<SolverException>(m, "SolverException");

  py::class_<Var>(m, "Var")
    .def(py::init<unsigned>(), py::arg("idx"))
    .def("idx", &Var::idx)
    .def("__str__", &Var::repr)
    .def("__repr__", &Var::repr)
    .def("__eq__", &Var::operator==)
    .def("__lt__", &Var::operator<)
    .def("__hash__", &Var::hash)
    .def(py::pickle([](const Var & var) { return var.idx(); },
		    [](unsigned idx) { return Var(idx); }));

  py::class_<Lit>(m, "Lit")
    .def(py::init<Var, bool>(), py::arg("var"), py::arg("sign"))
    .def("var", &Lit::var)
    .def("neg", &Lit::neg)
    .def("vidx", &Lit::vidx)
    .def("flip", &Lit::flip)
    .def("__str__", &Lit::repr)
    .def("__repr__", &Lit::repr)
    .def("__eq__", &Lit::operator==)
    .def("__lt__", &Lit::operator<)
    .def("__hash__", &Lit::hash)
    .def(py::pickle([](const Lit & lit) { return py::make_tuple(lit.var().idx(), lit.neg()); },
		    [](py::tuple t) { return Lit(Var(t[0].cast<unsigned>()), t[1].cast<bool>()); }));

  py::class_<Options>(m, "Options")
    .def(py::init<>())
    .def_readwrite("timeout_ms", &Options::timeout_ms)
    .def_readwrite("max_conflicts", &Options::max_conflicts)
    .def_readwrite("restart_max", &Options::restart_max)
    .def_readwrite("variable_decay", &Options::variable_decay)
    .def_readwrite("override_incremental", &Options::override_incremental)
    .def_readwrite("lookahead_simplify", &Options::lookahead_simplify)
    .def_readwrite("acce", &Options::acce)
    .def_readwrite("z3_replay_timeout_scale", &Options::z3_replay_timeout_scale)
    .def_readwrite("drat_trim_timeout_scale", &Options::drat_trim_timeout_scale)
    .def_readwrite("logfilename", &Options::logfilename);

  py::class_<Context>(m, "Context")
    .def(py::init<>());

  py::class_<Expr>(m, "Expr")
    .def("flip", &Expr::flip)
    .def("var", &Expr::var)
    .def("is_neg", &Expr::is_neg)
    .def("ilit", &Expr::ilit);

  // TODO(dselsam): make all caps to be consistent
  py::enum_<Status>(m, "Status")
    .value("UNKNOWN", Status::UNKNOWN)
    .value("UNSAT", Status::UNSAT)
    .value("SAT", Status::SAT);

  py::enum_<Assignment>(m, "Assignment")
    .value("POS", Assignment::POS)
    .value("NEG", Assignment::NEG)
    .value("SKIP", Assignment::SKIP);

  py::class_<TFData>(m, "TFData")
    .def_readonly("n_vars", &TFData::n_vars)
    .def_readonly("n_clauses", &TFData::n_clauses)
    .def_readonly("CL_idxs", &TFData::CL_idxs)
    .def_readonly("core_var_mask", &TFData::core_var_mask)
    .def_readonly("core_clause_mask", &TFData::core_clause_mask);

  py::class_<Solver>(m, "Solver")
    .def(py::init<Context &, Options const & >(),
	 py::arg("ctx"), py::arg("opts"),
	 py::keep_alive<1, 2>(),
	 py::call_guard<py::gil_scoped_release>())

    .def("clone", &Solver::clone, py::arg("ctx"), py::keep_alive<0, 2>(), py::call_guard<py::gil_scoped_release>())
    .def("from_string", &Solver::from_string, py::arg("s"), py::call_guard<py::gil_scoped_release>())
    .def("from_file", &Solver::from_file, py::arg("filename"), py::call_guard<py::gil_scoped_release>())
    .def("serialize", &Solver::serialize, py::call_guard<py::gil_scoped_release>())
    .def("dimacs", &Solver::dimacs, py::call_guard<py::gil_scoped_release>())
    .def("add", &Solver::add, py::arg("expr"))
    .def("get_free_var", &Solver::get_free_var, py::arg("v_idx"))
    .def("propagate", &Solver::propagate, py::call_guard<py::gil_scoped_release>())
    .def("check", &Solver::check, py::call_guard<py::gil_scoped_release>())
    .def("check_with_timeout_ms", &Solver::check_with_timeout_ms, py::arg("timeout_ms"), py::call_guard<py::gil_scoped_release>())
    .def("set_activity_scores", &Solver::set_activity_scores, py::arg("pi"), py::arg("pi_scale"), py::call_guard<py::gil_scoped_release>())
    .def("cube", &Solver::cube, py::call_guard<py::gil_scoped_release>())

    .def("to_tf_data", &Solver::to_tf_data, py::call_guard<py::gil_scoped_release>())
    .def("to_tf_data_with_core", &Solver::to_tf_data_with_core, py::call_guard<py::gil_scoped_release>())
    .def("get_more_cores", &Solver::get_more_cores, py::arg("ctx"), py::arg("max_tries"), py::arg("percent_to_keep"), py::call_guard<py::gil_scoped_release>());

  m.def("deserialize", &deserialize, py::arg("ctx"), py::arg("opts"), py::arg("serial"), py::keep_alive<0, 1>(), py::call_guard<py::gil_scoped_release>());
}


/*! \file amr.hpp
\brief Abstract class for amr
\author Elad Steinberg
*/

#ifndef AMR_HPP
#define AMR_HPP 1

#include "computational_cell_2d.hpp"
#include "extensive.hpp"
#include "../common/equation_of_state.hpp"
#include "OuterBoundary.hpp"
#include "../../tessellation/tessellation.hpp"
#include "../../tessellation/ConvexHull.hpp"
#include "clipper.hpp"
#include "../test_2d/main_loop_2d.hpp"
#include "../../tessellation/polygon_overlap_area.hpp"
#include <boost/scoped_ptr.hpp>
#include "interpolations/LinearGaussImproved.hpp"

//! \brief Abstract class for cell update scheme in amr
class AMRCellUpdater
{
public:

	/*! \brief Calculates the computational cell
	\param extensive Extensive conserved variables
	\param eos Equation of state
	\param volume Cell volume
	\param old_cell Old computational cell
	\param tracerstickernames The names of the tracers and stickers
	\return Computational cell
	*/
	virtual ComputationalCell ConvertExtensiveToPrimitve(const Extensive& extensive,const EquationOfState& eos,
		double volume,ComputationalCell const& old_cell,TracerStickerNames const& tracerstickernames) const = 0;

	//! \brief Class destructor
	virtual ~AMRCellUpdater(void);
};

//! \brief Abstract class for extensive update scheme in amr
class AMRExtensiveUpdater
{
public:

	/*! \brief Calculates the computational cell
	\param cell Computational cell
	\param eos Equation of state
	\param volume Cell volume
	\param tracerstickernames The names of the tracers and stickers
	\return Extensive
	*/
	virtual Extensive ConvertPrimitveToExtensive(const ComputationalCell& cell, const EquationOfState& eos,
		double volume,TracerStickerNames const& tracerstickernames) const = 0;

	//! \brief Class destructor
	virtual ~AMRExtensiveUpdater(void);
};

//! \brief Simple class for extensive update scheme in amr
class SimpleAMRExtensiveUpdater : public AMRExtensiveUpdater
{
public:
	Extensive ConvertPrimitveToExtensive(const ComputationalCell& cell, const EquationOfState& eos,
		double volume, TracerStickerNames const& tracerstickernames) const;
};

//! \brief Simple class for cell update scheme in amr
class SimpleAMRCellUpdater : public AMRCellUpdater
{
private:
	const vector<string>  toskip_;
public:
	/*!
	\brief class constructor
	\param toskip A list of sticker names to skip their cell update
	*/
  explicit SimpleAMRCellUpdater(vector<string> toskip);

	ComputationalCell ConvertExtensiveToPrimitve(const Extensive& extensive, const EquationOfState& eos,
		double volume, ComputationalCell const& old_cell,TracerStickerNames const& tracerstickernames) const;
};

//! \brief Chooses which cells should be remove
class CellsToRemove
{
public:
	/*!
	\brief Finds the cells to remove
	\param tess The tesselation
	\param cells The computational cells
	\param time The sim time
	\param tracerstickernames The names of the tracers and stickers
	\return The indeces of cells to remove with a corresponding merit which decides if there are neighboring cells which one to choose to remove
	*/
	virtual std::pair<vector<size_t>,vector<double> > ToRemove(Tessellation const& tess,
		vector<ComputationalCell> const& cells,double time,
		TracerStickerNames const& tracerstickernames)const=0;

	//! \brief Virtual destructor
	virtual ~CellsToRemove(void);
};

//! \brief Chooses which cells should be refined
class CellsToRefine
{
public:
	/*!
	\brief Finds the cells to refine
		\param tess The tesselation
		\param cells The computational cells
		\param time The sim time
		\param tracerstickernames The names of the tracers and stickers
		\return The indeces of cells to remove
     */
	virtual vector<size_t> ToRefine(Tessellation const& tess, vector<ComputationalCell> const& cells, double time,
		TracerStickerNames const& tracerstickernames)const = 0;

	//! \brief Virtual destructor
	virtual ~CellsToRefine(void);
};

//! \brief Base class for amr
class AMR : public Manipulate
{
protected:

  /*! \brief Calculates the positions of the new points
    \param ToRefine points to refine
    \param tess Tessellation
    \param NewPoints output
    \param Moved displacement for periodic grid
    \param obc Outer boundary conditions
    */
#ifdef RICH_MPI
  //!    \param proc_chull Cells' convex hull
#endif // RICH_MPI
  void GetNewPoints
  (vector<size_t> const& ToRefine,
   Tessellation const& tess,
   vector<std::pair<size_t, Vector2D> > &NewPoints,
   vector<Vector2D> &Moved,
   OuterBoundary const& obc
#ifdef RICH_MPI
	  , vector<Vector2D> const& proc_chull
#endif
	  )const;
#ifdef RICH_MPI

  /*! \brief Removes points because they are near the edge of a cpu domain
    \param ToRemove Candidates for AMR
	\param merits The merits for points to be removed. given as input and output. should be empty vector for refinement
    \param tess Tessellation
    \return The new indices and merits of points
   */
  vector<size_t> RemoveNearBoundaryPoints(vector<size_t> const&ToRemove,
	  Tessellation const& tess,vector<double> &merits)const;
#endif

  /*! \brief Calculates the positions of the new points like AREPO
  \param ToRefine points to refine
  \param tess Tessellation
  \param NewPoints output
  \param Moved displacement for periodic grid
  \param obc Outer boundary conditions
  */
  void GetNewPoints2
	  (vector<size_t> const& ToRefine,
		  Tessellation const& tess,
		  vector<std::pair<size_t, Vector2D> > &NewPoints,
		  vector<Vector2D> &Moved,
		  OuterBoundary const& obc)const;

public:
	/*!
	\brief Runs the AMR
	\param sim The sim object
	*/
	virtual void operator() (hdsim &sim) = 0;
	/*!
	\brief Runs the refine
	\param tess The tessellation
	\param cells The computational cells
	\param eos The equation of state
	\param extensives The extensive variables
	\param time The sim time
	\param obc Outer boundary conditions
	\param tracerstickernames The names of the tracers and stickers
	*/
#ifdef RICH_MPI
  //! \param proctess Tessellation of the processes (for parallel runs)
#endif // RICH_MPI
	virtual void UpdateCellsRefine(Tessellation &tess,
		OuterBoundary const& obc, vector<ComputationalCell> &cells, EquationOfState const& eos,
		vector<Extensive> &extensives, double time,
#ifdef RICH_MPI
		Tessellation const& proctess,
#endif
		TracerStickerNames const& tracerstickernames)const = 0;
	/*!
	\brief Runs the removal
	\param tess The tessellation
	\param cells The computational cells
	\param eos The equation of state
	\param extensives The extensive variables
	\param time The sim time
	\param obc The outer boundary conditions
	\param tracerstickernames The names of the tracers and stickers
	*/
#ifdef RICH_MPI
  //!	\param proctess Tessellation of the processes (for parallel runs)
#endif // RICH_MPI
	virtual void UpdateCellsRemove(Tessellation &tess,
		OuterBoundary const& obc, vector<ComputationalCell> &cells, vector<Extensive> &extensives,
		EquationOfState const& eos, double time,
#ifdef RICH_MPI
		Tessellation const& proctess,
#endif
		TracerStickerNames const& tracerstickernames)const = 0;
	//! \brief Virtual destructor
	virtual ~AMR(void);
};

//! \brief Conservative amr
//! \todo Make sure AMR works with all physical geometries
class ConservativeAMR : public AMR
{
private:
	CellsToRefine const& refine_;
	CellsToRemove const& remove_;
  SimpleAMRCellUpdater scu_;
  SimpleAMRExtensiveUpdater seu_;
  const bool periodic_;
	AMRCellUpdater* cu_;
	AMRExtensiveUpdater* eu_;
	LinearGaussImproved *interp_;

	ConservativeAMR(ConservativeAMR const& other);

	ConservativeAMR& operator=(ConservativeAMR const& other);

public:
	void operator() (hdsim &sim);

  /*! \brief Class constructor
    \param refine Refinement scheme
    \param remove Removal scheme
    \param cu Cell updater
    \param eu Extensive updater
    \param slopes Slopes
	\param periodic Flag whether the boundary is periodic or not
   */
	ConservativeAMR
	(CellsToRefine const& refine,
	 CellsToRemove const& remove,
		bool periodic = false,
	 LinearGaussImproved *slopes = 0,
	 AMRCellUpdater* cu=0,
	 AMRExtensiveUpdater* eu=0);

	void UpdateCellsRefine(Tessellation &tess,
		OuterBoundary const& obc, vector<ComputationalCell> &cells,EquationOfState const& eos,
		vector<Extensive> &extensives,double time,
#ifdef RICH_MPI
		Tessellation const& proctess,
#endif
		TracerStickerNames const& tracerstickernames)const;

	void UpdateCellsRemove(Tessellation &tess,
		OuterBoundary const& obc, vector<ComputationalCell> &cells, vector<Extensive> &extensives,
		EquationOfState const& eos,double time,
#ifdef RICH_MPI
		Tessellation const& proctess,
#endif
		TracerStickerNames const& tracerstickernames)const;
};

//! \brief Non conservative amr
class NonConservativeAMR : public AMR
{
private:
	CellsToRefine const& refine_;
	CellsToRemove const& remove_;
  SimpleAMRCellUpdater scu_;
  SimpleAMRExtensiveUpdater seu_;
  LinearGaussImproved *interp_;
	AMRExtensiveUpdater* eu_;

	NonConservativeAMR(NonConservativeAMR const& other);

	NonConservativeAMR& operator=(NonConservativeAMR const& other);

public:
	void operator() (hdsim &sim);

  /*! \brief Class constructor
    \param refine Refinement scheme
    \param remove Removal scheme
    \param eu Extensive updater
	\param slopes Slopes
   */
  NonConservativeAMR
  (CellsToRefine const& refine,
   CellsToRemove const& remove,
	  LinearGaussImproved *slopes = 0,
   AMRExtensiveUpdater* eu = 0);

	void UpdateCellsRefine(Tessellation &tess,
		OuterBoundary const& obc, vector<ComputationalCell> &cells, EquationOfState const& eos,
		vector<Extensive> &extensives, double time,
#ifdef RICH_MPI
		Tessellation const& proctess,
#endif
		TracerStickerNames const& tracerstickernames)const;

	void UpdateCellsRemove(Tessellation &tess,
		OuterBoundary const& obc, vector<ComputationalCell> &cells, vector<Extensive> &extensives,
		EquationOfState const& eos, double time,
#ifdef RICH_MPI
		Tessellation const& proctess,
#endif
		TracerStickerNames const& tracerstickernames)const;
};

//! \brief Conservative amr using old method to split cells
//! \todo Make sure AMR works with all physical geometries
class ConservativeAMROld : public AMR
{
private:
	CellsToRefine const& refine_;
	CellsToRemove const& remove_;
	SimpleAMRCellUpdater scu_;
	SimpleAMRExtensiveUpdater seu_;
	AMRCellUpdater* cu_;
	AMRExtensiveUpdater* eu_;
	LinearGaussImproved *interp_;

	ConservativeAMROld(ConservativeAMROld const& other);

	ConservativeAMROld& operator=(ConservativeAMROld const& other);

public:
	void operator() (hdsim &sim);

	/*! \brief Class constructor
	\param refine Refinement scheme
	\param remove Removal scheme
	\param cu Cell updater
	\param eu Extensive updater
	\param slopes Slopes
	*/
	ConservativeAMROld
		(CellsToRefine const& refine,
			CellsToRemove const& remove,
			LinearGaussImproved *slopes = 0,
			AMRCellUpdater* cu = 0,
			AMRExtensiveUpdater* eu = 0);

	void UpdateCellsRefine(Tessellation &tess,
		OuterBoundary const& obc, vector<ComputationalCell> &cells, EquationOfState const& eos,
		vector<Extensive> &extensives, double time,
#ifdef RICH_MPI
		Tessellation const& proctess,
#endif
		TracerStickerNames const& tracerstickernames)const;

	void UpdateCellsRemove(Tessellation &tess,
		OuterBoundary const& obc, vector<ComputationalCell> &cells, vector<Extensive> &extensives,
		EquationOfState const& eos, double time,
#ifdef RICH_MPI
		Tessellation const& proctess,
#endif
		TracerStickerNames const& tracerstickernames)const;
};

#endif // AMR_HPP


//
// Created by Arnold Müller on 18.02.17.
//

#include "Properties.h"
#include <boost/filesystem.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>

const std::string Properties::filename_ { "simulation.json"};

Properties::Properties(const std::string configname) : configname_(configname)
{
    ReadConfigFile(Properties::filename_, configname_);
}

Properties::~Properties() {
    WriteConfigFile(Properties::filename_);
}

bool Properties::ReadConfigFile(const std::string& filename, const std::string& configname) {
    using boost::property_tree::iptree;
    if (ConfigFileExists()) {
        iptree pt;
        boost::property_tree::read_json(Properties::filename_, pt);
        iptree& def = pt.get_child(configname);

        simulation_intervall        = def.get<unsigned>("simulation.intervall", simulation_intervall);
        simulation_f                = def.get<double>("simulation.f", simulation_f);
        universe_g                  = def.get<double>("universe.g", universe_g);
        universe_planet_density     = def.get<double>("universe.planet.density", universe_planet_density);
        universe_collision_joindist = def.get<double>("universe.collision.joindist", universe_collision_joindist);
        universe_collision_k        = def.get<double>("universe.collision.k", universe_collision_k);
        generate_n                  = def.get<unsigned>("generate.n", generate_n);
        generate_speed_min          = def.get<double>("generate.speed.min", generate_speed_min);
        generate_speed_max          = def.get<double>("generate.speed.max", generate_speed_max);
        generate_border_part        = def.get<double>("generate.border.part", generate_border_part);
        generate_mass_min           = def.get<double>("generate.mass.min", generate_mass_min);
        generate_mass_max           = def.get<double>("generate.mass.max", generate_mass_max);
        generate_mass_alpha         = def.get<double>("generate.mass.alpha", generate_mass_alpha);
        generate_mass_beta          = def.get<double>("generate.mass.beta", generate_mass_beta);
        generate_pos_xmax           = def.get<double>("generate.pos.xmax", generate_pos_xmax);
        generate_pos_ymax           = def.get<double>("generate.pos.ymax", generate_pos_xmax);
        view_forcefield_show        = def.get<bool>("view.forcefield.show", view_forcefield_show);
        view_forcefield_columns     = def.get<unsigned>("view.forcefield.columns", view_forcefield_columns);
        return true;
    }
    return false;
}

bool Properties::ConfigFileExists() const {
    return boost::filesystem::exists(boost::filesystem::path(Properties::filename_));
}

bool Properties::WriteConfigFile(const std::string &filename) {
    using boost::property_tree::iptree;
    iptree def;
    if (ConfigFileExists()) {
        boost::property_tree::read_json(Properties::filename_, def);
    }

    def.put<unsigned>(configname_ + ".simulation.intervall", simulation_intervall);
    def.put<double>(configname_ + ".simulation.f", simulation_f);
    def.put<double>(configname_ + ".universe.g", universe_g);
    def.put<double>(configname_ + ".universe.planet.density", universe_planet_density);
    def.put<double>(configname_ + ".universe.collision.joindist", universe_collision_joindist);
    def.put<double>(configname_ + ".universe.collision.k", universe_collision_k);
    def.put<unsigned>(configname_ + ".generate.n", generate_n);
    def.put<double>(configname_ + ".generate.speed.min", generate_speed_min);
    def.put<double>(configname_ + ".generate.speed.max", generate_speed_max);
    def.put<double>(configname_ + ".generate.border.part", generate_border_part);
    def.put<double>(configname_ + ".generate.mass.min", generate_mass_min);
    def.put<double>(configname_ + ".generate.mass.max", generate_mass_max);
    def.put<double>(configname_ + ".generate.mass.alpha", generate_mass_alpha);
    def.put<double>(configname_ + ".generate.mass.beta", generate_mass_beta);
    def.put<double>(configname_ + ".generate.pos.xmax", generate_pos_xmax);
    def.put<double>(configname_ + ".generate.pos.ymax", generate_pos_ymax);
    def.put<bool>(configname_ + ".view.forcefield.show", view_forcefield_show);
    def.put<unsigned>(configname_ + ".view.forcefield.columns", view_forcefield_columns);

    boost::property_tree::write_json(Properties::filename_, def);
    return false;
}

void Properties::AddReaderWriter(PropertiesReaderWriter &p) {
    this->readerwriters_.insert(&p);
}

void Properties::RemoveReaderWriter(PropertiesReaderWriter &p) {
    this->readerwriters_.erase(&p);
}

void Properties::UpdateReaders() const {
    for(auto& r : this->readerwriters_) {
        r->ReadProperties(*this);
    }
}

void Properties::UpdateWriters() {
    for(auto& w : this->readerwriters_) {
        w->WriteProperties(*this);
    }
}


//==================================================================================================
/*!
  @file

  @copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_FUNCTION_SCALAR_REALPOW_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_SCALAR_REALPOW_HPP_INCLUDED

#include <boost/simd/function/definition/realpow.hpp>
#include <boost/simd/arch/common/scalar/function/realpow.hpp>

#endif


#include <boost/geometry/strategies/cartesian/side_by_triangle.hpp>


#include <ctime>
#include <iostream>
#include <memory>

#include <Eigen/Core>

#include <ippp/Core.h>
#include <ippp/Environment.h>
#include <ippp/Planner.h>
#include <ippp/UI.h>

using namespace ippp;

void printTime(clock_t begin, clock_t end) {
    double elapsed_secs = double(end - begin) / CLOCKS_PER_SEC;
    std::cout << "Computation time: " << elapsed_secs << std::endl;
}

void simpleRRT() {
    const unsigned int dim = 6;
    std::shared_ptr<Jaco> robot(new Jaco());
    std::shared_ptr<Environment> environment(new Environment(3, AABB(Vector3(-200, -200, -200), Vector3(200, 200, 200)), robot));
    // environment->addObstacle(obstacleModel);

    robot->saveMeshConfig(util::Vecd(0, 0, 0, 0, 0, 0));

    ModuleConfigurator<dim> creator;
    creator.setEnvironment(environment);
    creator.setCollisionType(CollisionType::PQP);

    RRT<dim> planner(environment, creator.getRRTOptions(30), creator.getGraph());
    Vector6 start = util::Vecd(180, 180, 180, 180, 180, 180);
    Vector6 goal = util::Vecd(275, 167.5, 57.4, 241, 82.7, 75.5);

    // compute the tree
    clock_t begin = std::clock();
    bool connected = planner.computePath(start, goal, 4000, 6);
    clock_t end = std::clock();
    printTime(begin, end);

    std::vector<std::shared_ptr<Node<dim>>> nodes = planner.getGraphNodes();
    std::vector<Transform> graphPoints;
    std::cout << "Init Graph has: " << nodes.size() << "nodes" << std::endl;

    for (auto & node : nodes)
        graphPoints.push_back(std::dynamic_pointer_cast<Jaco>(robot)->directKinematic(node->getValues()));
    //writer::writeVecsToFile<dim>(graphPoints, "example.ASC", 10);

    if (connected) {
        std::cout << "Init and goal could be connected!" << std::endl;
        std::vector<Vector6> pathAngles = planner.getPath(5);

        std::vector<Transform> pathPoints;
        pathPoints.reserve(pathAngles.size());
        for (const auto& angles : pathAngles)
            pathPoints.push_back(std::dynamic_pointer_cast<Jaco>(robot)->directKinematic(angles));
        // writer::appendVecsToFile<dim>(pathPoints, "example.ASC", 10);

        // Helper vrep(dim);
        // vrep.start();
        // for (auto angles : pathAngles)
        //    vrep.setPos(angles);
    }
}

void treeConnection() {
    const unsigned int dim = 6;
    std::shared_ptr<RobotBase> robot(new Jaco());
    std::shared_ptr<Environment> environment(new Environment(3, AABB(Vector3(-200, -200, -200), Vector3(200, 200, 200)), robot));
    // environment->addObstacle(obstacleModel);

    // create two trees from init and from goal
    ModuleConfigurator<dim> creator1;
    creator1.setEnvironment(environment);
    creator1.setCollisionType(CollisionType::PQP);
    ModuleConfigurator<dim> creator2;
    creator2.setEnvironment(environment);
    creator2.setCollisionType(CollisionType::PQP);

    RRTStar<dim> plannerGoalNode(environment, creator1.getRRTOptions(20), creator1.getGraph());
    RRTStar<dim> plannerInitNode(environment, creator2.getRRTOptions(20), creator2.getGraph());

    // set properties to the planners
    plannerInitNode.setInitNode(util::Vecd(180, 180, 180, 180, 180, 180));
    plannerGoalNode.setInitNode(util::Vecd(275, 167.5, 57.4, 241, 82.7, 75.5));

    // compute the tree
    clock_t begin = std::clock();
    plannerInitNode.computeTree(20000, 2);
    plannerGoalNode.computeTree(20000, 2);
    clock_t end = std::clock();
    printTime(begin, end);

    // get random sample from the first pathPlanner and try to connect to both planners
    Eigen::VectorXd goal;
    bool connected = false;
    double minCost = std::numeric_limits<double>::max();
    for (int i = 0; i < 10000; ++i) {
        //        Vec<double> sample = plannerInitNode.getSamplePoint();
        //
        //        bool planner1Connected = plannerInitNode.connectGoalNode(sample);
        //        bool planner2Connected = plannerGoalNode.connectGoalNode(sample);
        //        if (planner1Connected && planner2Connected) {
        //            double cost = plannerInitNode.getGoalNode()->getCost() + plannerGoalNode.getGoalNode()->getCost();
        //            if (cost < minCost) {
        //                goal = sample;
        //                connected = true;
        //            }
        //        }
    }

    std::vector<std::shared_ptr<Node<dim>>> nodes = plannerInitNode.getGraphNodes();
    std::vector<Transform> graphPoints;
    std::cout << "Init Graph has: " << nodes.size() << "nodes" << std::endl;

    graphPoints.reserve(nodes.size());
    for (auto& node : nodes)
        graphPoints.push_back(std::dynamic_pointer_cast<Jaco>(robot)->directKinematic(node->getValues()));
    // writer::writeVecsToFile<dim>(graphPoints, "example.ASC", 10);

    nodes = plannerGoalNode.getGraphNodes();
    std::cout << "Goal Graph has: " << nodes.size() << "nodes" << std::endl;
    for (auto& node : nodes)
        graphPoints.push_back(std::dynamic_pointer_cast<Jaco>(robot)->directKinematic(node->getValues()));
    // writer::appendVecsToFile<dim>(graphPoints, "example.ASC", 10);

    if (connected) {
        std::cout << "Init and goal could be connected!" << std::endl;
        plannerInitNode.connectGoalNode(goal);
        plannerGoalNode.connectGoalNode(goal);

        std::vector<Vector6> pathAngles = plannerInitNode.getPath(5);
        std::vector<Vector6> temp = plannerGoalNode.getPath(5);
        pathAngles.insert(pathAngles.end(), temp.begin(), temp.end());

        std::vector<Transform> pathPoints;
        pathPoints.reserve(pathAngles.size());
        for (const auto& angles : pathAngles)
            pathPoints.push_back(std::dynamic_pointer_cast<Jaco>(robot)->directKinematic(angles));
        // writer::appendVecsToFile<dim>(pathPoints, "example.ASC", 10);

        // for (int i = 0; i < pathPoints.size(); ++i)
        //    vrep->setPos(pathAngles[i]);
    }
}

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

    return 0;
}


// Copyright (C) 2021 Christian Brommer, Control of Networked Systems, University of Klagenfurt, Austria.
//
// All rights reserved.
//
// This software is licensed under the terms of the BSD-2-Clause-License with
// no commercial use allowed, the full terms of which are made available
// in the LICENSE file. No license in patents is granted.
//
// You can contact the author at <christian.brommer@ieee.org>

#include <gmock/gmock.h>
#include <mars/sensors/vision/vision_measurement_type.h>
#include <mars/sensors/vision/vision_sensor_class.h>
#include <mars/sensors/vision/vision_sensor_state_type.h>
#include <mars/type_definitions/base_states.h>
#include <mars/type_definitions/buffer_data_type.h>
#include <mars/type_definitions/buffer_entry_type.h>
#include <Eigen/Dense>

class mars_vision_sensor_test : public testing::Test
{
public:
};

TEST_F(mars_vision_sensor_test, CTOR_POSE_SENSOR)
{
  mars::CoreState core_states;
  std::shared_ptr<mars::CoreState> core_states_sptr = std::make_shared<mars::CoreState>(core_states);
  mars::VisionSensorClass vision_sensor("Vision", core_states_sptr);
}

TEST_F(mars_vision_sensor_test, VISION_SENSOR_MEASUREMENT)
{
  Eigen::Vector3d position;               // Position [x y z]
  Eigen::Quaternion<double> orientation;  // Quaternion [W X Y Z]

  position << 1, 2, 3;
  orientation = Eigen::Quaternion<double>(1, 0, 0, 0);
  mars::VisionMeasurementType measurement(position, orientation);

  Eigen::IOFormat OctaveFmt(Eigen::StreamPrecision, 0, ", ", ";\n", "", "", "[", "]");
  std::cout << measurement.position_.format(OctaveFmt) << std::endl;
  std::cout << measurement.orientation_.coeffs().format(OctaveFmt) << std::endl;
}

TEST_F(mars_vision_sensor_test, VISION_SENSOR_INIT)
{
  Eigen::Vector3d position;               // Position [x y z]
  Eigen::Quaternion<double> orientation;  // Quaternion [W X Y Z]

  position << 1, 2, 3;
  orientation = Eigen::Quaternion<double>(1, 0, 0, 0);
  mars::VisionMeasurementType measurement(position, orientation);

  mars::CoreState core_states;
  std::shared_ptr<mars::CoreState> core_states_sptr = std::make_shared<mars::CoreState>(core_states);
  mars::VisionSensorClass vision_sensor("Vision", core_states_sptr);

  EXPECT_DEATH(vision_sensor.Initialize(1, std::make_shared<mars::VisionMeasurementType>(measurement),
                                        std::make_shared<mars::CoreType>()),
               "");
}

TEST_F(mars_vision_sensor_test, VISION_UPDATE)
{
  Eigen::Vector3d position;               // Position [x y z]
  Eigen::Quaternion<double> orientation;  // Quaternion [W X Y Z]

  position << 1, 2, 3;
  orientation = Eigen::Quaternion<double>(1, 0, 0, 0);
  mars::VisionMeasurementType measurement(position, orientation);

  mars::CoreState core_states;
  std::shared_ptr<mars::CoreState> core_states_sptr = std::make_shared<mars::CoreState>(core_states);
  mars::VisionSensorClass vision_sensor("Vision", core_states_sptr);

  mars::CoreStateType prior_core_state;
  mars::BufferDataType prior_sensor_buffer_data;
  Eigen::Matrix<double, prior_core_state.size_error_ + 15, prior_core_state.size_error_ + 15> prior_cov;
  prior_cov.setIdentity();

  // TODO no update without init
  //  int timestamp = 1;
  //  mars::BufferDataType test =
  //      vision_sensor.CalcUpdate(timestamp, std::make_shared<mars::VisionMeasurementType>(measurement),
  //      prior_core_state,
  //                             prior_sensor_buffer_data.sensor_, prior_cov);
}


/** Similarity Search
“© Copyright 2017  Hewlett Packard Enterprise Development LP
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.”
*/
#include <boost/asio/io_service.hpp>
#include <boost/thread/thread.hpp>
#include <boost/atomic.hpp>
#include "headers.h"
#include "SelfTuning.h"
#include <iostream>
#include <vector>
#include <string>
#include "TimeSeriesBuilder.h"
#include "LSHManager.h"
#include "LSHHashFunction.h"
#include "IndexSearch.h"
#include "NaiveSearch.h"
#include "MergeResult.h"
#include <fstream>
#include <chrono>
#include <thread>

/*
#include <boost/bind.hpp>
#include <boost/asio.hpp>
*/

using namespace std;

/* struct to hold data to be passed to a thread
 *    this shows how multiple data items can be passed to a thread */
// SEarchCoordinatorTracker should be added
void testNaiveSearch(int topk_no, string datafile, string queryfile) {
	// create LSHManager
	LSHManager* pLSH = LSHManager::getInstance();

	timespec before, after;

	// create TimeSeriesBuilder to populate time series data
	TimeSeriesBuilder& tBuilder = pLSH->getTimeSeriesBuilder();
	tBuilder.deserialize(datafile);
cout << "Timeseries read done" << endl;

	tBuilder.constructCompactTs();

	SearchCoordinatorTracker& tracker = pLSH->getSearchCoordinatorTracker();
	string sId("search");

	std::ifstream infile(queryfile);
	int query_id = 0;
	for (std::string line; std::getline(infile, line); ) {
		vector<RealT> query;

		// get query points
		char* str = (char*)line.c_str();
		char* pch = strtok (str," ");
	//id = atoi(pch);
	//pch = strtok (NULL, ",");
		cout << "\n\n*****************query " << query_id++ << endl;
	//int i=0;
		while (pch != NULL)
		{
			query.push_back(atof(pch));
	//cout << i << "," << query[i] << endl;
			pch = strtok (NULL, " ");
	//	i++;
		}

        tracker.addSearchCoordinator(sId,query,topk_no);
        SearchCoordinator& searchCoord = tracker.getSearchCoordinator(sId);
        NaiveSearch& naiveSearch = searchCoord.getNaiveSearch();
        priority_queue<SearchResult, vector<SearchResult>, CompareSearchResult> topK;
        vector<SearchResult> vec_result;

        	clock_gettime(CLOCK_MONOTONIC, &before);

		naiveSearch.computeTopK(query, topk_no);

        	while(naiveSearch.getTopK().size() != 0) {
                	SearchResult searchResult = naiveSearch.getTopK().top();
			bool bfind = false;
			for(int j=0;j<vec_result.size();j++) {
				SearchResult mt = vec_result[j];
               			if(mt.getId() == searchResult.getId() &&
               			   mt.getOffset() == searchResult.getOffset()) {
					bfind = true;
					break;
				}

			}
			naiveSearch.getTopK().pop();
		//cout << "**id=" << searchResult.getId() << ",offset=" << searchResult.getOffset() << ",distance=" << searchResult.getDistance() << endl;
			if(bfind) continue;
			vec_result.push_back(searchResult);
		}

	for(int i=0;i<vec_result.size();i++) {
		SearchResult searchResult = vec_result[i];
		if(topK.size() < topk_no) {
			topK.push(searchResult);
		}
		else {
			SearchResult mt = topK.top();
		 	if(mt.getDistance() > searchResult.getDistance()) {
		   		topK.pop();
			 	topK.push(searchResult);
		    	}
		}
	}
        	clock_gettime(CLOCK_MONOTONIC, &after);

	while(topK.size() > 0) {

               	SearchResult searchResult = topK.top();
               	int id = searchResult.getId();
               	int offset = searchResult.getOffset();
               	float distance = searchResult.getDistance();

		cout << "id=" << id << ",offset=" << offset << ",distance=" << distance << endl;

               	topK.pop();
	}
	timespec diff_time = diff(before, after);
	long search_time = diff_time.tv_sec*1000 + diff_time.tv_nsec/1000000;
        cout << "search_time(ms)=" << search_time << endl;
	tracker.removeSearchCoordinator(sId);
	}
}

int main(int nargs, char **args){

	if(nargs < 4) {
		printf("NaiveTest <topk_no> <datafile> <queryfile>\n");
		fflush(stdout);
	} else {
		int topk_no = atoi(args[1]);
		//cout << "topk_no=" << topk_no << endl;
		string datafile = string(args[2]);
		//cout << "datafile=" << datafile << endl;
		string queryfile = string(args[3]);
		//cout << "queryfile=" << queryfile << endl;
		testNaiveSearch(topk_no, datafile, queryfile);
	}

}


#include <armadillo>

using namespace std;

class Rows{
	public:
	vector<string> text;
	vector<float> numeric;
	vector<int> theAss;
};

class Dataframe{
public:
	vector<Rows> data;vector<bool> istext;
	vector<string> header;
	arma::mat Matrix;

	//Member functions
	bool isNumeric(string s);
	Dataframe(string file,char delimiter);
	void printDataframe(int rows);
	void printMatrix(int rows);
	void integerEncode(int column);
	vector<int> getAss();
}


#include <unistd.h>

#include "calotypes/CalibrationLog.h"
#include "calotypes/Shufflers.hpp"

#include <boost/random/random_device.hpp>
#include <boost/random/mersenne_twister.hpp>

#include <boost/algorithm/string.hpp>

using namespace calotypes;

void PrintHelp()
{
	std::cout << "shuffle_log: Shuffles a detection log randomly" << std::endl;
	std::cout << "Arguments:" << std::endl;
	std::cout << "[-i inputPath] Input detection log" << std::endl;
	std::cout << "[-o outputPath] Output detection prefix" << std::endl;
	std::cout << "[-n numShuffles (1)] Number of permutations to create" << std::endl;
}

int main ( int argc, char** argv )
{

	std::string inputPath, outputPath;
	unsigned int numCopies = 1;
	char c;

	if( argc == 1 )
	{
		PrintHelp();
		return 0;
	}

	while( (c = getopt( argc, argv, "hi:o:n:")) != -1 )
	{
		switch( c )
		{
			case 'h':
				PrintHelp();
				return 0;
			case 'i':
				inputPath.assign( optarg );
				break;
			case 'o':
				outputPath.assign( optarg );
				break;
			case 'n':
				numCopies = strtol( optarg, NULL, 10 );
				break;
			case '?':
                return -1;
            default:
				std::cerr << "Unrecognized option." << std::endl;
                return -1;
		}
	}

	CalibrationLogReader reader( inputPath );

	std::vector< std::string > splits;
	boost::split( splits, inputPath, boost::is_any_of( "." ) );
	std::string extension = splits[ splits.size()-1 ];

	std::vector<CameraTrainingData> points;
	CameraTrainingData data;
	while( reader.GetNext( data ) )
	{
		points.push_back( data );
	}

	boost::random::random_device rng;
	boost::random::mt19937 generator;
	generator.seed( rng );

	std::vector<unsigned int> inds( points.size() );
	for( unsigned int i = 0; i < numCopies; i++ )
	{
		FisherYatesShuffle( points.size(), inds, generator );

		std::stringstream ss;
		ss << outputPath << "_" << i << "." << extension;
		CalibrationLogWriter writer( ss.str() );
		for( unsigned int j = 0; j < points.size(); j++ )
		{
			writer.WriteNext( points[ inds[j] ] );
		}
	}

}


/*
  High Performance Astrophysical Reconstruction and Processing (HARP)
  (c) 2014-2015, The Regents of the University of California,
  through Lawrence Berkeley National Laboratory.  See top
  level LICENSE file for details.
*/

#include <iostream>
#include <cstdio>

extern "C" {
  #include <unistd.h>
}

#include <boost/program_options.hpp>

#include <harp.hpp>

using namespace std;
using namespace harp;

namespace popts = boost::program_options;

int main ( int argc, char *argv[] ) {

  double tstart;
  double tstop;

  cout.precision ( 10 );
  cerr.precision ( 10 );

  size_t lambda_width = 0;
  size_t lambda_overlap = 0;

  size_t spec_width = 0;
  size_t spec_overlap = 0;

  bool lambda_mask = true;

  string outroot = "harp_";

  string jsonpar = "";

  string prefix = "harp:  ";

  bool quiet = false;
  bool debug = false;

  fitsfile * fp;

  double global_start = wtime();

  // Declare options

  popts::options_description desc ( "Allowed Options" );

  desc.add_options()
  ( "help,h", "display usage information" )
  ( "quiet,q", "supress information printing" )
  ( "debug,d", "write out intermediate data products for debugging" )
  ( "out", popts::value<string>( &outroot ), "root path of output files (default = \"harp_\")" )
  ( "spec_width", popts::value < size_t > ( &spec_width ), "number of spectra to solve at once" )
  ( "spec_overlap", popts::value < size_t > ( &spec_overlap ), "minimum spectra to overlap" )
  ( "lambda_width", popts::value < size_t > ( &lambda_width ), "number of wavelength points to solve at once" )
  ( "lambda_overlap", popts::value < size_t > ( &lambda_overlap ), "minimum wavelength points to overlap" )
  ( "no_mask", "disable pixel masking in lambda overlap region" )
  ( "par", popts::value < string > ( &jsonpar ), "JSON parameter file" )
  ;

  popts::variables_map vm;

  popts::store(popts::command_line_parser( argc, argv ).options(desc).run(), vm);

  popts::notify(vm);

  if ( ( argc < 2 ) || vm.count( "help" ) || ( ! vm.count( "par" ) ) ) {
    cerr << endl;
    cerr << desc << endl;
    return 0;
  }

  if ( vm.count( "quiet" ) ) {
    quiet = true;
  }

  if ( vm.count( "debug" ) ) {
    debug = true;
  }

  if ( vm.count( "no_mask" ) ) {
    lambda_mask = false;
  }

  // Read metadata

  boost::property_tree::ptree par;

  if ( ! quiet ) {
    cout << prefix << "Loading JSON parameter file..." << endl;
  }

  boost::property_tree::json_parser::read_json ( jsonpar, par );

  // For now, we expect one psf and one image at the lop level of the
  // document.

  // Create input PSF

  if ( ! quiet ) {
    cout << prefix << "Creating PSF..." << endl;
  }

  tstart = wtime();

  boost::property_tree::ptree psf_props = par.get_child ( "psf" );

  psf_p design = load_psf ( psf_props );

  size_t psf_imgrows = design->img_rows();
  size_t psf_imgcols = design->img_cols();
  size_t psf_npix = psf_imgrows * psf_imgcols;

  size_t psf_nspec = design->n_spec();
  size_t psf_nlambda = design->n_lambda();
  size_t psf_nbins = psf_nspec * psf_nlambda;

  vector_double lambda = design->lambda();

  tstop = wtime();

  if ( ! quiet ) {
    cout << prefix << "  time = " << tstop-tstart << " seconds" << endl;
    cout << prefix << "  " << psf_nspec << " spectra each with " << psf_nlambda << " wavelength points" << endl;
    cout << prefix << "  image dimensions = " << psf_imgcols << " x " << psf_imgrows << endl;
  }

  // Read input image

  if ( ! quiet ) {
    cout << prefix << "Creating input image..." << endl;
  }

  tstart = wtime();

  boost::property_tree::ptree img_props = par.get_child ( "image" );

  image_p img = load_image ( img_props );

  size_t imgrows = img->n_rows();
  size_t imgcols = img->n_cols();
  size_t npix = imgrows * imgcols;

  tstop = wtime();

  if ( ! quiet ) {
    cout << prefix << "  time = " << tstop-tstart << " seconds" << endl;
    cout << prefix << "  dimensions = " << imgcols << " x " << imgrows << endl;
  }

  if ( ( imgrows != psf_imgrows ) || ( imgcols != psf_imgcols ) ) {
    HARP_THROW( "PSF and image have inconsistent dimensions" );
    exit(1);
  }

  if ( ! quiet ) {
    cout << prefix << "Reading input image..." << endl;
  }

  tstart = wtime();

  vector_double measured ( npix );
  measured.clear();

  vector_double invnoise ( npix );
  invnoise.clear();

  img->values ( measured );
  img->inv_variance ( invnoise );

  tstop = wtime();

  if ( ! quiet ) {
    cout << prefix << "  time = " << tstop-tstart << " seconds" << endl;
  }

  if ( debug ) {

    if ( ! quiet ) {
      cout << prefix << "(debug mode) Dumping input image to disk..." << endl;
    }

    tstart = wtime();

    string out_img_path = outroot + "debug_input_image.fits";

    image_fits::write ( out_img_path, imgrows, measured, invnoise );

    tstop = wtime();

    if ( ! quiet ) {
      cout << prefix << "  time = " << tstop-tstart << " seconds" << endl;
    }

  }

  // target list

  // eventually we will read a target list and this will be used for sky subtraction and will
  // be written to the output spectral files.  For now, we create a fake list here.

  vector < object_p > target_list ( psf_nspec );

  for ( size_t i = 0; i < psf_nspec; ++i ) {
    target_list[i].reset ( new object ( OBJECT_UNKNOWN, "" ) );
  }

  // output spectral products

  vector_double data_f ( psf_nbins );
  data_f.clear();

  vector_double data_Rf ( psf_nbins );
  data_Rf.clear();

  vector_double data_err ( psf_nbins );
  data_err.clear();

  vector_double data_truth ( psf_nbins );
  data_truth.clear();

  vector_double data_Rtruth ( psf_nbins );
  data_Rtruth.clear();

  // read truth spectra, if provided

  bool dotruth = false;

  if ( par.count ( "truth" ) > 0 ) {

    if ( ! quiet ) {
      cout << prefix << "Reading input truth spectra..." << endl;
    }

    tstart = wtime();

    boost::property_tree::ptree truth_props = par.get_child ( "truth" );

    spec_p truth_spec = load_spec ( truth_props );
    size_t truth_nspec = truth_spec->n_spec();
    size_t truth_nlambda = truth_spec->n_lambda();
    size_t truth_nbins = truth_nspec * truth_nlambda;

    if ( truth_nbins != psf_nbins ) {
      ostringstream o;
      o << "truth spectrum has " << truth_nbins << " spectral bins, but the PSF has " << psf_nbins << " bins";
      HARP_THROW( o.str().c_str() );
    }

    vector_double truth_lambda;

    truth_spec->values ( data_truth );
    truth_spec->lambda ( truth_lambda );

    for ( size_t i = 0; i < psf_nlambda; ++i ) {
      if ( fabs ( truth_lambda[i] - lambda[i] ) / lambda[i] > 1.0e-5 ) {
        ostringstream o;
        o << "wavelength point " << i << " does not match between PSF (" << lambda[i] << ") and truth spec (" << truth_lambda[i] << ")";
        HARP_THROW( o.str().c_str() );
      }
    }

    // FIXME: verify that target list matches input, after we actually read the input
    // target list.

    dotruth = true;

    tstop = wtime();

    if ( ! quiet ) {
      cout << prefix << "  time = " << tstop-tstart << " seconds" << endl;
    }

  }

  // Divide spectral data into overlapping regions

  if ( spec_width < 1 ) {
    // do all spectra
    spec_width = psf_nspec;
  }

  if ( lambda_width < 1 ) {
    lambda_width = psf_nlambda;
  }

  spec_slice_p slice ( new spec_slice ( 1, 0, 0, psf_nspec, psf_nlambda, spec_width, lambda_width, spec_overlap, lambda_overlap ) );

  vector < spec_slice_region > regions = slice->regions ( 0 );

  if ( ! quiet ) {
    cout << prefix << "Extracting " << regions.size() << " spectral chunks, each with " << spec_width << " spectra ( overlap = " << spec_overlap << " ) and " << lambda_width << " lambda points ( overlap = " << lambda_overlap << " )" << endl;
  }

  if ( debug && ( ! quiet ) ) {

    for ( vector < spec_slice_region > :: const_iterator gsit = regions.begin(); gsit != regions.end(); ++gsit ) {
      cout << prefix << "    spec (" << gsit->first_good_spec << " - " << (gsit->first_good_spec + gsit->n_good_spec - 1) << ") wavelength (" << gsit->first_good_lambda << " - " << (gsit->first_good_lambda + gsit->n_good_lambda - 1) << ")" << endl;
    }

    cout.flush();

  }

  // timing results

  map < string, double > timing;

  // Process all spectral slices

  string extract_prefix = "";
  if ( ! quiet ) {
    extract_prefix = prefix;
  }

  extract_slices ( slice, design, measured, invnoise, data_truth, data_Rf, data_f, data_err, data_Rtruth, timing, false, lambda_mask, prefix );

  // subtract sky if needed

  // Total timings

  if ( ! quiet ) {
    cout << prefix << "Aggregate Timings:" << endl;
    cout << prefix << "  Build design matrix = " << timing["design"] << " seconds" << endl;
    cout << prefix << "  Build inverse covariance = " << timing["inverse"] << " seconds" << endl;
    cout << prefix << "  Eigendecompose inverse = " << timing["eigen"] << " seconds" << endl;
    cout << prefix << "  Compute column norm = " << timing["norm"] << " seconds" << endl;
    cout << prefix << "  Compute noise weighted spec = " << timing["nsespec"] << " seconds" << endl;
    cout << prefix << "  Extract spectra = " << timing["extract"] << " seconds" << endl;
  }

  // Write outputs and compute chi-square if input truth is given

  if ( ! quiet ) {
    cout << prefix << "Writing solution and error..." << endl;
  }

  tstart = wtime();

  string outfile = outroot + "spec_Rf.fits";
  spec_fits::write ( outfile, data_Rf, data_err, lambda );

  vector_double fake_err ( psf_nbins );
  fake_err.clear();

  outfile = outroot + "spec_f.fits";
  spec_fits::write ( outfile, data_f, fake_err, lambda );

  if ( dotruth ) {

    vector_double data_chisq ( psf_nbins );

    double chisq_reduced = 0.0;

    for ( size_t i = 0; i < psf_nbins; ++i ) {
      if ( data_err[i] > std::numeric_limits < double > :: epsilon() ) {
        data_chisq[i] = ( data_Rf[i] - data_Rtruth[i] ) / data_err[i];
        data_chisq[i] *= data_chisq[i];
      } else {
        data_chisq[i] = 0.0;
      }
      chisq_reduced += data_chisq[i];
    }

    chisq_reduced /= (double)( psf_nbins );

    if ( ! quiet ) {
      cout << prefix << "  Reduced Chi square = " << chisq_reduced << endl;
    }

    outfile = outroot + "spec_Rtruth.fits";
    spec_fits::write ( outfile, data_Rtruth, data_chisq, lambda );

  }

  tstop = wtime();

  if ( ! quiet ) {
    cout << prefix << "  time = " << tstop-tstart << " seconds" << endl;
  }

  // this next bit of code might (if debug == dotruth == true) generate 3 new images and the full PSF for
  // debugging purposes.  If we are tight on RAM, this could push us over the edge.  Explicitly deallocate
  // un-needed data objects at this point.

  measured.resize(0);
  data_Rf.resize(0);
  data_Rtruth.resize(0);
  data_err.resize(0);

  if ( debug ) {

    if ( ! quiet ) {
      cout << prefix << "Writing projected, deconvolved spectra..." << endl;
    }

    tstart = wtime();

    matrix_double_sparse AT;
    design->project_transpose ( AT );

    vector_double f_projected;
    sparse_mv_trans ( AT, data_f, f_projected );

    data_f.resize(0);

    outfile = outroot + "image_f-project.fits";
    image_fits::write ( outfile, imgrows, f_projected, invnoise );

    tstop = wtime();

    if ( ! quiet ) {
      cout << prefix << "  time = " << tstop-tstart << " seconds" << endl;
    }

    if ( dotruth ) {

      if ( ! quiet ) {
        cout << prefix << "Writing projected truth spectra..." << endl;
      }

      tstart = wtime();

      vector_double truth_projected;
      sparse_mv_trans ( AT, data_truth, truth_projected );

      // save more memory before allocating chisquare image

      AT.resize ( 0, 0, false );
      data_truth.resize(0);

      // pixel space chi-square

      vector_double data_chisq ( npix );

      double chisq_reduced = 0.0;

      for ( size_t i = 0; i < npix; ++i ) {
        if ( invnoise[i] > std::numeric_limits < double > :: epsilon() ) {
          data_chisq[i] = ( f_projected[i] - truth_projected[i] ) * sqrt( invnoise[i] );
          data_chisq[i] *= data_chisq[i];
        } else {
          data_chisq[i] = 0.0;
        }
        chisq_reduced += data_chisq[i];
      }

      chisq_reduced /= (double)( npix );

      if ( ! quiet ) {
        cout << prefix << "  Pixel space reduced Chi square = " << chisq_reduced << endl;
      }

      outfile = outroot + "image_truth-project.fits";
      image_fits::write ( outfile, imgrows, truth_projected, data_chisq );

      tstop = wtime();

      if ( ! quiet ) {
        cout << prefix << "  time = " << tstop-tstart << " seconds" << endl;
      }

    }

  }

  // final timing

  double global_stop = wtime();

  if ( ! quiet ) {
    cout << prefix << "Total run time = " << global_stop-global_start << " seconds" << endl;
  }

  return 0;
}


/*
@copyright Louis Dionne 2014
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#include <boost/hana/comparable/equal_mcd.hpp>
#include <boost/hana/comparable/laws.hpp>
#include <boost/hana/comparable/not_equal_mcd.hpp>

#include <boost/hana/detail/assert.hpp>
#include <boost/hana/detail/constexpr.hpp>
#include <boost/hana/detail/minimal/comparable.hpp>
#include <boost/hana/list/instance.hpp>
using namespace boost::hana;

template <typename mcd>
void test() {
    constexpr auto comparable = detail::minimal::comparable<mcd>;
    BOOST_HANA_CONSTEXPR_ASSERT(
        Comparable::laws::check(
            list(comparable(0), comparable(1), comparable(2))
        )
    );
}

int main() {
    test<Comparable::equal_mcd>();
    test<Comparable::not_equal_mcd>();
}


/*
 *
 * declare function se:qr() as xs:string external;
 * se:qr()
 *
 * <result>base64 qr code</result>
 */

#include <boost/lexical_cast.hpp>

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>

#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include "QrCode.hpp"
#include "sedna_ef.h"

#include "base64.h"

using std::uint8_t;
using qrcodegen::QrCode;
using qrcodegen::QrSegment;
using namespace cv;

static std::string printQr(const QrCode &qr);

#define DLLEXPORT

static std::string printQr(const QrCode &qr) {
	const int width = 5;
	int w = qr.getSize() * width;
	std::string out;
	Mat img(w,w,CV_8UC1);

	for (int y = 0; y < qr.getSize(); y++) {
		for (int x = 0; x < qr.getSize() ; x++) {
			//out += (qr.getModule(x, y) ? "##" : "  ");
			if(qr.getModule(x,y)){
				cv::rectangle(img, cv::Rect(x*width,y*width,width,width),Scalar(0,0,0),cv::FILLED);
			}else{
				cv::rectangle(img, cv::Rect(x*width,y*width,width,width),Scalar(255,255,255),cv::FILLED);
			}
		}
	}
	std::vector<BYTE> buff;
	char tmp[1024];
	try{
		cv::imencode(".jpg", img, buff);
		out = base64_encode(buff.data(),buff.size());
	}catch(const cv::Exception &ex){
		out = "false " + std::string(ex.what());
	}

	return out;
}

extern "C" {
	SEDNA_SEQUENCE_ITEM DLLEXPORT *qr(SEDNA_EF_INIT *init, SEDNA_EF_ARGS *args, char * error_msg_buf);
	void DLLEXPORT qr_init(SEDNA_EF_INIT *init, char * error_msg_buf);
	void DLLEXPORT qr_deinit(SEDNA_EF_INIT *init, char * error_msg_buf);
}

char const *ef_names[] = { "qr", NULL};
SEDNA_SEQUENCE_ITEM *item = NULL;

void qr_init(SEDNA_EF_INIT *init, char * error_msg_buf)
{
	item = (SEDNA_SEQUENCE_ITEM*)init->sedna_malloc(sizeof(SEDNA_SEQUENCE_ITEM));
	item->next = NULL;
}

void qr_deinit(SEDNA_EF_INIT *init, char * error_msg_buf)
{
	free(item->data.val_string);
}

SEDNA_SEQUENCE_ITEM *qr(SEDNA_EF_INIT *init, SEDNA_EF_ARGS *args, char * error_msg_buf)
{
	if (args->length != 1)
	{
		sprintf(error_msg_buf, "bad number of arguments!");
		return NULL;
	}

	SEDNA_SEQUENCE_ITEM *it = args->args[0];
	it->data.type = SEDNATYPE_string;
	std::string strInput = boost::lexical_cast<std::string>(it->data.val_string);

	if(item == NULL){
		sprintf(error_msg_buf, "item is null");
		return NULL;
	}

	// // Make and print the QR Code symbol
	const QrCode qr = QrCode::encodeText(strInput.c_str(), QrCode::Ecc::LOW);
	std::string out;

	// create QR Image as JPG and return image as Base64
	out = printQr(qr);

	// set return string to Sedna item
	item->data.type = SEDNATYPE_string;
	item->data.val_string = (SEDNA_string)malloc(out.length() + 1);
	strcpy(item->data.val_string, out.c_str());

	return item;
}


// 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, www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also tools/license/license.mtl.txt in the distribution.

#ifndef MTL_MATRIX_SPARSE_BANDED_INCLUDE
#define MTL_MATRIX_SPARSE_BANDED_INCLUDE

#include <algorithm>
#include <cassert>
#include <ostream>
#include <vector>
#include <boost/type_traits/make_signed.hpp>

#include <boost/numeric/mtl/matrix/dimension.hpp>
#include <boost/numeric/mtl/matrix/parameter.hpp>
#include <boost/numeric/mtl/matrix/base_matrix.hpp>
#include <boost/numeric/mtl/matrix/crtp_base_matrix.hpp>
#include <boost/numeric/mtl/matrix/mat_expr.hpp>
#include <boost/numeric/mtl/operation/is_negative.hpp>
#include <boost/numeric/mtl/operation/update.hpp>
#include <boost/numeric/mtl/utility/exception.hpp>
#include <boost/numeric/mtl/utility/is_row_major.hpp>
#include <boost/numeric/mtl/utility/maybe.hpp>
#include <boost/numeric/mtl/utility/static_assert.hpp>

namespace mtl { namespace matrix {

/// Sparse banded matrix class
template <typename Value, typename Parameters = matrix::parameters<> >
class sparse_banded
  : public base_matrix<Value, Parameters>,
    public const_crtp_base_matrix< sparse_banded<Value, Parameters>, Value, typename Parameters::size_type >,
    public crtp_matrix_assign< sparse_banded<Value, Parameters>, Value, typename Parameters::size_type >,
    public mat_expr< sparse_banded<Value, Parameters> >
{
    MTL_STATIC_ASSERT((mtl::traits::is_row_major<Parameters>::value), "Only row-major sparse banded matrices supported so far.");

    typedef std::size_t                                size_t;
    typedef base_matrix<Value, Parameters>             super;
    typedef sparse_banded<Value, Parameters>           self;

  public:
    typedef Value                                      value_type;
    typedef typename Parameters::size_type             size_type;
    typedef typename boost::make_signed<size_type>::type  band_size_type;
    typedef crtp_matrix_assign<self, Value, size_type> assign_base;

    /// Construct matrix of dimension \p nr by \p nc
    explicit sparse_banded(size_type nr= 0, size_type nc= 0)
      : super(non_fixed::dimensions(nr, nc)), data(0), inserting(false)
    {}

    /// Copy from other types
    template <typename MatrixSrc>
    explicit sparse_banded(const MatrixSrc& src) : data(0), inserting(false)
    {	*this= src;    }

    ~sparse_banded() { delete[] data; }
    void check() const { MTL_DEBUG_THROW_IF(inserting, access_during_insertion()); }
    void check(size_type MTL_DEBUG_ARG(r), size_type MTL_DEBUG_ARG(c)) const
    {
	check();
	MTL_DEBUG_THROW_IF(is_negative(r) || r >= this->num_rows()
			   || is_negative(c) || c >= this->num_cols(), index_out_of_range());
    }

    using assign_base::operator=;

    void make_empty() ///< Delete all entries
    {
	delete[] data; data= 0;
	bands.resize(0);
    }

    /// Change dimension to \p r by \p c; deletes all entries
    void change_dim(size_type r, size_type c)
    {
	make_empty();
	super::change_dim(r, c);
    }

    /// Offset of entry r,c if in matrix (otherwise offset of next entry)
    utilities::maybe<size_type> offset(size_type r, size_type c) const
    {
	check(r, c);
	band_size_type dia= band_size_type(c) - band_size_type(r);
	size_type      b= find_dia(dia);
	return utilities::maybe<size_type>(r * bands.size() + b, size_t(b) < bands.size() && bands[b] == dia);
    }

    /// Value of matrix entry
    value_type operator()(size_type r, size_type c) const
    {
	using math::zero;
	utilities::maybe<size_type> o= offset(r, c);
	return o ? data[o.value()] : zero(value_type());
    }

    /// L-value reference of stored matrix entry
    /** To be used with care; in debug mode, exception is thrown if entry is not found **/
    value_type& lvalue(size_type r, size_type c)
    {
	utilities::maybe<size_type> o= offset(r, c);
	MTL_DEBUG_THROW_IF(!o, runtime_error("Entry doesn't exist."));
	return data[o.value()];
    }

    /// Print matrix on \p os
    friend std::ostream& operator<<(std::ostream& os, const self& A)
    {
	const size_type bs= A.bands.size(), nc= num_cols(A), s= bs * num_rows(A);
	print_size_max p;
	for (size_type i= 0; i < s; i++)
	    p(A.data[i]);

	for (size_type r= 0; r < num_rows(A); r++) {
	    os << '[';
	    size_type b= 0;
	    while (b < bs && -A.bands[b] > long(r)) b++;
	    for (size_type c= 0; c < nc; c++) {
		os.width(p.max);
		if (b == bs || long(c) - long(r) < A.bands[b])
		    os << Value(0);
		else
		    os << A.data[r * bs + b++];
		os << (c + 1 == nc ? "]\n" : " ");
	    }
	}
	return os;
    }

    /// Number of structural non-zeros (i.e. stored entries) which is the the number of bands times rows
    size_type nnz() const { return bands.size() * this->num_rows(); }
    size_type find_major(size_type offset) const { return offset % bands.size(); }

    friend size_t num_rows(const self& A) { return A.num_rows(); }
    friend size_t num_cols(const self& A) { return A.num_cols(); }

    std::vector<band_size_type> const& ref_bands() const { return bands; } ///< Refer bands vector [advanced]
    const value_type*                  ref_data() const { return data; } ///< Refer data pointer [advanced]

  private:
    size_type find_dia(band_size_type dia) const
    {
	size_type i= 0;
	while (i < size_type(bands.size()) && dia > bands[i]) i++;
	return i;
    }

    template <typename, typename, typename> friend struct sparse_banded_inserter;

    std::vector<band_size_type>    bands;
    value_type*                    data;
    bool                           inserting;
};

/// Inserter for sparse banded matrix
template <typename Value, typename Parameters, typename Updater = mtl::operations::update_store<Value> >
struct sparse_banded_inserter
{
    typedef Value                                                            value_type;
    typedef typename Parameters::size_type                                   size_type;
    typedef sparse_banded<Value, Parameters>                                 matrix_type;
    typedef sparse_banded_inserter                                           self;
    typedef typename matrix_type::band_size_type                             band_size_type;
    typedef operations::update_proxy<self, size_type>                        proxy_type;

  private:
    struct bracket_proxy
    {
	bracket_proxy(self& ref, size_type row) : ref(ref), row(row) {}

	proxy_type operator[](size_type col) { return proxy_type(ref, row, col); }

	self&      ref;
	size_type  row;
    };

    void insert_dia(size_type dia_band, band_size_type dia)
    {
	using std::swap;
	// empty vector and entry at the end
	diagonals.push_back(std::vector<Value>(num_rows(A), Value(0)));
	A.bands.push_back(dia);

	for (size_type i= diagonals.size() - 1; i > dia_band; i--) {
	    swap(diagonals[i-1], diagonals[i]);
	    swap(A.bands[i-1], A.bands[i]);
	}
    }

  public:
    /// Construct inserter for matrix \p A; second argument for slot_size ignored
    sparse_banded_inserter(matrix_type& A, size_type) : A(A)
    {
	MTL_THROW_IF(A.inserting, runtime_error("Two inserters on same matrix"));
	MTL_THROW_IF(A.data, runtime_error("Inserter does not support modifications yet."));
	A.inserting= true;
    }

    ~sparse_banded_inserter()
    {
	const size_type bs= A.bands.size();
	Value* p= A.data= new Value[A.bands.size() * num_rows(A)];
	for (size_type r= 0; r < num_rows(A); r++)
	    for (size_type b= 0; b < bs; b++)
		*p++= diagonals[b][r];
	assert(p - A.data == long(A.bands.size() * num_rows(A)));
	A.inserting= false;
    }

    /// Proxy to insert into A[row][col]
    bracket_proxy operator[] (size_type row)
    {
	return bracket_proxy(*this, row);
    }

    /// Proxy to insert into A[row][col]
    proxy_type operator() (size_type row, size_type col)
    {
	return proxy_type(*this, row, col);
    }

    /// Modify A[row][col] with \p val using \p Modifier
    template <typename Modifier>
    void modify(size_type row, size_type col, Value val)
    {
	MTL_DEBUG_THROW_IF(is_negative(row) || row >= num_rows(A) || is_negative(col) || col >= num_cols(A),
			   index_out_of_range());
	const band_size_type dia= col - row;
	const size_type      dia_band= A.find_dia(dia);

	if (dia_band == size_type(A.bands.size()) || dia != A.bands[dia_band])
	    insert_dia(dia_band, dia);

	Modifier()(diagonals[dia_band][row], val);
    }

    /// Modify A[row][col] with \p val using the class' updater
    void update(size_type row, size_type col, Value val)
    {
	using math::zero;
	modify<Updater>(row, col, val);
    }

  private:
    matrix_type&                      A;
    std::vector<std::vector<Value> >  diagonals;
};

template <typename Value, typename Parameters>
struct sparse_banded_key
{
    typedef std::size_t                                   size_t;
    typedef sparse_banded_key                             self;
    typedef mtl::matrix::sparse_banded<Value, Parameters> matrix_type;

    explicit sparse_banded_key(const matrix_type& A, size_t offset)
      : A(A), offset(offset) {}

    bool operator== (sparse_banded_key const& other) const { return offset == other.offset; }
    bool operator!= (sparse_banded_key const& other) const { return !(*this == other); }

    const matrix_type&   A;
    size_t               offset;
};

// Cursor over every element
template <typename Value, typename Parameters>
struct sparse_banded_cursor
  : public sparse_banded_key<Value, Parameters>
{
    typedef sparse_banded_key<Value, Parameters>          base;
    typedef mtl::matrix::sparse_banded<Value, Parameters> matrix_type;
    typedef sparse_banded_cursor                          self;

    explicit sparse_banded_cursor(const matrix_type& A, size_t offset)
      : base(A, offset)    {}

    self& operator++() { ++this->offset; return *this;  }
    const base& operator* () const { return *this; }
};

// ================
// Free functions
// ================

template <typename Value, typename Parameters>
inline std::size_t size(const sparse_banded<Value, Parameters>& matrix)
{
    return std::size_t(matrix.num_cols()) * std::size_t(matrix.num_rows());
}

}} // namespace mtl::matrix

namespace mtl {
    using matrix::sparse_banded;
}

// ================
// Range generators
// ================

namespace mtl { namespace traits {

    // Cursor over all rows
    // Supported if row major matrix
    template <typename Value, typename Parameters>
    struct range_generator<glas::tag::row, sparse_banded<Value, Parameters> >
      : detail::all_rows_range_generator<sparse_banded<Value, Parameters>, complexity_classes::linear_cached>
    {};

    template <class Value, class Parameters>
    struct range_generator<glas::tag::nz,
			   detail::sub_matrix_cursor<sparse_banded<Value, Parameters>, glas::tag::row, 2> >
    {
	typedef typename Parameters::size_type                                                 size_type;
	typedef detail::sub_matrix_cursor<sparse_banded<Value, Parameters>, glas::tag::row, 2> cursor_type;
	typedef complexity_classes::linear_cached                                              complexity;
	typedef mtl::matrix::sparse_banded_cursor<Value, Parameters>                           type;
	static int const                                                                       level = 1;
	MTL_STATIC_ASSERT((is_row_major<Parameters>::value), "Only row-major sparse banded matrices supported so far.");

	type begin(cursor_type const& cursor) const
	{
	    size_type r= cursor.key, b= 0, bs= cursor.ref.ref_bands().size();
	    while (b < bs && -cursor.ref.ref_bands()[b] > long(r)) b++;
	    return type(cursor.ref, r * bs + b);
	}
	type end(cursor_type const& cursor) const
	{
	    size_type r= cursor.key, bs= cursor.ref.ref_bands().size(), b= bs, nc= cursor.ref.num_cols();
	    while (b > 0 && cursor.ref.ref_bands()[b-1] + long(r) >= long(nc)) b--;
	    return type(cursor.ref, r * bs + b);
	}

	// type lower_bound(cursor_type const& cursor, size_type position) const {}
    };

    // Cursor over all rows or columns, depending which one is major
    template <typename Value, typename Parameters>
    struct range_generator<glas::tag::major, sparse_banded<Value, Parameters> >
      : range_generator<glas::tag::row, sparse_banded<Value, Parameters> >
    {};

}} // namespace mtl::traits

#endif // MTL_MATRIX_SPARSE_BANDED_INCLUDE


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

/*
 Copyright (C) 2014 Jose Aparicio
 Copyright (C) 2014 Peter Caspers

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

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

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

/*! \file makecds.hpp
    \brief Helper class to instantiate standard market cds.
*/

#ifndef quantlib_makecds_hpp
#define quantlib_makecds_hpp

#include <ql/instruments/creditdefaultswap.hpp>
#include <boost/optional.hpp>

namespace QuantLib {

    //! helper class
    /*! This class provides a more comfortable way
        to instantiate standard cds.
    */
    class MakeCreditDefaultSwap {
      public:
        MakeCreditDefaultSwap(const Period &tenor, const Real couponRate);
        MakeCreditDefaultSwap(const Date &termDate, const Real couponRate);

        operator CreditDefaultSwap() const;
        operator boost::shared_ptr<CreditDefaultSwap>() const;

        MakeCreditDefaultSwap &withUpfrontRate(Real);
        MakeCreditDefaultSwap &withSide(Protection::Side);
        MakeCreditDefaultSwap &withNominal(Real);
        MakeCreditDefaultSwap &withCouponTenor(Period);
        MakeCreditDefaultSwap &withDayCounter(DayCounter &);
        MakeCreditDefaultSwap &withLastPeriodDayCounter(DayCounter &);

        MakeCreditDefaultSwap &
        withPricingEngine(const boost::shared_ptr<PricingEngine> &);

      private:
        Protection::Side side_;
        Real nominal_;
        boost::optional<Period> tenor_;
        boost::optional<Date> termDate_;
        Period couponTenor_;
        Real couponRate_;
        Real upfrontRate_;
        DayCounter dayCounter_;
        DayCounter lastPeriodDayCounter_;

        boost::shared_ptr<PricingEngine> engine_;
    };
}

#endif