/*------------------------------------------------------------------------------
Copyright © 2015 by Nicola Bombieri

H-BF is provided under the terms of The MIT License (MIT):

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------*/
/**
 * @author Federico Busato
 * Univerity of Verona, Dept. of Computer Science
 * federico.busato@univr.it
 */
#include <boost/config.hpp>
#include <vector>
#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/bellman_ford_shortest_paths.hpp>
#include "Host/GraphSSSP.hpp"
#include "XLib.hpp"
using namespace timer;

struct EdgeProperties {
	int weight;
};

void GraphSSSP::BoostBellmanFord(const node_t source) {
	using namespace boost;

	typedef std::pair < int, int > Edge;
	typedef adjacency_list < vecS, vecS, directedS, no_property, EdgeProperties> Graph;

	Edge* edgesB = new Edge[E];
	int k = 0;
	for (int i = 0; i < V; i++) {
		for (int j = OutNodes[i]; j < OutNodes[i+1]; j++)
			edgesB[k++] = Edge(i, OutEdges[j]);
	}
	Graph g(edgesB, edgesB + E, V);

	graph_traits < Graph >::edge_iterator ei, ei_end;
	property_map<Graph, int EdgeProperties::*>::type weight_pmap = get(&EdgeProperties::weight, g);
	int i = 0;
	for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei, ++i)
		weight_pmap[*ei] = Weights[i];

	std::vector<int> distance(V, (std::numeric_limits < int >::max)());
	std::vector<std::size_t> parent(V);
	for (i = 0; i < V; ++i)
		parent[i] = i;
	distance[source] = 0;

	Timer<HOST> TM;
    TM.start();

	bellman_ford_shortest_paths(g, int (V), weight_map(weight_pmap).distance_map(&distance[0]).predecessor_map(&parent[0]));

	TM.getTime("Boost BellmanFord");

    BellmanFord_Queue_init();
    BellmanFord_Queue(source);

	graph_traits < Graph >::vertex_iterator vi, vend;
	i = 0;
	for (boost::tie(vi, vend) = vertices(g); vi != vend; ++vi) {
		if (distance[*vi] != Distance[i++])
			__ERROR("distance error");
	}
    BellmanFord_Queue_end();
}


// Author(s): Jeroen van der Wulp, Wieger Wesselink
// Copyright: see the accompanying file COPYING or copy at
// https://github.com/mCRL2org/mCRL2/blob/master/COPYING
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#define BOOST_TEST_MODULE enumerator_test
#include <boost/test/included/unit_test.hpp>

#include "mcrl2/data/consistency.h"
#include "mcrl2/data/detail/concepts.h"
#include "mcrl2/data/enumerator_with_iterator.h"
#include "mcrl2/data/optimized_boolean_operators.h"
#include "mcrl2/data/parse.h"
#include "mcrl2/data/print.h"
#include "mcrl2/data/replace.h"
#include "mcrl2/data/substitutions/mutable_map_substitution.h"

using namespace mcrl2;
using namespace mcrl2::data;

// Example: parse_variable_list("x:Nat; y:Pos");
variable_list parse_variable_list(const std::string& text, const data_specification& dataspec = detail::default_specification())
{
  variable_vector result;
  parse_variables(text, std::back_inserter(result), dataspec);
  return variable_list(result.begin(), result.end());
}

void enumerate(const data_specification& dataspec,
               const variable_list& variables,
               const data_expression& expression,
               std::size_t expected_no_of_solutions,
               bool more_solutions_possible)
{
  typedef enumerator_list_element_with_substitution<> enumerator_element;
  typedef enumerator_algorithm_with_iterator<> enumerator_type;

  data::enumerator_identifier_generator id_generator;
  rewriter rewr(dataspec);
  enumerator_type enumerator(rewr, dataspec, rewr, id_generator);
  std::size_t number_of_solutions = 0;
  mutable_indexed_substitution<> sigma;
  data::enumerator_queue<enumerator_element> enumerator_deque(enumerator_element(variables, expression));
  auto i = enumerator.begin(sigma, enumerator_deque);
  for ( ; number_of_solutions < expected_no_of_solutions && i != enumerator.end(); ++i)
  {
    mutable_map_substitution<> rho;
    i->add_assignments(variables, rho, rewr);
    std::cout << "solution = " << rho << std::endl;
    number_of_solutions++;
  }
  BOOST_CHECK(number_of_solutions == expected_no_of_solutions && (more_solutions_possible || i == enumerator.end()));
}

void enumerate(const std::string& dataspec_text,
               const std::string& variable_text,
               const std::string& expression_text,
               const std::string& free_variable_text,
               std::size_t number_of_solutions,
               bool more_solutions_possible)
{
  data_specification dataspec = parse_data_specification(dataspec_text);
  variable_list variables = parse_variable_list(variable_text, dataspec);
  variable_list free_variables = parse_variable_list(free_variable_text, dataspec);
  data_expression expression = parse_data_expression(expression_text, free_variables, dataspec);
  enumerate(dataspec, variables, expression, number_of_solutions, more_solutions_possible);
}

BOOST_AUTO_TEST_CASE(empty_test)
{
  typedef enumerator_list_element_with_substitution<> enumerator_element;
  typedef enumerator_algorithm_with_iterator<> enumerator_type;

  // test manual construction of rewr with rewriter
  data_specification dataspec;
  rewriter rewr(dataspec);
  variable_list variables;

  std::size_t count = 0;
  data::enumerator_identifier_generator id_generator;

  // explicit with condition rewr and condition
  enumerator_type enumerator(rewr, dataspec, rewr, id_generator);

  mutable_indexed_substitution<> sigma;
  data::enumerator_queue<enumerator_element> enumerator_deque(enumerator_element(variables, sort_bool::true_()));
  for (auto i = enumerator.begin(sigma, enumerator_deque); i != enumerator.end(); ++i)
  {
    count++;
  }
  BOOST_CHECK(count == 1);

  // explicit with condition but without condition rewr
  enumerator_deque.clear();
  enumerator_deque.push_back(enumerator_element(variables, sort_bool::true_()));
  for (auto i = enumerator.begin(sigma, enumerator_deque); i != enumerator.end(); ++i)
  {
    count++;
  }
  BOOST_CHECK(count == 2);

  variables = parse_variable_list("y: Nat;");
  enumerator_deque.clear();
  enumerator_deque.push_back(enumerator_element(variables, sort_bool::false_()));
  for (auto i = enumerator.begin(sigma, enumerator_deque); i != enumerator.end(); ++i)
  {
    BOOST_CHECK(false);
  }
}

BOOST_AUTO_TEST_CASE(list_test)
{
  std::string dataspec_text =
    "sort list_of_booleans;                                    \n"
    "cons empty : list_of_booleans;                            \n"
    "     lcons : Bool # list_of_booleans -> list_of_booleans; \n"
    "map  size   : list_of_booleans -> Nat;                    \n"
    "var  l : list_of_booleans;                                \n"
    "     b : Bool;                                            \n"
    "eqn  size(empty) = 0;                                     \n"
    "     size(lcons(b,l)) = 1 + size(l);                      \n"
    ;
  std::string variable_text = "x: list_of_booleans; y: Nat;";
  std::string expression_text = "y == size(x) && 0 < y && y < 2";
  std::string free_variable_text = "x : list_of_booleans; y : Nat;";
  std::size_t number_of_solutions = 0;
  bool more_solutions_possible = true; // Changed!
  enumerate(dataspec_text, variable_text, expression_text, free_variable_text, number_of_solutions, more_solutions_possible);
}

BOOST_AUTO_TEST_CASE(structured_sort_test)
{
  std::string dataspec_text =
    "sort D = struct d1(E) | d2(E); \n"
    "     E = struct e1 | e2;       \n"
    ;
  std::string variable_text = "d: D;";
  std::string expression_text = "forall d: D. d == e";
  std::string free_variable_text = "e: D;";
  std::size_t number_of_solutions = 4;
  bool more_solutions_possible = false;
  enumerate(dataspec_text, variable_text, expression_text, free_variable_text, number_of_solutions, more_solutions_possible);
}

BOOST_AUTO_TEST_CASE(equality_substitution_test)
{
  std::string dataspec_text = "sort L = Nat;";
  std::string variable_text = "";
  std::string expression_text = "x == 17 && x != 17";
  std::string free_variable_text = "x : Pos;";
  std::size_t number_of_solutions = 0;
  bool more_solutions_possible = true; // Changed!

  std::clog << "Test1 equality\n";
  enumerate(dataspec_text, variable_text, expression_text, free_variable_text, number_of_solutions, more_solutions_possible);

  std::clog << "Test2 equality\n";
  expression_text = "x == 17 && x == x";
  number_of_solutions = 1;
  more_solutions_possible = false;
  enumerate(dataspec_text, variable_text, expression_text, free_variable_text, number_of_solutions, more_solutions_possible);

  std::clog << "Test3 equality\n";
  expression_text = "x == 17 && 2*x == 34";
  enumerate(dataspec_text, variable_text, expression_text, free_variable_text, number_of_solutions, more_solutions_possible);

  std::clog << "Test4 equality\n";
  variable_text = "";
  number_of_solutions = 1;
  enumerate(dataspec_text, variable_text, expression_text, free_variable_text, number_of_solutions, more_solutions_possible);

  std::clog << "Test4 equality: return two non exact solutions\n";
  variable_text = "b: Bool;";
  number_of_solutions = 2;
  enumerate(dataspec_text, variable_text, expression_text, free_variable_text, number_of_solutions, more_solutions_possible);
}

BOOST_AUTO_TEST_CASE(test_ticket_1486)
{
  std::string dataspec_text = "sort L = Int;";
  std::string variable_text = "y : Nat;";
  std::string expression_text = "y + 20 == 25";
  std::string free_variable_text = "y : Nat;";
  std::size_t number_of_solutions = 1;
  bool more_solutions_possible = false;
  enumerate(dataspec_text, variable_text, expression_text, free_variable_text, number_of_solutions, more_solutions_possible);
}

BOOST_AUTO_TEST_CASE(tree_test)
{
  std::string dataspec_text =
    "sort tree_with_booleans;                                                   \n"
    "cons leaf : Bool -> tree_with_booleans;                                    \n"
    "cons node : tree_with_booleans # tree_with_booleans -> tree_with_booleans; \n"
    ;
  std::string variable_text = "x : tree_with_booleans;";
  std::string expression_text = "true";
  std::string free_variable_text = "";
  std::size_t number_of_solutions = 32;
  bool more_solutions_possible = true;
  enumerate(dataspec_text, variable_text, expression_text, free_variable_text, number_of_solutions, more_solutions_possible);
}

BOOST_AUTO_TEST_CASE(mutually_recursive_test)
{
  std::string dataspec_text =
    "sort this;                                              \n"
    "     that;                                              \n"
    "cons a : this;                                          \n"
    "     A : that -> this;                                  \n"
    "     b : that;                                          \n"
    "     B : this -> that;                                  \n"
    "map  maximum_a : Nat # this -> Bool;                    \n"
    "     maximum_a : Nat # that -> Bool;                    \n"
    "     maximum_b : Nat # this -> Bool;                    \n"
    "     maximum_b : Nat # that -> Bool;                    \n"
    "var  x : Nat;                                           \n"
    "     ax : this;                                         \n"
    "     bx : that;                                         \n"
    "eqn  maximum_a(x,A(bx)) = maximum_a(Int2Nat(x - 1),bx); \n"
    "     maximum_a(0,a) = true;                             \n"
    "     maximum_b(x,B(ax)) = maximum_b(Int2Nat(x - 1),ax); \n"
    "     maximum_b(0,b) = true;                             \n"
    ;

  std::string variable_text = "x : this;";
  std::string expression_text = "true";
  std::string free_variable_text = "";
  std::size_t number_of_solutions = 32;
  bool more_solutions_possible = true;
  enumerate(dataspec_text, variable_text, expression_text, free_variable_text, number_of_solutions, more_solutions_possible);

  variable_text = "x : that;";
  enumerate(dataspec_text, variable_text, expression_text, free_variable_text, number_of_solutions, more_solutions_possible);
}

BOOST_AUTO_TEST_CASE(set_test)
{
  std::string dataspec_text = "sort Dummy;\n";
  std::string variable_text = "f: Set(Bool);";
  std::string expression_text = "true in f";
  std::string free_variable_text = "f: Set(Bool);";
  std::size_t number_of_solutions = 4;
  bool more_solutions_possible = false;
  enumerate(dataspec_text, variable_text, expression_text, free_variable_text, number_of_solutions, more_solutions_possible);
}

inline
data_expression_vector generate_values(const data_specification& dataspec, const sort_expression& s, std::size_t max_size = 1000)
{
  typedef enumerator_list_element_with_substitution<> enumerator_element;
  typedef enumerator_algorithm_with_iterator<> enumerator_type;

  std::size_t max_internal_variables = 10000;
  data_expression_vector result;
  data::enumerator_identifier_generator id_generator;

  rewriter rewr(dataspec);
  enumerator_type enumerator(rewr, dataspec, rewr, id_generator, max_internal_variables);
  variable v("x", s);
  variable_list variables;
  variables.push_front(v);
  mutable_indexed_substitution<> sigma;
  data::enumerator_queue<enumerator_element> enumerator_deque(enumerator_element(variables, sort_bool::true_()));
  for (auto i = enumerator.begin(sigma, enumerator_deque); i != enumerator.end() ; ++i)
  {
    i->add_assignments(variables, sigma, rewr);
    result.push_back(sigma(variables.front()));
    if (result.size() >= max_size)
    {
      break;
    }
  }
  return result;
}

bool no_duplicates(const data_expression_vector& v)
{
  std::set<data_expression> s;
  s.insert(v.begin(), v.end());
  return s.size() == v.size();
}

BOOST_AUTO_TEST_CASE(generate_values_test)
{
  std::string DATASPEC =
    "sort Identifiers = struct t1 | t2 | t3 | t4 | t5 | p1 | p2 | s1 | r1 | r2 | r3 | IL;                                                                                \n"
    "     signal_Messages = struct ic_set_proceed_signal | ic_set_stop_signal;                                                                                \n"
    "     track_Messages = struct dv_free_track | dv_occupied_track;                                                                                \n"
    "     point_Messages = struct ic_move_right_point | ic_move_left_point | dv_at_right_point | dv_at_left_point;                                                         \n"
    "     route_Messages = struct ic_cancel_route_route | ic_set_route_route;                                                                                \n"
    "     Interlocking_Messages = struct c_set_stop | c_move_left | c_set_route | c_IL_off | c_cancel_route | tc_set_proceed | c_IL_on | c_move_right;                     \n"
    "     environment_Messages = struct i_signal_status_proceed_signal_environment | i_signal_status_stop_signal_environment | i_track_status_occupied_track_environment | \n"
    "       i_track_status_free_track_environment | i_point_at_right_point_environment | i_point_locked_point_environment | i_point_ok_point_environment |                 \n"
    "       i_point_broken_point_environment | i_point_at_left_point_environment | i_route_cancelled_route_environment | i_route_established_route_environment;            \n"
    "     rail_yard_Messages = struct sv_set_proceed_signal_railyard | sv_set_stop_signal_railyard | sv_move_right_point_railyard | sv_move_left_point_railyard;           \n"
    "     Enum3 = struct e2_3 | e1_3 | e0_3;                                                                                \n"
    "     track__ready_States = struct track__ready_free_substate | track__ready_occupied_substate | track__ready_startup_substate | track__ready_nop;                     \n"
    "     Enum15 = struct e14_15 | e13_15 | e12_15 | e11_15 | e10_15 | e9_15 | e8_15 | e7_15 | e6_15 | e5_15 | e4_15 | e3_15 | e2_15 | e1_15 | e0_15;                      \n"
    "     point_States = struct point__broken_substate | point__startup_substate | point__working_substate;                                                                \n"
    "     point__working_States = struct point__working_right_substate | point__working_left_substate | point__working_moving_substate | point__working_nop;               \n"
    "     point__working_moving_States = struct point__working_moving_left_substate | point__working_moving_right_substate | point__working_moving_nop;                    \n"
    "     Enum7 = struct e6_7 | e5_7 | e4_7 | e3_7 | e2_7 | e1_7 | e0_7;                                                                                \n"
    "     signal_States = struct signal__stop_substate | signal__proceed_substate;                                                                                \n"
    "     Enum10 = struct e9_10 | e8_10 | e7_10 | e6_10 | e5_10 | e4_10 | e3_10 | e2_10 | e1_10 | e0_10;                                                                   \n"
    "     route_States = struct route__idle_substate | route__established_substate | route__setting_up_substate;                                                           \n"
    "     route__established_States = struct route__established_in_use_substate | route__established_active_substate | route__established_nop;                             \n"
    "     Enum11 = struct e10_11 | e9_11 | e8_11 | e7_11 | e6_11 | e5_11 | e4_11 | e3_11 | e2_11 | e1_11 | e0_11;                                                          \n"
    "     Enum13 = struct e12_13 | e11_13 | e10_13 | e9_13 | e8_13 | e7_13 | e6_13 | e5_13 | e4_13 | e3_13 | e2_13 | e1_13 | e0_13;                                        \n"
    "     HAL_device_States = struct HAL_device__normal_substate;                                                                                \n"
    "     track_States = struct track__ready_substate;                                                                                \n"
    ;
  data_specification dataspec = parse_data_specification(DATASPEC);

  for (const sort_expression& sort: dataspec.user_defined_sorts())
  {
    sort_expression s = normalize_sorts(sort, dataspec);
    std::clog << "--- sort " << data::pp(s) << std::endl;
    data_expression_vector v = generate_values(dataspec, s, 10);
    std::clog << " possible values: " << core::detail::print_set(v) << std::endl;
    BOOST_CHECK(v.size() <= 10);
    BOOST_CHECK(no_duplicates(v));
  }

  for (const alias& a: dataspec.user_defined_aliases())
  {
    sort_expression s = normalize_sorts(a.reference(),dataspec);
    std::clog << "--- sort " << data::pp(s) << std::endl;
    data_expression_vector v = generate_values(dataspec, s, 10);
    std::clog << " possible values: " << core::detail::print_set(v) << std::endl;
    BOOST_CHECK(v.size() <= 10);
    BOOST_CHECK(no_duplicates(v));
  }
}

BOOST_AUTO_TEST_CASE(constructors_that_are_not_a_normal_form_test)
{
  std::string dataspec_text =
    "sort FloorID     = struct F1 | F2;\n"
    " \n"
    "map  fSucc      : FloorID           -> FloorID;\n"
    "     equal      : FloorID # FloorID      -> Bool;\n"
    "\n"
    "var  f,g       : FloorID;\n"
    "eqn  F2               = fSucc(F1); \n"
    "     equal(fSucc(f),fSucc(g))  = equal(f,g);\n"
    "     equal(fSucc(f),F1)      = false;\n"
    "     equal(F1,fSucc(g))      = false;\n"
    "     equal(F1,F1)        = true;\n"
    "     f==g            = equal(f,g);\n"
    ;
  std::string variable_text = "f: FloorID;";
  std::string expression_text = "equal(f, F2)";
  const std::string& free_variable_text = variable_text;
  std::size_t number_of_solutions = 1;
  bool more_solutions_possible = false;
  enumerate(dataspec_text, variable_text, expression_text, free_variable_text, number_of_solutions, more_solutions_possible);
}

BOOST_AUTO_TEST_CASE(cannot_enumerate_real_default)
{
  typedef data::enumerator_list_element<data_expression> enumerator_element;

  data_expression result = data::sort_bool::true_();
  data::variable_list v  = { data::variable("r", data::sort_real::real_()) };
  data_expression phi = parse_data_expression("r == r", v);
  data::data_specification dataspec;
  dataspec.add_context_sort(data::sort_real::real_());
  data::rewriter R(dataspec);
  data::enumerator_identifier_generator id_generator("x");
  data::enumerator_algorithm<> E(R, dataspec, R, id_generator, (std::numeric_limits<std::size_t>::max)());
  data::rewriter::substitution_type sigma;

  try
  {
    E.enumerate(enumerator_element(v, phi),
                sigma,
                [&](const enumerator_element& p)
                {
                  result = data::optimized_and(result, p.expression());
                  return result == data::sort_bool::false_();
                },
                is_true,
                is_false
    );
  }
  catch (mcrl2::runtime_error& e)
  {
    std::cout << e.what() << std::endl;
    return;
  }
  BOOST_CHECK(false);
}

BOOST_AUTO_TEST_CASE(cannot_enumerate_real_with_substitution)
{
  typedef data::enumerator_list_element_with_substitution<data_expression> enumerator_element;

  data::data_specification dataspec;
  dataspec.add_context_sort(data::sort_real::real_());
  data::rewriter R(dataspec);
  data::variable_list v;
  v.push_front(data::variable("r", data::sort_real::real_()));
  data_expression phi = parse_data_expression("r == r", v);
  data::mutable_indexed_substitution<> sigma;
  std::size_t max_count = 1000;
  bool throw_exceptions = true;
  data::enumerator_identifier_generator id_generator;
  data::enumerator_algorithm_with_iterator<rewriter, enumerator_element> E(R, dataspec, R, id_generator, max_count, throw_exceptions);
  data::enumerator_queue<enumerator_element> P(enumerator_element(v, phi));
  try {
    for (auto i = E.begin(sigma, P); i != E.end() ; ++i)
    {
      // skip
    }
  }
  catch (mcrl2::runtime_error& e)
  {
    std::cout << e.what() << std::endl;
    return;
  }
  BOOST_CHECK(false);
}

BOOST_AUTO_TEST_CASE(enumerate_callback)
{
  typedef data::enumerator_list_element<data_expression> enumerator_element;
  enumerator_identifier_generator id_generator;
  data_specification dataspec;
  dataspec.add_context_sort(data::sort_int::int_());
  std::size_t max_count = 10;
  data::rewriter r(dataspec);
  data::enumerator_algorithm<rewriter> E(r, dataspec, r, id_generator, max_count);

  auto enumerate = [&](const data_expression& x)
  {
    data::mutable_indexed_substitution<> sigma;
    data_expression result;
    id_generator.clear();
    if (is_forall(x))
    {
      const auto& x_ = atermpp::down_cast<forall>(x);
      result = true_();
      E.enumerate(enumerator_element(x_.variables(), r(x_.body())),
                  sigma,
                  [&](const enumerator_element& p)
                  {
                    result = data::optimized_and(result, p.expression());
                    return is_false(result);
                  },
                  is_true,
                  is_false
      );
    }
    else if (is_exists(x))
    {
      const exists& x_ = atermpp::down_cast<exists>(x);
      result = false_();
      E.enumerate(enumerator_element(x_.variables(), r(x_.body())),
                  sigma,
                  [&](const enumerator_element& p)
                  {
                    result = data::optimized_or(result, p.expression());
                    return is_true(result);
                  },
                  is_false,
                  is_true
      );
    }
    return result;
  };

  BOOST_CHECK_EQUAL(enumerate(parse_data_expression("forall n: Nat. n < 2")), false_());
  BOOST_CHECK_EQUAL(enumerate(parse_data_expression("exists n: Nat. n < 2")), true_());
}

BOOST_AUTO_TEST_CASE(enumerate_expressions_test)
{
  const std::string dataspec_text =
          "sort D = struct d1(E) | d2(E);\n"
          "     E = struct e1 | e2;      \n"
          ;
  std::string variable_text = "d:D";
  data_specification dataspec = parse_data_specification(dataspec_text);
  data::variable v = parse_variable(variable_text, dataspec);
  rewriter r(dataspec);
  std::string result = core::detail::print_list(enumerate_expressions(v.sort(), dataspec, r));
  std::string expected_result1 = "[ d1(e1), d1(e2), d2(e1), d2(e2) ]";
  std::string expected_result2 = "[ d2(e2), d2(e1), d1(e2), d1(e1) ]"; // When garbage collection is frequent, this answer occurs.
  BOOST_CHECK(result == expected_result1 || result == expected_result2);
}


#include <mochimochi/multi_classifier.hpp>
#include <mochimochi/utility.hpp>
#include <boost/program_options.hpp>
#include <iostream>

int main(const int ac, const char* const * const av) {
  using namespace boost::program_options;

  options_description description("options");
  description.add_options()
    ("help", "")
    ("dim", value<int>()->default_value(0), "データの次元数")
    ("class", value<std::size_t>()->default_value(0), "クラス数")
    ("train", value<std::string>()->default_value(""), "学習データのファイルパス")
    ("test", value<std::string>()->default_value(""), "評価データのファイルパス")
    ("c", value<double>()->default_value(0.5), "ハイパパラメータ(C)")
    ("select", value<int>()->default_value(2), "0:PA 1:PA-1 2:PA-2");

  variables_map vm;
  store(parse_command_line(ac, av, description), vm);
  notify(vm);

  if(vm.count("help")) { std::cout << description << std::endl; }

  const auto dim = vm["dim"].as<int>();
  const auto n_class = vm["class"].as<std::size_t>();
  const auto train_path = vm["train"].as<std::string>();
  const auto test_path = vm["test"].as<std::string>();
  const auto c = vm["c"].as<double>();
  const auto select = vm["select"].as<int>();

  std::string line;
  std::ifstream train_data(train_path);

  MPA mpa(dim, n_class, c, select);
  std::cout << "training..." << std::endl;
  while(std::getline(train_data, line)) {
    auto data = utility::read_ones<std::size_t>(line, dim);
    mpa.update(data.second, data.first);
  }

  int collect = 0;
  int all = 0;
  std::ifstream test_data(test_path);
  std::cout << "predicting..." << std::endl;
  while(std::getline(test_data, line)) {
    auto data = utility::read_ones<std::size_t>(line, dim);
    auto pred = mpa.predict(data.second);
    if(pred == data.first) {
      ++collect;
    }
    ++all;
  }

  std::cout << "Accuracy = " << (100.0 * collect / all) << "% (" << collect << "/" << all << ")" << std::endl;

  return 0;
}


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

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.

Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.

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

#include "PETDynamicView.h"

#include "mitkWorkbenchUtil.h"

#include "mitkAterialInputFunctionGenerator.h"

#include "mitkOneTissueCompartmentModelFactory.h"
#include "mitkOneTissueCompartmentModelParameterizer.h"
#include "mitkExtendedOneTissueCompartmentModelFactory.h"
#include "mitkExtendedOneTissueCompartmentModelParameterizer.h"
#include "mitkTwoTissueCompartmentFDGModelFactory.h"
#include "mitkTwoTissueCompartmentFDGModelParameterizer.h"
#include "mitkTwoTissueCompartmentModelFactory.h"
#include "mitkTwoTissueCompartmentModelParameterizer.h"
#include "mitkNumericTwoTissueCompartmentModelFactory.h"
#include "mitkNumericTwoTissueCompartmentModelParameterizer.h"

#include <mitkNodePredicateAnd.h>
#include <mitkNodePredicateNot.h>
#include <mitkNodePredicateProperty.h>
#include <mitkNodePredicateDataType.h>
#include <mitkNodePredicateOr.h>
#include <mitkPixelBasedParameterFitImageGenerator.h>
#include <mitkROIBasedParameterFitImageGenerator.h>
#include <mitkLevenbergMarquardtModelFitFunctor.h>
#include <mitkSumOfSquaredDifferencesFitCostFunction.h>
#include <mitkSquaredDifferencesFitCostFunction.h>
#include <mitkChiSquareFitCostFunction.h>
#include <mitkReducedChiSquareFitCostFunction.h>
#include <mitkSimpleBarrierConstraintChecker.h>
#include <mitkModelFitResultHelper.h>
#include <mitkImageTimeSelector.h>
#include <mitkMaskedDynamicImageStatisticsGenerator.h>
#include <mitkExtractTimeGrid.h>
#include <mitkInitialParameterizationDelegateBase.h>

#include <QMessageBox>
#include <QThreadPool>
#include <QmitkDataStorageComboBox.h>
#include <QFileDialog>
#include <boost/tokenizer.hpp>

// Includes for image casting between ITK and MITK
#include <mitkImage.h>
#include "mitkImageCast.h"
#include "mitkITKImageImport.h"
#include <itkImage.h>
#include <itkImageRegionIterator.h>
#include <iostream>

const std::string PETDynamicView::VIEW_ID = "org.mitk.gui.qt.pharmacokinetics.pet";

inline double convertToDouble(const std::string& data)
{
  std::istringstream stepStream(data);
  double value = 0.0;
  stepStream >> value;
  return value;
}

void PETDynamicView::SetFocus()
{
  m_Controls.btnModelling->setFocus();
}

void PETDynamicView::CreateQtPartControl(QWidget* parent)
{
  m_Controls.setupUi(parent);

  m_Controls.btnModelling->setEnabled(false);
  m_Controls.errorMessageLabel->hide();

  this->InitModelComboBox();

  connect(m_Controls.btnModelling, SIGNAL(clicked()), this, SLOT(OnModellingButtonClicked()));

  connect(m_Controls.comboModel, SIGNAL(currentIndexChanged(int)), this, SLOT(OnModellSet(int)));
  connect(m_Controls.radioPixelBased, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls()));

  //AIF setting
  m_Controls.groupAIF->hide();
  m_Controls.btnAIFFile->setEnabled(false);
  m_Controls.btnAIFFile->setEnabled(false);
  m_Controls.radioAIFImage->setChecked(true);
  m_Controls.comboAIFMask->SetDataStorage(this->GetDataStorage());
  m_Controls.comboAIFMask->SetPredicate(m_IsMaskPredicate);
  m_Controls.comboAIFMask->setVisible(true);
  m_Controls.comboAIFMask->setEnabled(true);
  m_Controls.comboAIFImage->SetDataStorage(this->GetDataStorage());
  m_Controls.comboAIFImage->SetPredicate(m_IsNoMaskImagePredicate);
  m_Controls.comboAIFImage->setEnabled(false);
  m_Controls.checkDedicatedAIFImage->setEnabled(true);
  m_Controls.HCLSpinBox->setValue(0.0);

  connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.comboAIFMask,
          SLOT(setVisible(bool)));
  connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.labelAIFMask,
          SLOT(setVisible(bool)));
  connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.checkDedicatedAIFImage,
          SLOT(setVisible(bool)));
  connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.comboAIFMask,
          SLOT(setEnabled(bool)));
  connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.checkDedicatedAIFImage,
          SLOT(setEnabled(bool)));
  connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.checkDedicatedAIFImage,
          SLOT(setVisible(bool)));
  connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.comboAIFImage,
          SLOT(setVisible(bool)));
  connect(m_Controls.checkDedicatedAIFImage, SIGNAL(toggled(bool)), m_Controls.comboAIFImage,
          SLOT(setEnabled(bool)));
  connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls()));
  connect(m_Controls.radioAIFFile, SIGNAL(toggled(bool)), m_Controls.btnAIFFile,
          SLOT(setEnabled(bool)));
  connect(m_Controls.radioAIFFile, SIGNAL(toggled(bool)), m_Controls.aifFilePath,
          SLOT(setEnabled(bool)));
  connect(m_Controls.radioAIFFile, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls()));

  connect(m_Controls.btnAIFFile, SIGNAL(clicked()), this, SLOT(LoadAIFfromFile()));

  //Model fit configuration
  m_Controls.groupBox_FitConfiguration->hide();

  m_Controls.checkBox_Constraints->setEnabled(false);
  m_Controls.constraintManager->setEnabled(false);
  m_Controls.initialValuesManager->setEnabled(false);
  m_Controls.initialValuesManager->setDataStorage(this->GetDataStorage());

  connect(m_Controls.radioButton_StartParameters, SIGNAL(toggled(bool)), this,
          SLOT(UpdateGUIControls()));
  connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), this,
          SLOT(UpdateGUIControls()));
  connect(m_Controls.initialValuesManager, SIGNAL(initialValuesChanged(void)), this, SLOT(UpdateGUIControls()));

  connect(m_Controls.radioButton_StartParameters, SIGNAL(toggled(bool)),
          m_Controls.initialValuesManager,
          SLOT(setEnabled(bool)));
  connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), m_Controls.constraintManager,
          SLOT(setEnabled(bool)));
  connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), m_Controls.constraintManager,
          SLOT(setVisible(bool)));

  UpdateGUIControls();

}

void PETDynamicView::UpdateGUIControls()
{
  m_Controls.lineFitName->setPlaceholderText(QString::fromStdString(this->GetDefaultFitName()));
  m_Controls.lineFitName->setEnabled(!m_FittingInProgress);

  m_Controls.checkBox_Constraints->setEnabled(m_modelConstraints.IsNotNull());

   bool is1TCMFactory = dynamic_cast<mitk::OneTissueCompartmentModelFactory*>
                        (m_selectedModelFactory.GetPointer()) != nullptr;
   bool isExt1TCMFactory = dynamic_cast<mitk::ExtendedOneTissueCompartmentModelFactory*>
                        (m_selectedModelFactory.GetPointer()) != nullptr;
   bool isFDGCMFactory = dynamic_cast<mitk::TwoTissueCompartmentFDGModelFactory*>
                        (m_selectedModelFactory.GetPointer()) != nullptr;

  bool is2TCMFactory = dynamic_cast<mitk::TwoTissueCompartmentModelFactory*>
                       (m_selectedModelFactory.GetPointer()) != nullptr ||
                       dynamic_cast<mitk::NumericTwoTissueCompartmentModelFactory*>
                       (m_selectedModelFactory.GetPointer()) != nullptr;

  m_Controls.groupAIF->setVisible(is1TCMFactory || isExt1TCMFactory || isFDGCMFactory || is2TCMFactory);

  m_Controls.groupBox_FitConfiguration->setVisible(m_selectedModelFactory);

  m_Controls.groupBox->setEnabled(!m_FittingInProgress);
  m_Controls.comboModel->setEnabled(!m_FittingInProgress);
  m_Controls.groupAIF->setEnabled(!m_FittingInProgress);
  m_Controls.groupBox_FitConfiguration->setEnabled(!m_FittingInProgress);

  m_Controls.radioROIbased->setEnabled(m_selectedMask.IsNotNull());

  m_Controls.btnModelling->setEnabled(m_selectedImage.IsNotNull()
                                      && m_selectedModelFactory.IsNotNull() && !m_FittingInProgress && CheckModelSettings());
}

//void PETDynamicView::OnModelSettingChanged()
//{
//  bool ok = m_selectedImage.IsNotNull() && m_selectedModelFactory.IsNotNull() && !m_FittingInProgress && CheckModelSettings();

//  m_Controls.btnModelling->setEnabled(ok);
//}

void PETDynamicView::OnModellSet(int index)
{
  m_selectedModelFactory = nullptr;

  if (index > 0)
  {
    if (static_cast<ModelFactoryStackType::size_type>(index) <= m_FactoryStack.size() )
    {
        m_selectedModelFactory = m_FactoryStack[index - 1];
    }
    else
    {
        MITK_WARN << "Invalid model index. Index outside of the factory stack. Factory stack size: "<< m_FactoryStack.size() << "; invalid index: "<< index;
    }
  }

  if (m_selectedModelFactory)
  {
    this->m_modelConstraints = dynamic_cast<mitk::SimpleBarrierConstraintChecker*>
                               (m_selectedModelFactory->CreateDefaultConstraints().GetPointer());

    m_Controls.initialValuesManager->setInitialValues(m_selectedModelFactory->GetParameterNames(),
        m_selectedModelFactory->GetDefaultInitialParameterization());

    if (this->m_modelConstraints.IsNull())
    {
      this->m_modelConstraints = mitk::SimpleBarrierConstraintChecker::New();
    }

    m_Controls.constraintManager->setChecker(this->m_modelConstraints,
        this->m_selectedModelFactory->GetParameterNames());
  }

  m_Controls.checkBox_Constraints->setEnabled(m_modelConstraints.IsNotNull());

  UpdateGUIControls();
}

std::string PETDynamicView::GetFitName() const
{
  std::string fitName = m_Controls.lineFitName->text().toStdString();
  if (fitName.empty())
  {
    fitName = m_Controls.lineFitName->placeholderText().toStdString();
  }
  return fitName;
}

std::string PETDynamicView::GetDefaultFitName() const
{
    std::string defaultName = "undefined model";

    if (this->m_selectedModelFactory.IsNotNull())
    {
        defaultName = this->m_selectedModelFactory->GetClassID();
    }

    if (this->m_Controls.radioPixelBased->isChecked())
    {
        defaultName += "_pixel";
    }
    else
    {
        defaultName += "_roi";
    }

    return defaultName;
}

void PETDynamicView::OnModellingButtonClicked()
{
  //check if all static parameters set
  if (m_selectedModelFactory.IsNotNull() && CheckModelSettings())
  {
    mitk::ParameterFitImageGeneratorBase::Pointer generator = nullptr;
    mitk::modelFit::ModelFitInfo::Pointer fitSession = nullptr;

    bool isOTCFactory = dynamic_cast<mitk::OneTissueCompartmentModelFactory*>
                        (m_selectedModelFactory.GetPointer()) != nullptr;
    bool isextOTCFactory = dynamic_cast<mitk::ExtendedOneTissueCompartmentModelFactory*>
                        (m_selectedModelFactory.GetPointer()) != nullptr;

    bool isFDGFactory = dynamic_cast<mitk::TwoTissueCompartmentFDGModelFactory*>
                        (m_selectedModelFactory.GetPointer()) != nullptr;

    bool isTTCFactory = dynamic_cast<mitk::TwoTissueCompartmentModelFactory*>
                        (m_selectedModelFactory.GetPointer()) != nullptr;
    bool isNumTTCFactory = dynamic_cast<mitk::NumericTwoTissueCompartmentModelFactory*>
                           (m_selectedModelFactory.GetPointer()) != nullptr;

    if (isOTCFactory)
    {
      if (this->m_Controls.radioPixelBased->isChecked())
      {
        GenerateModelFit_PixelBased<mitk::OneTissueCompartmentModelParameterizer>(fitSession, generator);
      }
      else
      {
        GenerateModelFit_ROIBased<mitk::OneTissueCompartmentModelParameterizer>(fitSession, generator);
      }
    }

    else if (isextOTCFactory)
    {
      if (this->m_Controls.radioPixelBased->isChecked())
      {
        GenerateModelFit_PixelBased<mitk::ExtendedOneTissueCompartmentModelParameterizer>(fitSession, generator);
      }
      else
      {
        GenerateModelFit_ROIBased<mitk::ExtendedOneTissueCompartmentModelParameterizer>(fitSession, generator);
      }
    }
    else if (isFDGFactory)
    {
      if (this->m_Controls.radioPixelBased->isChecked())
      {
        GenerateModelFit_PixelBased<mitk::TwoTissueCompartmentFDGModelParameterizer>(fitSession, generator);
      }
      else
      {
        GenerateModelFit_ROIBased<mitk::TwoTissueCompartmentFDGModelParameterizer>(fitSession, generator);
      }
    }

    else if (isTTCFactory)
    {
      if (this->m_Controls.radioPixelBased->isChecked())
      {
        GenerateModelFit_PixelBased<mitk::TwoTissueCompartmentModelParameterizer>(fitSession, generator);
      }
      else
      {
        GenerateModelFit_ROIBased<mitk::TwoTissueCompartmentModelParameterizer>(fitSession, generator);
      }
    }

    else if (isNumTTCFactory)
    {
      if (this->m_Controls.radioPixelBased->isChecked())
      {
        GenerateModelFit_PixelBased<mitk::NumericTwoTissueCompartmentModelParameterizer>(fitSession,
            generator);
      }
      else
      {
        GenerateModelFit_ROIBased<mitk::NumericTwoTissueCompartmentModelParameterizer>(fitSession,
            generator);
      }
    }

    //add other models with else if

    if (generator.IsNotNull() && fitSession.IsNotNull())
    {
      m_FittingInProgress = true;
      DoFit(fitSession, generator);
    }
    else
    {
      QMessageBox box;
      box.setText("Fitting error!");
      box.setInformativeText("Could not establish fitting job. Error when setting ab generator, model parameterizer or session info.");
      box.setStandardButtons(QMessageBox::Ok);
      box.setDefaultButton(QMessageBox::Ok);
      box.setIcon(QMessageBox::Warning);
      box.exec();
    }

  }
  else
  {
    QMessageBox box;
    box.setText("Static parameters for model are not set!");
    box.setInformativeText("Some static parameters, that are needed for calculation are not set and equal to zero. Modeling not possible");
    box.setStandardButtons(QMessageBox::Ok);
    box.setDefaultButton(QMessageBox::Ok);
    box.setIcon(QMessageBox::Warning);
    box.exec();
  }
}

void PETDynamicView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/,
                                        const QList<mitk::DataNode::Pointer>& selectedNodes)
{
    m_selectedMaskNode = nullptr;
    m_selectedMask = nullptr;

    m_Controls.errorMessageLabel->setText("");
    m_Controls.masklabel->setText("No (valid) mask selected.");
    m_Controls.timeserieslabel->setText("No (valid) series selected.");

    QList<mitk::DataNode::Pointer> nodes = selectedNodes;

    if (nodes.size() > 0 && this->m_IsNoMaskImagePredicate->CheckNode(nodes.front()))
    {
      this->m_selectedNode = nodes.front();
      auto selectedImage = dynamic_cast<mitk::Image*>(this->m_selectedNode->GetData());
      m_Controls.timeserieslabel->setText((this->m_selectedNode->GetName()).c_str());

      if (selectedImage != this->m_selectedImage)
      {
        if (selectedImage)
        {
          this->m_Controls.initialValuesManager->setReferenceImageGeometry(selectedImage->GetGeometry());
        }
        else
        {
          this->m_Controls.initialValuesManager->setReferenceImageGeometry(nullptr);
        }
      }
      this->m_selectedImage = selectedImage;
      nodes.pop_front();
    }
    else
    {
      this->m_selectedNode = nullptr;
      this->m_selectedImage = nullptr;
      this->m_Controls.initialValuesManager->setReferenceImageGeometry(nullptr);
    }

    if (nodes.size() > 0 && this->m_IsMaskPredicate->CheckNode(nodes.front()))
    {
        this->m_selectedMaskNode = nodes.front();
        this->m_selectedMask = dynamic_cast<mitk::Image*>(this->m_selectedMaskNode->GetData());

        if (this->m_selectedMask->GetTimeSteps() > 1)
        {
          MITK_INFO <<
                    "Selected mask has multiple timesteps. Only use first timestep to mask model fit. Mask name: " <<
                    m_selectedMaskNode->GetName();
          mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New();
          maskedImageTimeSelector->SetInput(this->m_selectedMask);
          maskedImageTimeSelector->SetTimeNr(0);
          maskedImageTimeSelector->UpdateLargestPossibleRegion();
          this->m_selectedMask = maskedImageTimeSelector->GetOutput();
        }

        m_Controls.masklabel->setText((this->m_selectedMaskNode->GetName()).c_str());
    }

    if (m_selectedMask.IsNull())
    {
      this->m_Controls.radioPixelBased->setChecked(true);
    }

    m_Controls.errorMessageLabel->show();

    UpdateGUIControls();
}

bool PETDynamicView::CheckModelSettings() const
{
  bool ok = true;

  //check wether any model is set at all. Otherwise exit with false
  if (m_selectedModelFactory.IsNotNull())
  {

    bool isOTCFactory = dynamic_cast<mitk::OneTissueCompartmentModelFactory*>
                          (m_selectedModelFactory.GetPointer()) != nullptr;
    bool isextOTCFactory = dynamic_cast<mitk::ExtendedOneTissueCompartmentModelFactory*>
                          (m_selectedModelFactory.GetPointer()) != nullptr;
    bool isFDGFactory = dynamic_cast<mitk::TwoTissueCompartmentFDGModelFactory*>
                        (m_selectedModelFactory.GetPointer()) != nullptr;
    bool isTTCFactory = dynamic_cast<mitk::TwoTissueCompartmentModelFactory*>
                        (m_selectedModelFactory.GetPointer()) != nullptr;
    bool isNumTTCFactory = dynamic_cast<mitk::NumericTwoTissueCompartmentModelFactory*>
                           (m_selectedModelFactory.GetPointer()) != nullptr;

    if (isOTCFactory || isextOTCFactory || isFDGFactory || isTTCFactory || isNumTTCFactory)
    {
        if (this->m_Controls.radioAIFImage->isChecked())
        {
          ok = ok && m_Controls.comboAIFMask->GetSelectedNode().IsNotNull();

          if (this->m_Controls.checkDedicatedAIFImage->isChecked())
          {
            ok = ok && m_Controls.comboAIFImage->GetSelectedNode().IsNotNull();
          }
        }
        else if (this->m_Controls.radioAIFFile->isChecked())
        {
          ok = ok && (this->AIFinputGrid.size() != 0) && (this->AIFinputFunction.size() != 0);
        }
        else
        {
          ok = false;
        }

     }
    //add other models as else if and check wether all needed static parameters are set
    else
    {
      ok = false;
    }

    if (this->m_Controls.radioButton_StartParameters->isChecked() && !this->m_Controls.initialValuesManager->hasValidInitialValues())
    {
      std::string warning = "Warning. Invalid start parameters. At least one parameter as an invalid image setting as source.";
      MITK_ERROR << warning;
      m_Controls.infoBox->append(QString("") + QString::fromStdString(warning) + QString(""));

      ok = false;
    };
  }
  else
  {
    ok = false;
  }

  return ok;
}

void PETDynamicView::ConfigureInitialParametersOfParameterizer(mitk::ModelParameterizerBase*
    parameterizer) const
{
  if (m_Controls.radioButton_StartParameters->isChecked())
  {
    //use user defined initial parameters
    mitk::InitialParameterizationDelegateBase::Pointer paramDelegate = m_Controls.initialValuesManager->getInitialParametrizationDelegate();
    parameterizer->SetInitialParameterizationDelegate(paramDelegate);
  }
}

template <typename TParameterizer>
void PETDynamicView::GenerateModelFit_PixelBased(
  mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo,
  mitk::ParameterFitImageGeneratorBase::Pointer& generator)
{
  mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator =
    mitk::PixelBasedParameterFitImageGenerator::New();

  typename TParameterizer::Pointer modelParameterizer =
    TParameterizer::New();

  mitk::AIFBasedModelBase::AterialInputFunctionType aif;
  mitk::AIFBasedModelBase::TimeGridType aifTimeGrid;

  GetAIF(aif, aifTimeGrid);

  //Model configuration (static parameters) can be done now
  modelParameterizer->SetAIF(aif);
  modelParameterizer->SetAIFTimeGrid(aifTimeGrid);
  this->ConfigureInitialParametersOfParameterizer(modelParameterizer);

  //Specify fitting strategy and criterion parameters
  mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer);

  //Parametrize fit generator
  fitGenerator->SetModelParameterizer(modelParameterizer);
  std::string roiUID = "";

  if (m_selectedMask.IsNotNull())
  {
    fitGenerator->SetMask(m_selectedMask);
    roiUID = m_selectedMask->GetUID();
  }

  fitGenerator->SetDynamicImage(this->m_selectedImage);
  fitGenerator->SetFitFunctor(fitFunctor);

  generator = fitGenerator.GetPointer();

  //Create model info
  modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer,
    m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), this->GetFitName(), roiUID);

  mitk::ScalarListLookupTable::ValueType infoSignal;

  for (mitk::AIFBasedModelBase::AterialInputFunctionType::const_iterator pos = aif.begin();
       pos != aif.end(); ++pos)
  {
    infoSignal.push_back(*pos);
  }

  modelFitInfo->inputData.SetTableValue("AIF", infoSignal);
}

template <typename TParameterizer>
void PETDynamicView::GenerateModelFit_ROIBased(
  mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo,
  mitk::ParameterFitImageGeneratorBase::Pointer& generator)
{
  mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator =
    mitk::ROIBasedParameterFitImageGenerator::New();

  typename TParameterizer::Pointer modelParameterizer =
    TParameterizer::New();

  //Compute AIF
  mitk::AterialInputFunctionGenerator::Pointer aifGenerator =
    mitk::AterialInputFunctionGenerator::New();
  aifGenerator->SetDynamicImage(this->m_selectedImage);
  aifGenerator->SetMask(this->m_selectedAIFMask);

  mitk::AIFBasedModelBase::AterialInputFunctionType aif = aifGenerator->GetAterialInputFunction();
  mitk::AIFBasedModelBase::TimeGridType aifTimeGrid = aifGenerator->GetAterialInputFunctionTimeGrid();

  //Model configuration (static parameters) can be done now
  modelParameterizer->SetAIF(aif);
  modelParameterizer->SetAIFTimeGrid(aifTimeGrid);

  this->ConfigureInitialParametersOfParameterizer(modelParameterizer);

  //Compute ROI signal
  mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator =
    mitk::MaskedDynamicImageStatisticsGenerator::New();
  signalGenerator->SetMask(m_selectedMask);
  signalGenerator->SetDynamicImage(m_selectedImage);
  signalGenerator->Generate();

  mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean();

  //Specify fitting strategy and criterion parameters
  mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer);

  //Parametrize fit generator
  fitGenerator->SetModelParameterizer(modelParameterizer);
  fitGenerator->SetMask(m_selectedMask);
  fitGenerator->SetFitFunctor(fitFunctor);
  fitGenerator->SetSignal(roiSignal);
  fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(m_selectedImage));

  generator = fitGenerator.GetPointer();

  std::string roiUID = "";
  if (m_selectedMask.IsNotNull())
  {
    roiUID = m_selectedMask->GetUID();
  }

  //Create model info
  modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer,
    m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), this->GetFitName(), roiUID);

  mitk::ScalarListLookupTable::ValueType infoSignal;

  for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos =
         roiSignal.begin(); pos != roiSignal.end(); ++pos)
  {
    infoSignal.push_back(*pos);
  }

  modelFitInfo->inputData.SetTableValue("ROI", infoSignal);

  infoSignal.clear();

  for (mitk::AIFBasedModelBase::AterialInputFunctionType::const_iterator pos = aif.begin();
       pos != aif.end(); ++pos)
  {
    infoSignal.push_back(*pos);
  }

  modelFitInfo->inputData.SetTableValue("AIF", infoSignal);
}

void PETDynamicView::DoFit(const mitk::modelFit::ModelFitInfo* fitSession,
                           mitk::ParameterFitImageGeneratorBase* generator)
{
  std::stringstream message;
  message << "" << "Fitting Data Set . . ." << "";
  m_Controls.errorMessageLabel->setText(message.str().c_str());
  m_Controls.errorMessageLabel->show();

  /////////////////////////
  //create job and put it into the thread pool
  ParameterFitBackgroundJob* pJob = new ParameterFitBackgroundJob(generator, fitSession,
      this->m_selectedNode);

  pJob->setAutoDelete(true);

  connect(pJob, SIGNAL(Error(QString)), this, SLOT(OnJobError(QString)));
  connect(pJob, SIGNAL(Finished()), this, SLOT(OnJobFinished()));
  connect(pJob, SIGNAL(ResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType,
                       const ParameterFitBackgroundJob*)), this,
          SLOT(OnJobResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType,
                                        const ParameterFitBackgroundJob*)), Qt::BlockingQueuedConnection);

  connect(pJob, SIGNAL(JobProgress(double)), this, SLOT(OnJobProgress(double)));
  connect(pJob, SIGNAL(JobStatusChanged(QString)), this, SLOT(OnJobStatusChanged(QString)));

  QThreadPool* threadPool = QThreadPool::globalInstance();
  threadPool->start(pJob);
}

PETDynamicView::PETDynamicView() : m_FittingInProgress(false)
{
  m_selectedImage = nullptr;
  m_selectedMask = nullptr;

  mitk::ModelFactoryBase::Pointer factory =
    mitk::OneTissueCompartmentModelFactory::New().GetPointer();
    m_FactoryStack.push_back(factory);
    factory = mitk::ExtendedOneTissueCompartmentModelFactory::New().GetPointer();
    m_FactoryStack.push_back(factory);
    factory = mitk::TwoTissueCompartmentModelFactory::New().GetPointer();
    m_FactoryStack.push_back(factory);
    factory = mitk::TwoTissueCompartmentFDGModelFactory::New().GetPointer();
    m_FactoryStack.push_back(factory);
    factory = mitk::NumericTwoTissueCompartmentModelFactory::New().GetPointer();
    m_FactoryStack.push_back(factory);

  mitk::NodePredicateDataType::Pointer isLabelSet = mitk::NodePredicateDataType::New("LabelSetImage");
  mitk::NodePredicateDataType::Pointer isImage = mitk::NodePredicateDataType::New("Image");
  mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true));
  mitk::NodePredicateAnd::Pointer isLegacyMask = mitk::NodePredicateAnd::New(isImage, isBinary);

  mitk::NodePredicateOr::Pointer isMask = mitk::NodePredicateOr::New(isLegacyMask, isLabelSet);
  mitk::NodePredicateAnd::Pointer isNoMask = mitk::NodePredicateAnd::New(isImage, mitk::NodePredicateNot::New(isMask));

  this->m_IsMaskPredicate = mitk::NodePredicateAnd::New(isMask, mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))).GetPointer();

  this->m_IsNoMaskImagePredicate = mitk::NodePredicateAnd::New(isNoMask, mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))).GetPointer();

}

void PETDynamicView::OnJobFinished()
{
  this->m_Controls.infoBox->append(QString("Fitting finished"));
  this->m_FittingInProgress = false;
};

void PETDynamicView::OnJobError(QString err)
{
  MITK_ERROR << err.toStdString().c_str();

  m_Controls.infoBox->append(QString("") + err + QString(""));

};

void PETDynamicView::OnJobResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType results,
    const ParameterFitBackgroundJob* pJob)
{
  //Store the resulting parameter fit image via convenience helper function in data storage
  //(handles the correct generation of the nodes and their properties)
  mitk::modelFit::StoreResultsInDataStorage(this->GetDataStorage(), results, pJob->GetParentNode());

  m_Controls.errorMessageLabel->setText("");
  m_Controls.errorMessageLabel->hide();
};

void PETDynamicView::OnJobProgress(double progress)
{
  QString report = QString("Progress. ") + QString::number(progress);
  this->m_Controls.infoBox->append(report);
};

void PETDynamicView::OnJobStatusChanged(QString info)
{
  this->m_Controls.infoBox->append(info);
}

void PETDynamicView::InitModelComboBox() const
{
  this->m_Controls.comboModel->clear();
  this->m_Controls.comboModel->addItem(tr("No model selected"));

  for (ModelFactoryStackType::const_iterator pos = m_FactoryStack.begin();
       pos != m_FactoryStack.end(); ++pos)
  {
    this->m_Controls.comboModel->addItem(QString::fromStdString((*pos)->GetClassID()));
  }

  this->m_Controls.comboModel->setCurrentIndex(0);
};

mitk::ModelFitFunctorBase::Pointer PETDynamicView::CreateDefaultFitFunctor(
  const mitk::ModelParameterizerBase* parameterizer) const
{
  mitk::LevenbergMarquardtModelFitFunctor::Pointer fitFunctor =
    mitk::LevenbergMarquardtModelFitFunctor::New();
  mitk::SumOfSquaredDifferencesFitCostFunction::Pointer evaluation =
    mitk::SumOfSquaredDifferencesFitCostFunction::New();

  fitFunctor->RegisterEvaluationParameter("sum_diff^2", evaluation);

  mitk::ChiSquareFitCostFunction::Pointer chi2 =
          mitk::ChiSquareFitCostFunction::New();
  fitFunctor->RegisterEvaluationParameter("Chi^2", chi2);

  mitk::ReducedChiSquareFitCostFunction::Pointer redchi2 =
          mitk::ReducedChiSquareFitCostFunction::New();
  fitFunctor->RegisterEvaluationParameter("redChi^2", redchi2);

  if (m_Controls.checkBox_Constraints->isChecked())
  {
    fitFunctor->SetConstraintChecker(m_modelConstraints);
  }

  mitk::ModelBase::Pointer refModel = parameterizer->GenerateParameterizedModel();

  ::itk::LevenbergMarquardtOptimizer::ScalesType scales;
  scales.SetSize(refModel->GetNumberOfParameters());
  scales.Fill(1.0);
  fitFunctor->SetScales(scales);

  return fitFunctor.GetPointer();
}

void PETDynamicView::GetAIF(mitk::AIFBasedModelBase::AterialInputFunctionType& aif,
                             mitk::AIFBasedModelBase::AterialInputFunctionType& aifTimeGrid)
{
  if (this->m_Controls.radioAIFFile->isChecked())
  {
    aif.clear();
    aifTimeGrid.clear();

    aif.SetSize(AIFinputFunction.size());
    aifTimeGrid.SetSize(AIFinputGrid.size());

    aif.fill(0.0);
    aifTimeGrid.fill(0.0);

    itk::Array<double>::iterator aifPos = aif.begin();

    for (std::vector<double>::const_iterator pos = AIFinputFunction.begin();
         pos != AIFinputFunction.end(); ++pos, ++aifPos)
    {
      *aifPos = *pos;
    }

    itk::Array<double>::iterator gridPos = aifTimeGrid.begin();

    for (std::vector<double>::const_iterator pos = AIFinputGrid.begin(); pos != AIFinputGrid.end();
         ++pos, ++gridPos)
    {
      *gridPos = *pos;
    }
  }
  else if (this->m_Controls.radioAIFImage->isChecked())
  {
    aif.clear();
    aifTimeGrid.clear();

    mitk::AterialInputFunctionGenerator::Pointer aifGenerator =
      mitk::AterialInputFunctionGenerator::New();

    //Hematocrit level
    aifGenerator->SetHCL(this->m_Controls.HCLSpinBox->value());

    //mask settings
    this->m_selectedAIFMaskNode = m_Controls.comboAIFMask->GetSelectedNode();
    this->m_selectedAIFMask = dynamic_cast<mitk::Image*>(this->m_selectedAIFMaskNode->GetData());

    if (this->m_selectedAIFMask->GetTimeSteps() > 1)
    {
      MITK_INFO <<
                "Selected AIF mask has multiple timesteps. Only use first timestep to mask model fit. AIF Mask name: "
                <<
                m_selectedAIFMaskNode->GetName() ;
      mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New();
      maskedImageTimeSelector->SetInput(this->m_selectedAIFMask);
      maskedImageTimeSelector->SetTimeNr(0);
      maskedImageTimeSelector->UpdateLargestPossibleRegion();
      this->m_selectedAIFMask = maskedImageTimeSelector->GetOutput();
    }

    if (this->m_selectedAIFMask.IsNotNull())
    {
      aifGenerator->SetMask(this->m_selectedAIFMask);
    }

    //image settings
    if (this->m_Controls.checkDedicatedAIFImage->isChecked())
    {
      this->m_selectedAIFImageNode = m_Controls.comboAIFImage->GetSelectedNode();
      this->m_selectedAIFImage = dynamic_cast<mitk::Image*>(this->m_selectedAIFImageNode->GetData());
    }
    else
    {
      this->m_selectedAIFImageNode = m_selectedNode;
      this->m_selectedAIFImage = m_selectedImage;
    }

    aifGenerator->SetDynamicImage(this->m_selectedAIFImage);

    aif = aifGenerator->GetAterialInputFunction();
    aifTimeGrid = aifGenerator->GetAterialInputFunctionTimeGrid();
  }
  else
  {
    mitkThrow() << "Cannot generate AIF. View is in a invalide state. No AIF mode selected.";
  }

}

void PETDynamicView::LoadAIFfromFile()
{
  QFileDialog dialog;
  dialog.setNameFilter(tr("Images (*.csv"));

  QString fileName = dialog.getOpenFileName();

  m_Controls.aifFilePath->setText(fileName);

  std::string m_aifFilePath = fileName.toStdString();
  //Read Input
  typedef boost::tokenizer< boost::escaped_list_separator<char> > Tokenizer;
  /////////////////////////////////////////////////////////////////////////////////////////////////
  //AIF Data

  std::ifstream in1(m_aifFilePath.c_str());

  if (!in1.is_open())
  {
    m_Controls.errorMessageLabel->setText("Could not open AIF File!");
  }

  std::vector< std::string > vec1;
  std::string line1;

  while (getline(in1, line1))
  {
    Tokenizer tok(line1);
    vec1.assign(tok.begin(), tok.end());

    //        if (vec1.size() < 3) continue;

    this->AIFinputGrid.push_back(convertToDouble(vec1[0]));
    this->AIFinputFunction.push_back(convertToDouble(vec1[1]));

  }

}


/*=============================================================================
Copyright (C) 2016 Jilles Steeve Dibangoye
==============================================================================*/

#include <sdm/exception.hpp>
#include <sdm/parser/ast.hpp>
#include <sdm/parser/parser.hpp>
#include <sdm/parser/config.hpp>
#include <sdm/parser/printer.hpp>
#include <sdm/parser/encoder.hpp>
#include <sdm/parser/parser_def.hpp>
#include <sdm/parser/ast_adapted.hpp>

#include <sdm/world/two_players_bayesian_game.hpp>
#include <sdm/world/two_players_normal_form_game.hpp>

#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/config/warning_disable.hpp>

#include <regex>
#include <string>
#include <fstream>
#include <iostream>
#include <limits>
#include <algorithm>

namespace sdm
{
  namespace parser
  {

    BOOST_SPIRIT_INSTANTIATE(dpomdp_type, iterator_type, context_type)
    BOOST_SPIRIT_INSTANTIATE(posg_type, iterator_type, context_type)

    // SDM_DECLARE_PARSER(GRAMMAR, TYPE_AST, TYPE_ENCODER, CLASSNAME, PARSER_NAME)
    SDMS_INSTANCIATE_PARSER(sdm::parser::dpomdp_t, sdm::ast::dpomdp_t, sdm::ast::dpomdp_encoder, sdm::MDP, parseMDP)
    SDMS_INSTANCIATE_PARSER(sdm::parser::dpomdp_t, sdm::ast::dpomdp_t, sdm::ast::dpomdp_encoder, sdm::POMDP, parsePOMDP)
    SDMS_INSTANCIATE_PARSER(sdm::parser::dpomdp_t, sdm::ast::dpomdp_t, sdm::ast::dpomdp_encoder, sdm::MPOMDP, parseMPOMDP)
    SDMS_INSTANCIATE_PARSER(sdm::parser::dpomdp_t, sdm::ast::dpomdp_t, sdm::ast::dpomdp_encoder, sdm::DecPOMDP, parseDecPOMDP)
    SDMS_INSTANCIATE_PARSER(sdm::parser::posg_t, sdm::ast::posg_t, sdm::ast::posg_encoder, sdm::POSG, parsePOSG)

    std::shared_ptr<sdm::MPOMDP> parse_file(std::string filename, Config config)
    {
      if (regex_match(filename, std::regex(".*\\.ndpomdp$")) || regex_match(filename, std::regex(".*\\.NDPOMDP$")))
      {
        return std::make_shared<NetworkedDistributedPOMDP>(filename);
      }
      else if (regex_match(filename, std::regex(".*\\.posg$")) || regex_match(filename, std::regex(".*\\.POSG$")))
      {
        auto posg = parsePOSG(filename.c_str());
        posg->configure(config);
        return posg;
      }
      else
      {
        auto dpomdp = parseMPOMDP(filename.c_str());
        dpomdp->configure(config);
        return dpomdp;
      }
    }

    std::shared_ptr<sdm::BayesianGameInterface> parse_file_bayesian(std::string filename)
    {

      auto split = [](const std::string chaine, char delimiteur)
      {
        std::vector<std::string> elements;
        std::stringstream ss(chaine);
        std::string sousChaine;
        while (getline(ss, sousChaine, delimiteur))
        {
          elements.push_back(sousChaine);
        }
        return elements;
      };
      std::shared_ptr<sdm::TwoPlayersBayesianGame> game;
      if (regex_match(filename, std::regex(".*\\.byg$")) || regex_match(filename, std::regex(".*\\.BYG$")))
      {
        game = std::make_shared<sdm::TwoPlayersBayesianGame>(sdm::TwoPlayersBayesianGame());
      }
      else
      {
        return nullptr; // right way ?
      }
      std::ifstream inputFile(filename);
      if (!inputFile)
      {
        throw sdm::exception::FileNotFoundException(std::string(filename));
      }
      std::string line;
      getline(inputFile, line);
      std::vector<std::string> lineElements(split(line, ' '));

      // get problem dimensions
      game->setTypeNumbers(lineElements);

      std::vector<int> typesNumbers = game->getTypesNumbers();

      getline(inputFile, line);
      lineElements = split(line, ' ');
      game->setGameDimensions(lineElements);
      std::vector<int> matrixDimensions = game->getGameDimensions();

      // get payoffMatrix
      for (int i = 0; i < typesNumbers[0] * typesNumbers[1] * matrixDimensions[0] * 2; i++)
      {
        getline(inputFile, line);
        lineElements = split(line, ' ');
        game->addPayoffLine(lineElements);
      }

      // get joint probabilities
      for (int i = 0; i < typesNumbers[0]; i++)
      {
        getline(inputFile, line);
        lineElements = split(line, ' ');
        game->addJointTypeProbabilities(lineElements);
      }

      return game;
    }

    std::shared_ptr<sdm::BayesianGameInterface> parse_file_normal_form(std::string filename)
    {

      auto split = [](const std::string chaine, char delimiteur)
      {
        std::vector<std::string> elements;
        std::stringstream ss(chaine);
        std::string sousChaine;
        while (getline(ss, sousChaine, delimiteur))
        {
          elements.push_back(sousChaine);
        }
        return elements;
      };
      std::shared_ptr<sdm::TwoPlayersNormalFormGame> game;
      if (regex_match(filename, std::regex(".*\\.nfg$")) || regex_match(filename, std::regex(".*\\.NFG$")))
      {
        game = std::make_shared<sdm::TwoPlayersNormalFormGame>(sdm::TwoPlayersNormalFormGame());
      }
      else
      {
        return nullptr; // right way ?
      }
      std::ifstream inputFile(filename);
      if (!inputFile)
      {
        throw sdm::exception::FileNotFoundException(std::string(filename));
      }
      std::string line;
      getline(inputFile, line);
      std::vector<std::string> lineElements(split(line, ' '));

      std::vector<int> typesNumbers = game->getTypesNumbers();
      game->setGameDimensions(lineElements);
      std::vector<int> matrixDimensions = game->getGameDimensions();

      // get payoffMatrix
      for (int i = 0; i < typesNumbers[0] * typesNumbers[1] * matrixDimensions[0] * 2; i++)
      {
        getline(inputFile, line);
        lineElements = split(line, ' ');
        game->addPayoffLine(lineElements);
      }

      return game;
    }

    std::shared_ptr<sdm::BayesianGameInterface> parse_file_bayesian_game(std::string filename)
    {
      if (regex_match(filename, std::regex(".*\\.nfg$")) || regex_match(filename, std::regex(".*\\.NFG$")))
      {
        return parse_file_normal_form(filename);
      }
      else if (regex_match(filename, std::regex(".*\\.byg$")) || regex_match(filename, std::regex(".*\\.BYG$")))
      {
        return parse_file_bayesian(filename);
      }
      else
      {
        throw sdm::exception::Exception("File format not supported by parse_file_bayesian");
      }
    }

  } // namespace parser

  // parser::dpomdp_type const& dpomdp(){
  //   return sdm::parser::dpomdp_t;
  // }

} // namespace sdm


// Demonstration of estimating an RC network using MST
// to accompany "Analyzing On-Chip Interconnect with Modern C++"
// Author: Jeff Trull <edaskel@att.net>

/*
Copyright (c) 2014 Jeffrey E. Trull

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include <iostream>
#include <vector>

#include <boost/iterator/counting_iterator.hpp>
#include <boost/graph/prim_minimum_spanning_tree.hpp>
#include <boost/property_map/function_property_map.hpp>

#include "ckt_graph.h"

// Define an implicit graph class
struct pin_distance_graph {
    // For Prim we must model "Vertex List Graph" and "Incidence Graph"
    // Graph requirements
    typedef size_t vertex_descriptor;
    typedef std::pair<size_t, size_t> edge_descriptor;
    typedef boost::undirected_tag directed_category;
    typedef boost::disallow_parallel_edge_tag edge_parallel_category;
    // inherit traversal from both concepts
    struct traversal_category : virtual public boost::vertex_list_graph_tag,
                                virtual public boost::incidence_graph_tag {};

    // Vertex List Graph requirements
    typedef boost::counting_iterator<size_t> vertex_iterator;
    typedef size_t vertices_size_type;

    // Incidence Graph requirements
    // The most complicated part is generating new edges
    // We need to make a standard conforming edge iterator
    // The Boost.Iterator library can help us make a "forward iterator"
    struct  edge_iterator_t : boost::iterator_facade<edge_iterator_t,
                                                     edge_descriptor,
                                                     boost::forward_traversal_tag,
                                                     edge_descriptor const&> {
        // Implementation constrained by two things:
        // 1) We need to provide a default constructor, so sentinel cannot reference graph
        // 2) We need to provide a non-const dereference operator for Prim, so must keep
        //    a true edge descriptor around

        edge_iterator_t()
            : g_(nullptr) {}  // initialize to invalid
        edge_iterator_t(pin_distance_graph const* g, vertex_descriptor u)
            : current_edge_(std::make_pair(u, 0)), g_(g) {
            advance_to_legal();
        }
    private:
        friend class boost::iterator_core_access;
        // iterator_facade requirements:
        void increment() {
            current_edge_.second++;
            advance_to_legal();
        }
        bool equal(edge_iterator_t const& other) const {
            // true if both invalid, or they match source/dest
            if (invalid()) {
                return other.invalid();
            } else {
                return !other.invalid() && (current_edge_ == other.current_edge_);
            }
        }
        edge_descriptor const& dereference() const {
            return current_edge_;
        }
        // implementation details for above:
        bool invalid() const {
            return ((g_ == nullptr) || (current_edge_.second >= num_vertices(*g_)));
        }
        void advance_to_legal() {
            // ensure target and source are not the same
            if (current_edge_.first == current_edge_.second) {
                current_edge_.second++;
            }
        }
        edge_descriptor current_edge_;
        pin_distance_graph const* g_;
    };

    typedef edge_iterator_t out_edge_iterator;
    typedef size_t degree_size_type;

    // Vertex List Graph free functions
    friend std::pair<vertex_iterator, vertex_iterator>
    vertices(pin_distance_graph const& g) {
        return std::make_pair(vertex_iterator(0), vertex_iterator(g.points_.size()));
    }

    friend vertices_size_type num_vertices(pin_distance_graph const& g) {
        return g.points_.size();
    }

    // Incidence Graph free functions
    friend vertex_descriptor source(edge_descriptor const& e, pin_distance_graph const&) {
        return e.first;
    }

    friend vertex_descriptor target(edge_descriptor const& e, pin_distance_graph const&) {
        return e.second;
    }

    friend std::pair<out_edge_iterator, out_edge_iterator>
    out_edges(vertex_descriptor u, pin_distance_graph const& g) {
        return std::make_pair(edge_iterator_t(&g, u), edge_iterator_t());
    }

    friend degree_size_type
    out_degree(vertex_descriptor, pin_distance_graph const& g) {
        return g.points_.size() - 1;  // "complete" graph, every node connected to every other
    }

    // Main functionality
    typedef std::pair<int, int> point_t;
    template<typename PtIter>
    pin_distance_graph(PtIter beg, PtIter end) : points_(beg, end) {}

    // Provide bracket operator a la adjacency_list
    point_t operator[](vertex_descriptor u) const {
        return points_[u];
    }

private:
    std::vector<point_t> points_;

};

// Run Prim on it
// Prim seems vertex-focused, as opposed to the edge-focused Kruskal
// since our edges are implicit I favored the former

int main() {
    using namespace boost;
    using namespace std;

    // Data for implicit graph - a list of points
    vector<pair<int, int>> pinlocs =
        {{-100, -100}, {-100, 100}, {0, 0}, {100, 100}, {100, -100},
         {-50, 0}, {103, 100}, {100, 90}};

    pin_distance_graph pdg(pinlocs.begin(), pinlocs.end());

    // Create Prim requirements (temporary data structures used in algorithm)

    // vertex index map.  Since we use size_t for vertex descriptors, they
    // are immediately usable as indices:
    auto vindex_map = typed_identity_property_map<size_t>();

    // Predecessor map
    vector<pin_distance_graph::vertex_descriptor> predvec(num_vertices(pdg));   // underlying storage
    auto predpmap = make_iterator_property_map(predvec.begin(), vindex_map);

    // Weight Map
    // Another case where we can be "implicit" - the weight of an edge is
    // the Manhattan distance between the vertices
    typedef pin_distance_graph::edge_descriptor edge_t;
    auto weightpmap = make_function_property_map<edge_t>(
        [&pdg](edge_t e) -> int {
            auto coord1 = pdg[source(e, pdg)];
            auto coord2 = pdg[target(e, pdg)];
            return abs(coord1.first - coord2.first) + abs(coord1.second - coord2.second);
        });

    // call Prim
    prim_minimum_spanning_tree(pdg, predpmap,
                               weight_map(weightpmap).
                               vertex_index_map(vindex_map));

    // produce output as SVG
    cout << "<svg xmlns=\"http://www.w3.org/2000/svg\"" << endl;
    cout << "     xmlns:xlink=\"http://www.w3.org/1999/xlink\">" << endl;
    auto vitpair = vertices(pdg);
    for (auto v : make_iterator_range(vitpair.first, vitpair.second)) {
        // SVG wants positive numbers, and the Y coordinate is canvas style (reversed from Cartesian)
        // so we will transform the data en route to the display for viewability:
        // scale by 2X, mirror Y axis, and add 250 to both
        int x2 = 250 + 2*pdg[v].first;
        int y2 = 250 - 2*pdg[v].second;
        if (predvec[v] == v) {
            // Root.  Make a red circle
            cout << "    <circle cx=\"" << x2 << "\" cy=\"" << y2 << "\" r=\"10\" style=\"fill:#cc0000\"/>" << endl;
        } else {
            // Not Root: a gray one
            cout << "    <circle cx=\"" << x2 << "\" cy=\"" << y2 << "\" r=\"10\" style=\"fill:#cccccc; stroke:#222222\"/>" << endl;
        }
    }
    // Go back and add lines on top of the circles (for visibility)
    for (auto v : make_iterator_range(vitpair.first, vitpair.second)) {
        int x2 = 250 + 2*pdg[v].first;
        int y2 = 250 - 2*pdg[v].second;
        if (predvec[v] != v) {
            int x1 = 250 + 2*pdg[predvec[v]].first;
            int y1 = 250 - 2*pdg[predvec[v]].second;
            // Make a line to the successor node
            cout << "    <line x1=\"" << x1 << "\" y1=\"" << y1 << "\" x2=\"" << x2 << "\" y2=\"" << y2 << "\" style=\"stroke:#666666; stroke-width:3px\"/>" << endl;
        }
    }
    cout << "</svg>" << endl;

    // Future work: turn resulting tree into an implicit graph compatible with ckt_graph_t,
    // with estimated RC values on edges

}


// STL includes
#include <iostream>
#include <fstream>
#include <set>
#include <cstdlib> // error codes
#include <memory>
#include <cmath>
#include <chrono>
#include <random>
// Local includes
#include "models.h"
#include "types.h"
#include "config.h"

#if HAVE_LIBBOOST_PROGRAM_OPTIONS == 1
  // Boost
  #include <boost/program_options.hpp>
  namespace po = boost::program_options;
#endif

std::string model_desc_help_string(std::set<std::string> models,
                                   std::map<std::string, std::string> models_desc,
                                   std::string base_string)
{
  std::string help_string = base_string;
  for (auto m: models)
    help_string += " \"" + m + "\"" + " : " + models_desc.at(m) + "\n";
  return help_string.substr(0, help_string.size() - 1);
}

int main(int argc, char const *argv[])
{
  // models
  std::map<std::string, std::string> models_description;
  std::map<std::string, std::string> models_parameters;
  std::map<std::string, unsigned int> models_parameters_count;
  std::set<std::string> implemented_models = {"delayed", "gn", "bianconi_barabasi", "generalized_gn"};
  // unstructured models
  models_description["delayed"] = "L. H.-Dufresne. et al., PRE 92, (2015).";
  models_parameters["delayed"] = "a, alpha, b, mu, tau.";
  models_parameters_count["delayed"] = 5;
  // structured models
  models_description["gn"] = "Krapivsky-Redner, PRE 63, (2001).";
  models_parameters["gn"] = "gamma, m.";
  models_parameters_count["gn"] = 2;
  models_description["bianconi_barabasi"] = "Bianconie-Barabasi, PRL 86, (2001).";
  models_parameters["bianconi_barabasi"] = "gamma, nu, avg, std, m.";
  models_parameters_count["bianconi_barabasi"] = 5;
  models_description["generalized_gn"] = "General PA with densification.";
  models_parameters["generalized_gn"] = "a, alpha, b, m1, m2, gamma, tau, negative_kernel.";
  models_parameters_count["generalized_gn"] = 8;

  /* ~~~~~ Program options ~~~~~~~*/
  unsigned int T;
  double_vec_t parameters;
  std::string model_name;
  unsigned int seed = 0;
  std::string state_file_path;

  // ================
  // BOOST INTERFACE
  // ================
  #if HAVE_LIBBOOST_PROGRAM_OPTIONS == 1
    // boost::po call
    po::options_description description("Options");
    description.add_options()
    ("model,m", po::value<std::string>(&model_name)->default_value("gn"),
      // automatically generated help message
      model_desc_help_string(implemented_models,
                             models_description,
                             "Name of the growth model. The implemented models are:\n").c_str())
    ("parameters,p", po::value<double_vec_t>(&parameters)->multitoken(),
      // automatically generated help message
      model_desc_help_string(implemented_models,
                             models_parameters,
                             "Parameters of the model, provided as a list of doubles.\n").c_str())
    ("T,t", po::value<unsigned int>(&T)->default_value(1000), "Number of growth events.")
    ("seed,d", po::value<unsigned int>(&seed),
        "Seed of the pseudo random number generator (Mersenne-twister 19937)."\
        "Seeded with current time if seed is not specified or equal to 0.")
    ("state_file,f", po::value<std::string>(&state_file_path),
      "Save state variables in a file upon completion (degree, fitness, etc.).")
    ("verbose,v", "Output parameters to stdlog.")
    ("help,h", "Produce this help message.")
    ;
    po::variables_map var_map;
    try
    {
      po::store(po::parse_command_line(argc,argv,description), var_map);
      po::notify(var_map);
    }
    catch (po::validation_error& e)
    {
      std::clog << "Boost program option error:\n";
      std::clog << e.what();
      std::clog << "\n";
      return EXIT_FAILURE;
    }

    // Input validation and actions
    if (var_map.count("help") > 0 || argc == 1)
    {
      std::clog << "Usage:\n"
                << "  "+std::string(argv[0])+" [--option_1=value] [--option_s2=value] ...\n";
      std::clog << description;
      return EXIT_SUCCESS;
    }
    if (var_map.count("seed") == 0)
    {
      seed = (unsigned int) std::chrono::high_resolution_clock::now().time_since_epoch().count();
    }
    if (models_parameters_count[model_name] != parameters.size())
    {
      std::clog << "Incorrect number of parameters for the \"" + model_name + "\" growth model.\n";
      return EXIT_FAILURE;
    }
    if (implemented_models.find(model_name) == implemented_models.end())
    {
      std::clog << "Model \"" + model_name + "\" not implemented.\n";
      return EXIT_FAILURE;
    }
    if (T <= 1)
    {
      std::clog << "Number of event T is too small (T=" << T << ")\n";
      return EXIT_FAILURE;
    }

    // Logger
    if (var_map.count("verbose") > 0)
    {
      std::clog << "Model: " << model_name << "\n";
      std::clog << "Parameters (" + models_parameters[model_name] + "): ";
      for (auto p: parameters)
        std::clog << p << " ";
      std::clog << "\n";
      std::clog << "T: " << T << "\n";
      std::clog << "Seed: " << seed << "\n";
    }
  // ================
  // end of boost interface
  // ================
  #else
    model_name = std::string(argv[1]);
    if (model_name=="-h")
    {
      std::clog << "This is the limited interface of this program. boost::program_options could not be found and linked.\n";
      std::clog << "Usage: " << argv[0] << "  model_name T seed param1 param2 ...\n\n";
      std::clog << model_desc_help_string(implemented_models,
                                          models_description,
                                          "Name of the growth model. The implemented models are:\n");
      std::clog << "\n\n";
      std::clog << model_desc_help_string(implemented_models,
                                          models_parameters,
                                          "Parameters of the model, provided as a list of doubles.\n");
      std::clog << "\n";
      return EXIT_SUCCESS;
    }
    T = std::atoi(argv[2]);
    seed = std::atoi(argv[3]);
    parameters.resize(models_parameters_count[model_name], 0);
    for (unsigned int i = 0; i < models_parameters_count[model_name]; ++i)
    {
      parameters[i] = std::atof(argv[4 + i]);
    }
  #endif

  /*~~~~~~~Initialize model~~~~~~~~~~~~*/
  std::shared_ptr<growth_model> model;

  // unstructured models
  if (model_name == "delayed")
  {
    double a = parameters[0];
    double alpha = parameters[1];
    double b = parameters[2];
    double gamma = parameters[3];
    double tau = parameters[4];
    model = std::make_shared<delayed_model>(a, alpha, b, gamma, tau);
  }
  // structured models
  if (model_name == "gn")
  {
    double gamma = parameters[0];
    unsigned int m = (unsigned int) parameters[1];
    model = std::make_shared<gn_model>(gamma, m);
  }
  if (model_name == "generalized_gn")
  {
    double a = parameters[0];
    double alpha = parameters[1];
    double b = parameters[2];
    unsigned int m1 = (unsigned int) parameters[3];
    unsigned int m2 = (unsigned int) parameters[4];
    double gamma = parameters[5];
    double tau = parameters[6];
    if (parameters[7]) gamma = -gamma;
    model = std::make_shared<generalized_gn_model>(a, alpha, b, m1, m2, gamma, tau);
    --T; // we start with an edge
  }
  if (model_name == "bianconi_barabasi")
  {
    double gamma = parameters[0];
    double nu = parameters[1];
    double avg = parameters[2];
    double std = parameters[3];
    unsigned int m = (unsigned int) parameters[4];
    model = std::make_shared<bianconi_barabasi_model>(gamma, nu, avg, std, m);
  }

  /*~~~~~~~~~~~Run~~~~~~~~~~~~~~~~*/
  std::mt19937 engine(seed);
  model->run(T, engine);
  // std::clog << "Ran\n";

  /*~~~Dump history to stream~~~~*/
  model->print_history(std::cout);
  #if HAVE_LIBBOOST_PROGRAM_OPTIONS == 1
      if (var_map.count("state_file") > 0)
      {
        std::ofstream sf(state_file_path.c_str(), std::ios::out);
        model->print_states(sf);
        sf.close();
      }
  #endif

  return 0;
}


#pragma once

#include "SM_utils.hpp"
#include "Tableau.hpp"

#include <Eigen/Dense>

#include <queue>
#include <vector>
#include <utility>
#include <type_traits>
#include <cstddef>
#include <memory>
#include <algorithm>
#include <unordered_set>
#include <functional>

namespace AQSystemSolver {

    template<typename Comp>
    using SolidVectorHeapIndexCompare=std::priority_queue<Eigen::Index, std::vector<Eigen::Index>, SM_utils::IndexCompare<Eigen::VectorXd, Comp, Eigen::Index>>;

    struct Solid{
        const Eigen::Index solidIndex;
        const Eigen::RowVectorXd row;
        const double constant;
        Eigen::Index column;

        Solid(Eigen::Index solidIndex_, Eigen::RowVectorXd row_, double constant_, Eigen::Index column_) :
        solidIndex{solidIndex_},
        row{std::move(row_)},
        constant{constant_},
        column{column_}
        {}
    };

    struct SolidCompare {
        using is_transparent=void;
        [[nodiscard]] auto operator()(const auto& l, const auto& r) const
        -> typename std::enable_if_t<SM_utils::is_pointer_fancy_v<decltype(l)> && SM_utils::is_pointer_fancy_v<decltype(r)>, bool>
        {
            return l->solidIndex<r->solidIndex;
        }
        [[nodiscard]] auto operator()(const auto& l, Eigen::Index r) const
        -> typename std::enable_if_t<SM_utils::is_pointer_fancy_v<decltype(l)>, bool>
        {
            return l->solidIndex<r;
        }
        [[nodiscard]] auto operator()(Eigen::Index l, const auto& r) const
        -> typename std::enable_if_t<SM_utils::is_pointer_fancy_v<decltype(r)>, bool>
        {
            return l<r->solidIndex;
        }
        [[nodiscard]] auto operator()(Eigen::Index l, Eigen::Index r) const {
            return l<r;
        }
    };
    template<typename T, typename... Args>
    class SolidIndexIndexer{
    private:
        const SM_utils::flat_set<T, Args...>& set;
    public:
        [[nodiscard]] auto operator[](std::size_t i) const {
            if constexpr(SM_utils::is_pointer_fancy_v<T>){
                return set[i]->solidIndex;
            } else {
                return set[i].solidIndex;
            }
        }
        [[nodiscard]] auto size() const {
            return set.size();
        }
        explicit SolidIndexIndexer(const SM_utils::flat_set<T, Args...>& set_): set{set_} {}
    };
    template<typename T, typename... Args>
    class ColumnIndexer{
    private:
        const SM_utils::flat_set<T, Args...>& set;
    public:
        [[nodiscard]] auto operator[](std::size_t i) const {
            if constexpr(SM_utils::is_pointer_fancy_v<T>){
                return set[i]->column;
            } else {
                return set[i].column;
            }
        }
        [[nodiscard]] auto size() const {
            return set.size();
        }
        explicit ColumnIndexer(const SM_utils::flat_set<T, Args...>& set_): set{set_} {}
    };
    class SolidOwningSet {
    private:
        using set_type=SM_utils::flat_set<std::unique_ptr<Solid>, SolidCompare>;
        using const_iterator=SM_utils::UnowningIterator<set_type::const_iterator>;
        set_type flat_set;
    public:
        [[nodiscard]] auto presenceIndexOf(const Solid* solid) const {
            return std::distance(flat_set.begin(), flat_set.find(solid));
        }
        [[nodiscard]] auto lower_bound_index(const Solid* solid) const {
            return std::distance(flat_set.begin(), std::lower_bound(flat_set.begin(), flat_set.end(), solid, SolidCompare()));
        }
        [[nodiscard]] const_iterator find(const auto& t) const {
            return flat_set.find(t);
        }
        [[nodiscard]] typename set_type::iterator find_extract(const auto& t) {
            return flat_set.find(t);
        }
        void insert(std::unique_ptr<Solid>&& in){
            flat_set.insert(std::move(in));
        }
        template<typename... Args>
        void emplace(Args&&... args){
            flat_set.emplace(std::forward<Args>(args)...);
        }
        void erase(set_type::iterator it){
            flat_set.erase(it);
        }
        [[nodiscard]] auto indexBySolidIndexes() const {
            return SolidIndexIndexer{flat_set};
        }
        [[nodiscard]] auto indexByColumn() const {
            return ColumnIndexer{flat_set};
        }
        [[nodiscard]] Solid* get(Eigen::Index presenceIndex) {
            return flat_set[presenceIndex].get();
        }
        [[nodiscard]] const Solid* get(Eigen::Index presenceIndex) const {
            return flat_set[presenceIndex].get();
        }
        [[nodiscard]] auto size() const {
            return flat_set.size();
        }
        void reserve(std::size_t s){
            flat_set.reserve(s);
        }
        [[nodiscard]] auto begin() const {
            return const_iterator{flat_set.begin()};
        }
        [[nodiscard]] auto end() const {
            return const_iterator{flat_set.end()};
        }
    };

    class SolidSystem{
    private:
        Eigen::Index numPresent;
        Tableau<> tableau;
        SolidOwningSet solidsPresent;
        SolidOwningSet solidsNotPresent;

        std::unordered_set<std::size_t> combinationsHash;
        std::size_t currentCombinationHash;

        template<bool adding>
        void addOrRemove(Solid* solidBeingChanged){
            SolidOwningSet& addingToContainer=adding ? solidsPresent : solidsNotPresent;
            SolidOwningSet& removingFromContainer=!adding ? solidsPresent : solidsNotPresent;

            auto extractedSolidIterator=removingFromContainer.find_extract(solidBeingChanged);
            addingToContainer.insert(std::move(*extractedSolidIterator));
            removingFromContainer.erase(extractedSolidIterator);
            if(adding){
                ++numPresent;
            } else {
                --numPresent;
            }
        }

        void add(Solid* solidBeingChanged){
            addOrRemove<true>(solidBeingChanged);
        }
        void remove(Solid* solidBeingChanged){
            addOrRemove<false>(solidBeingChanged);
        }

        [[nodiscard]] auto conditionallyAddHash(const Solid* solid){
            const std::size_t newHash=currentCombinationHash^(std::size_t(1)<<std::size_t(solid->solidIndex));
            if(combinationsHash.insert(newHash).second){
                currentCombinationHash=newHash;
                return true;
            }
            return false;
        }

        [[nodiscard]] auto conditionallyAdd(Solid* solid){
            if(conditionallyAddHash(solid)){
                add(solid);
                return true;
            }
            return false;
        }
        [[nodiscard]] auto conditionallyRemove(Solid* solid){
            if(conditionallyAddHash(solid)){
                remove(solid);
                return true;
            }
            return false;
        }
    public:
        [[nodiscard]] auto size() const {
            return tableau.rows();
        }
        [[nodiscard]] auto cols() const {
            return tableau.cols();
        }
        [[nodiscard]] const auto& getSolidsPresent() const {
            return solidsPresent;
        }
        [[nodiscard]] const auto& getSolidsNotPresent() const {
            return solidsNotPresent;
        }
        [[nodiscard]] const auto& getTableau() const& {
            return tableau;
        }
        [[nodiscard]] auto getTableau() && {
            return std::move(tableau);
        }

        [[nodiscard]] Eigen::VectorXd calculateSolidAmts(const Eigen::RowVectorXd& leftOvers) const {
            Eigen::MatrixXd solidAmtEqns(solidsPresent.size(), numPresent);
            for(const Solid* solid : solidsPresent){
                solidAmtEqns.col(solidsPresent.presenceIndexOf(solid))=solid->row(solidsPresent.indexByColumn()).transpose();
            }
            const Eigen::VectorXd solidAmtLeftOver=leftOvers.transpose()(solidsPresent.indexByColumn());
            return solidAmtEqns.partialPivLu().solve(solidAmtLeftOver);
        }
        //auto in order to avoid circular dependency
        [[nodiscard]] Eigen::VectorXd calculateSolubilityProducts(const Eigen::RowVectorXd& currentSolution, const auto& replacementDict){
            auto replacedTableauNotPresent=replacementDict.createReplacedTableau(tableau.reducedCopy(solidsNotPresent.indexBySolidIndexes(), Eigen::all));
            if(replacedTableauNotPresent.cols()){
                return replacedTableauNotPresent.evalTerms(currentSolution);
            }
            return replacedTableauNotPresent.getConstants();
        }

        struct SolidChangeAttempt {
            bool success;
            Solid* solid;
        };

        [[nodiscard]] auto getSolidToRemove(const Eigen::VectorXd& solidAmts){
            Solid* solidNeedsToDisolve=nullptr;
            //we use a heap cause we usually wont need the full sort
            for(
                auto indexHeap=SolidVectorHeapIndexCompare<std::greater<>>(SM_utils::CountingIterator(0), SM_utils::CountingIterator(solidAmts.rows()), SM_utils::IndexCompare<Eigen::VectorXd, std::greater<>, Eigen::Index>{solidAmts});
                Eigen::Index iThSolidPresent : SM_utils::ConsumingRange(indexHeap)
            ) {
                if(solidAmts.coeff(iThSolidPresent)<0.0) [[unlikely]] {
                    //not const because if we remove the solid then we're changing it
                    Solid* solid=solidsPresent.get(iThSolidPresent);
                    if(conditionallyRemove(solid)) [[likely]] {
                        return SolidChangeAttempt{true, solid};
                    }
                    if(solidNeedsToDisolve!=nullptr){
                        //std::cout<<"WARNING: EITHER NEARLY LOOPED OR GIBBS RULE FAILED (remove)"<<std::endl;
                        solidNeedsToDisolve=solid;
                    }
                } else {
                    return SolidChangeAttempt{false, solidNeedsToDisolve};
                }
            }
            return SolidChangeAttempt{false, solidNeedsToDisolve};
        }
        [[nodiscard]] auto getSolidToAdd(const Eigen::VectorXd& solubilityProducts){
            Solid* solidNeedsToForm=nullptr;

            //sort the solids in the wrong order so that we are much more likely to trigger a removal
            auto indexHeap=[&]() {
                if constexpr (SM_utils::debug) {
                    return SolidVectorHeapIndexCompare<std::greater<>>(SM_utils::CountingIterator(0), SM_utils::CountingIterator(solubilityProducts.rows()), SM_utils::IndexCompare<Eigen::VectorXd, std::greater<>, Eigen::Index>{solubilityProducts});
                } else {
                    return SolidVectorHeapIndexCompare<std::less<>>(SM_utils::CountingIterator(0), SM_utils::CountingIterator(solubilityProducts.rows()), SM_utils::IndexCompare<Eigen::VectorXd, std::less<>, Eigen::Index>{solubilityProducts});
                }
            }();
            for(Eigen::Index iThSolidNotPresent : SM_utils::ConsumingRange(indexHeap)) {
                if(solubilityProducts.coeff(iThSolidNotPresent)>1.0) [[likely]] { //the long running cases will have lots of solids being added
                    Solid* solid=solidsNotPresent.get(iThSolidNotPresent);
                    if(conditionallyAdd(solid)) [[likely]] {
                        return SolidChangeAttempt{true, solid};
                    }
                    if(solidNeedsToForm!=nullptr){
                        //std::cout<<"WARNING: EITHER NEARLY LOOPED OR GIBBS RULE FAILED (add)"<<std::endl;
                        solidNeedsToForm=solid;
                    }
                } else {
                    if constexpr(!SM_utils::debug) {
                        //short circuit if we're going in the right order
                        return SolidChangeAttempt{false, solidNeedsToForm};
                    }
                }
            }
            return SolidChangeAttempt{false, solidNeedsToForm};
        }

        template<typename T=Tableau<>, typename SetType=std::unordered_set<Eigen::Index>>
        SolidSystem(T&& tableau_, const SetType& starting_solids):
            numPresent{0},
            tableau{std::forward<T>(tableau_)}
        {
            solidsPresent.reserve(size());
            solidsNotPresent.reserve(size());
            for(Eigen::Index i=0; i<tableau.rows(); ++i){
                const bool starting=starting_solids.find(i)!=starting_solids.end();
                const std::size_t hash=std::size_t(1)<<i;
                if(starting){
                    ++numPresent;
                    currentCombinationHash^=hash;
                    solidsPresent.emplace(std::unique_ptr<Solid>(new Solid{i, tableau.getCoefficients().row(i), tableau.getConstant(i), -1}));
                } else {
                    solidsNotPresent.emplace(std::unique_ptr<Solid>(new Solid{i, tableau.getCoefficients().row(i), tableau.getConstant(i), -1}));
                }
            }
            combinationsHash.insert(currentCombinationHash);
        }
    private:
        template<typename T=Tableau<>>
        SolidSystem(T&& tableau_, std::size_t currentCombinationHash_, SolidOwningSet&& solidsPresent_, SolidOwningSet&& solidsNotPresent_) :
            numPresent{static_cast<Eigen::Index>(solidsPresent_.size())},
            tableau{std::forward<T>(tableau_)},
            solidsPresent{std::move(solidsPresent_)},
            solidsNotPresent{std::move(solidsNotPresent_)},
            combinationsHash{currentCombinationHash_},
            currentCombinationHash{currentCombinationHash_}
        { }
    public:
        [[nodiscard]] auto createNewWithInitialConditions() const {
            SolidOwningSet solidsPresent_;
            for(Solid * solidPresent : solidsPresent){
                solidsPresent_.emplace(std::make_unique<Solid>(solidPresent->solidIndex, solidPresent->row, solidPresent->constant, -1));
            }
            SolidOwningSet solidsNotPresent_;
            for(Solid * solidNotPresent : solidsNotPresent){
                solidsNotPresent_.emplace(std::make_unique<Solid>(solidNotPresent->solidIndex, solidNotPresent->row, solidNotPresent->constant, -1));
            }
            return SolidSystem(tableau, currentCombinationHash, std::move(solidsPresent_), std::move(solidsNotPresent_));
        }
    };
} // namespace AQSystemSolver