#include "conex/approximate_eigenvalues.h"

#include <chrono>

#include "gtest/gtest.h"

#include "conex/debug_macros.h"
#include "conex/test/test_util.h"
#include <Eigen/Dense>

namespace conex {

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

GTEST_TEST(Eigenvalues, NonsymmetricFromJacobiIterations) {
  int n = 4;
  MatrixXd A(n, n);
  A << 3, 1, 0, 1, 1, 3, 1, 0, 0, 1, 4, 1, 1, 0, 1, 5;
  A = A / A.trace();

  MatrixXd W = MatrixXd::Random(n, n);
  W = W * W.transpose();
  A = W * A;

  VectorXd r0(n);
  r0 << 1, 2, 0, 4;
  VectorXd eigJ = ApproximateEigenvalues(A, W, r0, n, false /*no compressed*/);
  std::sort(eigJ.data(), eigJ.data() + eigJ.rows());

  VectorXd eigL = ApproximateEigenvalues(A, W, r0, n, true /*use compressed*/);
  std::sort(eigL.data(), eigL.data() + eigL.rows());
  for (int i = 0; i < n; i++) {
    EXPECT_NEAR(eigL(i), eigJ(i), 1e-12);
  }

  auto eigA = eig(A).eigenvalues;
  std::sort(eigA.data(), eigA.data() + n);
  for (int i = 0; i < n; i++) {
    EXPECT_NEAR(eigJ(i), eigA(i), 1e-12);
  }
}

GTEST_TEST(Eigenvalues, TruncatedApproximiationInterlaces) {
  int n = 4;
  MatrixXd A(n, n);
  A << .1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5;

  VectorXd r0(n);
  r0 << 1, 2, 0, 4;

  int n_approx = 2;
  VectorXd eigJ =
      ApproximateEigenvalues(A, MatrixXd::Identity(n, n), r0, n_approx, true);
  VectorXd eigA = eig(A).eigenvalues;

  std::sort(eigJ.data(), eigJ.data() + eigJ.rows());
  std::sort(eigA.data(), eigA.data() + eigA.rows());
  EXPECT_TRUE(eigJ.tail(1)(0) <= eigA.tail(1)(0));
  EXPECT_TRUE(eigJ.head(1)(0) >= eigA.head(1)(0));
}

GTEST_TEST(Eigenvalues, Lanczos) {
  int n = 4;
  MatrixXd A(n, n);
  A << 3, 1, 0, 1, 1, 3, 1, 0, 0, 1, 4, 1, 1, 0, 1, 5;
  A = A / A.trace();

  VectorXd r0(n);
  r0 << 1, 2, 0, 4;
  VectorXd eigJ =
      ApproximateEigenvalues(A, MatrixXd::Identity(n, n), r0, n, true);
  auto eigA = eig(A).eigenvalues;

  auto eigL = ApproximateEigenvalues(A, r0, n);

  std::sort(eigJ.data(), eigJ.data() + n);
  std::sort(eigA.data(), eigA.data() + n);
  for (int i = 0; i < n; i++) {
    EXPECT_NEAR(eigJ(i), eigA(i), 1e-12);
  }
  for (int i = 0; i < n; i++) {
    EXPECT_NEAR(eigL(i), eigA(i), 1e-12);
  }
}

GTEST_TEST(Eigenvalues, Profile) {
  for (int k = 0; k < 4; k++) {
    int n = 25;
    MatrixXd S = MatrixXd::Random(n, n);
    MatrixXd St = S.transpose();
    S = S + St;
    MatrixXd W = MatrixXd::Random(n, n);
    W = W * W.transpose();
    MatrixXd WS = W * S;
    VectorXd r0 = VectorXd::Random(n);

    auto t1 = std::chrono::high_resolution_clock::now();
    VectorXd eigJ = ApproximateEigenvalues(WS, W, r0, n / 2, true);
    auto t2 = std::chrono::high_resolution_clock::now();
    auto duration1 =
        std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count();

    t1 = std::chrono::high_resolution_clock::now();
    VectorXd eigWS = eig(WS).eigenvalues;
    t2 = std::chrono::high_resolution_clock::now();
    auto duration2 =
        std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count();

    EXPECT_LE(duration1, duration2);
    EXPECT_NEAR(eigWS.maxCoeff() / eigJ.maxCoeff(), 1, 1e-2);
  }
}

}  // namespace conex


// Copyright 2004 The Trustees of Indiana University.

// Use, modification and distribution is subject to 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)

//  Authors: Douglas Gregor
//           Andrew Lumsdaine
#include <boost/graph/fruchterman_reingold.hpp>
#include <boost/graph/random_layout.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/simple_point.hpp>
#include <boost/lexical_cast.hpp>
#include <string>
#include <iostream>
#include <map>
#include <vector>
#include <boost/random/linear_congruential.hpp>
#include <boost/progress.hpp>
#include <boost/shared_ptr.hpp>

using namespace boost;

void usage()
{
  std::cerr << "Usage: fr_layout [options] <width> <height>\n"
            << "Arguments:\n"
            << "\t<width>\tWidth of the display area (floating point)\n"
            << "\t<Height>\tHeight of the display area (floating point)\n\n"
            << "Options:\n"
            << "\t--iterations n\tNumber of iterations to execute.\n"
            << "\t\t\tThe default value is 100.\n"
            << "Input:\n"
            << "  Input is read from standard input as a list of edges, one per line.\n"
            << "  Each edge contains two string labels (the endpoints) separated by a space.\n\n"
            << "Output:\n"
            << "  Vertices and their positions are written to standard output with the label,\n  x-position, and y-position of a vertex on each line, separated by spaces.\n";
}

typedef adjacency_list<listS, vecS, undirectedS,
                       property<vertex_name_t, std::string> > Graph;

typedef graph_traits<Graph>::vertex_descriptor Vertex;

typedef std::map<std::string, Vertex> NameToVertex;

Vertex get_vertex(const std::string& name, Graph& g, NameToVertex& names)
{
  NameToVertex::iterator i = names.find(name);
  if (i == names.end())
    i = names.insert(std::make_pair(name, add_vertex(name, g))).first;
  return i->second;
}

class progress_cooling : public linear_cooling<double>
{
  typedef linear_cooling<double> inherited;

 public:
  explicit progress_cooling(std::size_t iterations) : inherited(iterations)
  {
    display.reset(new progress_display(iterations + 1, std::cerr));
  }

  double operator()()
  {
    ++(*display);
    return inherited::operator()();
  }

 private:
  shared_ptr<boost::progress_display> display;
};

int main(int argc, char* argv[])
{
  int iterations = 100;

  if (argc < 3) { usage(); return -1; }

  double width = 0;
  double height = 0;

  for (int arg_idx = 1; arg_idx < argc; ++arg_idx) {
    std::string arg = argv[arg_idx];
    if (arg == "--iterations") {
      ++arg_idx;
      if (arg_idx >= argc) { usage(); return -1; }
      iterations = lexical_cast<int>(argv[arg_idx]);
    } else {
      if (width == 0.0) width = lexical_cast<double>(arg);
      else if (height == 0.0) height = lexical_cast<double>(arg);
      else {
        usage();
        return -1;
      }
    }
  }

  if (width == 0.0 || height == 0.0) {
    usage();
    return -1;
  }

  Graph g;
  NameToVertex names;

  std::string source, target;
  while (std::cin >> source >> target) {
    add_edge(get_vertex(source, g, names), get_vertex(target, g, names), g);
  }

  typedef std::vector<simple_point<double> > PositionVec;
  PositionVec position_vec(num_vertices(g));
  typedef iterator_property_map<PositionVec::iterator,
                                property_map<Graph, vertex_index_t>::type>
    PositionMap;
  PositionMap position(position_vec.begin(), get(vertex_index, g));

  minstd_rand gen;
  random_graph_layout(g, position, -width/2, width/2, -height/2, height/2, gen);
  fruchterman_reingold_force_directed_layout
    (g, position, width, height,
     cooling(progress_cooling(iterations)));

  graph_traits<Graph>::vertex_iterator vi, vi_end;
  for (tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) {
    std::cout << get(vertex_name, g, *vi) << '\t'
              << position[*vi].x << '\t' << position[*vi].y << std::endl;
  }
  return 0;
}


///////////////////////////////////////////////////////////////////////////////
//  Copyright Paul Bristow 2015.
//  Copyright Christopher Kormanyos 2015.
//  Copyright Nikhar Agrawal 2015.
//  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)
//

//! \file
//!\brief Tests multiplication for fixed_point negatable.

#define BOOST_TEST_MODULE test_negatable_basic_multiply
#define BOOST_LIB_DIAGNOSTIC

#include <iomanip>
#include <iostream>
#include <sstream>

#include <boost/fixed_point/fixed_point.hpp>
#include <boost/test/included/unit_test.hpp>

BOOST_AUTO_TEST_CASE(test_negatable_basic_multiply)
{
  //! small range and resolution
  {
    typedef boost::fixed_point::negatable<4, -2, boost::fixed_point::round::fastest> fixed_point_type_fastest_round;
    fixed_point_type_fastest_round x = fixed_point_type_fastest_round (-1.25) * fixed_point_type_fastest_round (1.5);
    fixed_point_type_fastest_round y = fixed_point_type_fastest_round(-2);
    fixed_point_type_fastest_round z = fixed_point_type_fastest_round(-1.75);

    BOOST_CHECK_EQUAL(x == y || x == z, true);

    x = fixed_point_type_fastest_round (1.25) * fixed_point_type_fastest_round (1.5);
    y = fixed_point_type_fastest_round (1.75);
    z = fixed_point_type_fastest_round (2);

    BOOST_CHECK_EQUAL(x == y || x == z, true);
  }

  //! larger range and small resolution
  {
    typedef boost::fixed_point::negatable<400, -2, boost::fixed_point::round::fastest> fixed_point_type_fastest_round;
    fixed_point_type_fastest_round x = fixed_point_type_fastest_round (-1.25) * fixed_point_type_fastest_round (1.5);
    fixed_point_type_fastest_round y = fixed_point_type_fastest_round(-2);
    fixed_point_type_fastest_round z = fixed_point_type_fastest_round(-1.75);

    BOOST_CHECK_EQUAL(x == y || x == z, true);

    x = fixed_point_type_fastest_round (1.25) * fixed_point_type_fastest_round (1.5);
    y = fixed_point_type_fastest_round (1.75);
    z = fixed_point_type_fastest_round (2);

    BOOST_CHECK_EQUAL(x == y || x == z, true);
  }

  //! larger range and larger resolution
  {
    typedef boost::fixed_point::negatable<87, -4, boost::fixed_point::round::fastest> fixed_point_type_fastest_round;
    fixed_point_type_fastest_round x = fixed_point_type_fastest_round (800.4375) * fixed_point_type_fastest_round (-7.5625);
    fixed_point_type_fastest_round y = fixed_point_type_fastest_round(-6053.3125);
    fixed_point_type_fastest_round z = fixed_point_type_fastest_round(-6053.25);

    BOOST_CHECK_EQUAL(x == y || x == z, true);

    x = fixed_point_type_fastest_round (800.4375) * fixed_point_type_fastest_round (7.5625);
    y = fixed_point_type_fastest_round (6053.3125);
    z = fixed_point_type_fastest_round (6053.25);

    BOOST_CHECK_EQUAL(x == y || x == z, true);
  }
}


// The contents of this file are in the public domain. See LICENSE_FOR_EXAMPLE_PROGRAMS.txt
/*

    This is an example showing how to use sparse feature vectors with
    the dlib C++ library's machine learning tools.

    This example creates a simple binary classification problem and shows
    you how to train a support vector machine on that data.

    The data used in this example will be 100 dimensional data and will
    come from a simple linearly separable distribution.
*/

#include <iostream>
#include <ctime>
#include <vector>
#include <dlib/svm.h>

using namespace std;
using namespace dlib;

int main()
{
    // In this example program we will be dealing with feature vectors that are sparse (i.e. most
    // of the values in each vector are zero).  So rather than using a dlib::matrix we can use
    // one of the containers from the STL to represent our sample vectors.  In particular, we
    // can use the std::map to represent sparse vectors.  (Note that you don't have to use std::map.
    // Any STL container of std::pair objects that is sorted can be used.  So for example, you could
    // use a std::vector<std::pair<unsigned long,double> > here so long as you took care to sort every vector)
    typedef std::map<unsigned long,double> sample_type;

    // This is a typedef for the type of kernel we are going to use in this example.
    // Since our data is linearly separable I picked the linear kernel.  Note that if you
    // are using a sparse vector representation like std::map then you have to use a kernel
    // meant to be used with that kind of data type.
    typedef sparse_linear_kernel<sample_type> kernel_type;

    // Here we create an instance of the pegasos svm trainer object we will be using.
    svm_pegasos<kernel_type> trainer;
    // Here we setup a parameter to this object.  See the dlib documentation for a
    // description of what this parameter does.
    trainer.set_lambda(0.00001);

    // Lets also use the svm trainer specially optimized for the linear_kernel and
    // sparse_linear_kernel.
    svm_c_linear_trainer<kernel_type> linear_trainer;
    // This trainer solves the "C" formulation of the SVM.  See the documentation for
    // details.
    linear_trainer.set_c(10);

    std::vector<sample_type> samples;
    std::vector<double> labels;

    // make an instance of a sample vector so we can use it below
    sample_type sample;

    // Now lets go into a loop and randomly generate 10000 samples.
    srand(time(0));
    double label = +1;
    for (int i = 0; i < 10000; ++i)
    {
        // flip this flag
        label *= -1;

        sample.clear();

        // now make a random sparse sample with at most 10 non-zero elements
        for (int j = 0; j < 10; ++j)
        {
            int idx = std::rand()%100;
            double value = static_cast<double>(std::rand())/RAND_MAX;

            sample[idx] = label*value;
        }

        // let the svm_pegasos learn about this sample.
        trainer.train(sample,label);

        // Also save the samples we are generating so we can let the svm_c_linear_trainer
        // learn from them below.
        samples.push_back(sample);
        labels.push_back(label);
    }

    // In addition to the rule we learned with the pegasos trainer lets also use our linear_trainer
    // to learn a decision rule.
    decision_function<kernel_type> df = linear_trainer.train(samples, labels);

    // Now we have trained our SVMs.  Lets test them out a bit.
    // Each of these statements prints the output of the SVMs given a particular sample.
    // Each SVM outputs a number > 0 if a sample is predicted to be in the +1 class and < 0
    // if a sample is predicted to be in the -1 class.

    sample.clear();
    sample[4] = 0.3;
    sample[10] = 0.9;
    cout << "This is a +1 example, its SVM output is: " << trainer(sample) << endl;
    cout << "df: " << df(sample) << endl;

    sample.clear();
    sample[83] = -0.3;
    sample[26] = -0.9;
    sample[58] = -0.7;
    cout << "This is a -1 example, its SVM output is: " << trainer(sample) << endl;
    cout << "df: " << df(sample) << endl;

    sample.clear();
    sample[0] = -0.2;
    sample[9] = -0.8;
    cout << "This is a -1 example, its SVM output is: " << trainer(sample) << endl;
    cout << "df: " << df(sample) << endl;

}


/* test_uniform_on_sphere_distribution.cpp
 *
 * Copyright Steven Watanabe 2011
 * 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)
 *
 * $Id: test_uniform_on_sphere_distribution.cpp 71018 2011-04-05 21:27:52Z steven_watanabe $
 *
 */

#include <boost/random/uniform_on_sphere.hpp>
#include <boost/assign/list_of.hpp>

#include <limits>

#define BOOST_RANDOM_DISTRIBUTION boost::random::uniform_on_sphere<>
#define BOOST_RANDOM_ARG1 dim
#define BOOST_RANDOM_ARG1_DEFAULT 2
#define BOOST_RANDOM_ARG1_VALUE 3

std::vector<double> min0 = boost::assign::list_of(-1.0)(0.0);
std::vector<double> max0 = boost::assign::list_of(1.0)(0.0);
std::vector<double> min1 = boost::assign::list_of(-1.0)(0.0)(0.0);
std::vector<double> max1 = boost::assign::list_of(1.0)(0.0)(0.0);

#define BOOST_RANDOM_DIST0_MIN min0
#define BOOST_RANDOM_DIST0_MAX max0
#define BOOST_RANDOM_DIST1_MIN min1
#define BOOST_RANDOM_DIST1_MAX max1

#define BOOST_RANDOM_TEST1_PARAMS (0)
#define BOOST_RANDOM_TEST1_MIN std::vector<double>()
#define BOOST_RANDOM_TEST1_MAX std::vector<double>()
#define BOOST_RANDOM_TEST2_PARAMS
#define BOOST_RANDOM_TEST2_MIN min0
#define BOOST_RANDOM_TEST2_MAX max0

#include <boost/test/test_tools.hpp>

BOOST_TEST_DONT_PRINT_LOG_VALUE( std::vector<double> )

#include "test_distribution.ipp"


#include "testsuite.h"
#include <blitz/vector2.h>
#include <blitz/array.h>

BZ_USING_NAMESPACE(blitz)

int main()
{
    Vector<int> a(6);
    a = 1, 4, 6, 3, 7, 8;

    BZTEST(a(0) == 1);
    BZTEST(a(1) == 4);
    BZTEST(a(2) == 6);
    BZTEST(a(3) == 3);
    BZTEST(a(4) == 7);
    BZTEST(a(5) == 8);
    cout << a << endl;

    Vector<int> b = a(Range(0,4,2));
    cout << b << endl;
//  b = -1, -2, -3;
//  workaround: use list initialization with Vector that has own storage
    Vector<int> bb(3);
    bb = -1, -2, -3;
    b = bb;
    cout << a << endl;
    cout << b << endl;
    BZTEST(a(0) == -1);
    BZTEST(a(1) == 4);
    BZTEST(a(2) == -2);
    BZTEST(a(3) == 3);
    BZTEST(a(4) == -3);
    BZTEST(a(5) == 8);

    Vector<int> c = a.reverse(firstDim);
    c = 0;
    BZTEST(a(0) == 0);
    BZTEST(a(1) == 0);
    BZTEST(a(2) == 0);
    BZTEST(a(3) == 0);
    BZTEST(a(4) == 0);
    BZTEST(a(5) == 0);
//  c = 8, 7, 3, 6, 4, 1;
//  workaround: use list initialization with Vector that has own storage
    Vector<int> cc(6);
    cc = 8, 7, 3, 6, 4, 1;
    c = cc;
    BZTEST(a(0) == 1);
    BZTEST(a(1) == 4);
    BZTEST(a(2) == 6);
    BZTEST(a(3) == 3);
    BZTEST(a(4) == 7);
    BZTEST(a(5) == 8);

    Array<int,1> d(5);
    d=1,-1,2,-2,0;
    BZTEST(d(0) == 1);
    BZTEST(d(1) == -1);
    BZTEST(d(2) == 2);
    BZTEST(d(3) == -2);
    BZTEST(d(4) == 0);

    return 0;
}


/*
 * Copyright (c) 2019 Opticks Team. All Rights Reserved.
 *
 * This file is part of Opticks
 * (see https://bitbucket.org/simoncblyth/opticks).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <iostream>
#include <fstream>
#include <ctime>            // std::time

#include <boost/random/mersenne_twister.hpp>
#include <boost/random/linear_congruential.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/random/uniform_real.hpp>
#include <boost/random/variate_generator.hpp>
#include <boost/generator_iterator.hpp>

#include "PLOG.hh"

//typedef boost::mt19937     RNG_t;
//typedef boost::ecuyer1988  RNG_t;
typedef boost::minstd_rand      RNG_t;

typedef boost::uniform_real<>   DST_t;
typedef boost::variate_generator< RNG_t, DST_t > GEN_t ;

class BRNG
{
    public:
        BRNG( float lo=0.f, float hi=1.f, unsigned _seed=0)
            :
            m_rng(),
            m_dst(lo, hi),
            m_gen(m_rng, m_dst)
        {
            seed(_seed);
        }

        float operator()()
        {
            return m_gen() ;
        }

        void seed(unsigned _seed)
        {
            m_rng.seed(_seed);
        }

        void dump( const char* label )
        {
            LOG(info) << label ;
            std::cout.setf(std::ios::fixed);
            for(int i = 0; i < 10; i++) std::cout << (*this)() << '\n';
        }

    private:
        RNG_t m_rng ;
        DST_t m_dst ;
        GEN_t m_gen ;
};

void test_0()
{
    LOG(info) << "." ;

    {
        RNG_t rng(42);
        DST_t dst(0,1);
        GEN_t uni(rng, dst);

        std::cout << "A : 10 samples in [0..1) (seed:42) \n";
        std::cout.setf(std::ios::fixed);
        for(int i = 0; i < 10; i++) std::cout << uni() << '\n';

        rng.seed(420);
        std::cout << "A : 10 samples in [0..1) (seed:420) \n";
        for(int i = 0; i < 10; i++) std::cout << uni() << '\n';
    }

    {

        RNG_t rng(42);
        DST_t dst(0,1);
        GEN_t uni(rng, dst);

        std::cout << "B : 10 samples of a uniform distribution in [0..1):\n";
        std::cout.setf(std::ios::fixed);
        for(int i = 0; i < 10; i++) std::cout << uni() << '\n';

        rng.seed(420);
        std::cout << "B : 10 more samples of a uniform distribution in [0..1):\n";
        for(int i = 0; i < 10; i++) std::cout << uni() << '\n';
    }

}

void test_1()
{
    LOG(info) << "." ;
    {
        BRNG a(0,1,42);
        a.dump("A : 10 samp, seed:42 ");
        a.seed(420);
        a.dump("A : 10 samp, seed 420 ");
    }
    {
        BRNG b(0,1,42);
        b.dump("B : 10 samp, seed:42 ");
        b.seed(420);
        b.dump("B : 10 samp, seed 420 ");
    }

}

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

    test_0();
    test_1();

    return 0;
}


#ifdef BOOST

#include <iostream>
#include <optional>

// Using boost-1.65.1
#include <boost/config.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/connected_components.hpp>
#include <boost/tokenizer.hpp>

using namespace std;

using undirected_graph =
    boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS>;

int
main()
{
	undirected_graph G;

	for (string line; getline(cin, line); ) {
		optional<int> last;
		for (string s : boost::tokenizer<>(line)) {
			int i = stoi(s);
			if (last)
				add_edge(*last, i, G);
			last = i;
		}
	}

	vector<int> component(num_vertices(G));
	int n = connected_components(G, &component[0]);
	cout << count(begin(component), end(component), component[0]) << endl;
	cout << n << endl;
}

// 134
// 193

#else //////////////////////////////////////////////////////////////////////

#include <algorithm>
#include <deque>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

using namespace std;

// bfs approach without recursion
// adapted from https://www.reddit.com/r/adventofcode/comments/7j89tr/x/dr4iya8/

int main()
{
	vector<vector<int>> edges;

	for (string line; getline(cin, line); ) {
		istringstream line_stream{line};
		int vertex_a, vertex_b;
		string _;
		line_stream >> vertex_a >> _;
		for (edges.emplace_back();  // assumes lines arrive in order
		     line_stream >> vertex_b;
		     line_stream >> _)
			edges[vertex_a].emplace_back(vertex_b);
	}

	vector<int> comp(size(edges), -1);
	int n = 0;

	for (auto vertex = begin(comp);
	     vertex != end(comp);
	     ++n, vertex = find(begin(comp), end(comp), -1)) {
		deque<long> queue{distance(begin(comp), vertex)};
		while (!empty(queue)) {
			int v = queue.front();
			queue.pop_front();
			comp[v] = n;
			copy_if(begin(edges[v]), end(edges[v]),
			    back_inserter(queue),
			    [&](int v){ return comp[v] == -1; });
		}
	}

	cout << count(begin(comp), end(comp), 0) << endl;
	cout << n << endl;
}

#endif


#include <boost/lexical_cast.hpp>
#include <cctype>
#include <iostream>
#include <iterator>
#include <ostream>
#include <sstream>
#include <stdexcept>
#include <vector>
#include <memory>

struct Token
{
	enum class Type { Literal, Plus, Minus, LParen, RParen, } type;
	std::string text;

	explicit Token(Type type, std::string text)
		:	type{std::move(type)},
			text{std::move(text)}
	{}

	friend std::ostream& operator<<(std::ostream& out, const Token& t)
	{
		return out << "`" << t.text << "`";
	}
};

std::vector<Token> lex(std::string input)
{
	std::vector<Token> result;
	for(auto i = 0ull; i < std::size(input); ++i)
	{
		if (input[i] == '+')
		{
			result.emplace_back(Token::Type::Plus, std::string(1, input[i]));
		}
		else if (input[i] == '-')
		{
			result.emplace_back(Token::Type::Minus, std::string(1, input[i]));
		}
		else if (input[i] == '(')
		{
			result.emplace_back(Token::Type::LParen, std::string(1, input[i]));
		}
		else if (input[i] == ')')
		{
			result.emplace_back(Token::Type::RParen, std::string(1, input[i]));
		}
		else if (std::isdigit(input[i]))
		{
			std::ostringstream out;
			out << input[i++];
			while(i < std::size(input) && std::isdigit(input[i]))
			{
				out << input[i];
				++i;
			}

			result.emplace_back(Token::Type::Literal, out.str());
			--i;
		}
		else if (input[i] == ' ')
		{
			// Do nothing!
			continue;
		}
		else
		{
			std::ostringstream err_msg;
			err_msg << "Invalid token";
			err_msg << " `" << input[i] << "` ";
			err_msg << " found while lexing!";
			throw std::runtime_error{err_msg.str()};
		}
	}

	return result;
}

struct IElement
{
	virtual int eval() const = 0;
};

struct Literal : public IElement
{
	int val;

	explicit Literal(int value)
		:	val{value}
	{}

	int eval() const override
	{
		return val;
	}
};

struct BinaryOperation : public IElement
{
	std::shared_ptr<IElement> lhs, rhs;
	enum class Type { Plus, Minus, NoOp } type;

	explicit BinaryOperation(std::shared_ptr<IElement> lhs = nullptr, std::shared_ptr<IElement> rhs = nullptr, Type t = Type::NoOp)
		:	lhs{std::move(lhs)},
			rhs{std::move(rhs)},
			type{std::move(t)}
	{}

	int eval() const override
	{
		auto lhs_val{lhs->eval()}, rhs_val{rhs->eval()};
		switch(type)
		{
		case Type::Plus:
			return lhs_val + rhs_val;
		case Type::Minus:
			return lhs_val - rhs_val;
		default:
			throw std::runtime_error{"Unsupported operation!"};
		}
	}
};

std::shared_ptr<IElement> parse(const std::vector<Token>& tokens)
{
	auto result{std::make_shared<BinaryOperation>()};
	for(auto i = 0ull; i < std::size(tokens); ++i)
	{
		switch(tokens[i].type)
		{
		case Token::Type::Literal:
			{
				auto lit{std::make_shared<Literal>(boost::lexical_cast<int>(tokens[i].text))};
				if(result->lhs == nullptr)
				{
					result->lhs = std::move(lit);
				}
				else
				{
					result->rhs = std::move(lit);
				}
			}
			break;
		case Token::Type::Plus:
			result->type = BinaryOperation::Type::Plus;
			break;
		case Token::Type::Minus:
			result->type = BinaryOperation::Type::Minus;
			break;
		case Token::Type::LParen:
			{
				auto j{i};
				while(j < std::size(tokens) && tokens[j].type != Token::Type::RParen)
				{
					++j;
				}

				if(j == std::size(tokens))
				{
					throw std::runtime_error{"Mismatched paranthesis!"};
				}

				auto subexpr{std::vector<Token>(tokens.begin() + (i + 1), tokens.begin() + j)};
				auto elem{parse(subexpr)};
				if(result->lhs == nullptr)
				{
					result->lhs = std::move(elem);
				}
				else
				{
					result->rhs = std::move(elem);
				}

				i = j;
			}
			break;
		default:
			break;
		}
	}

	return result;
}

int main()
{
	std::string input{"(13 - 4) - (12 + 1)"};
	auto res{lex(input)};
	std::copy(res.cbegin(), res.cend(), std::ostream_iterator<decltype(res)::value_type>(std::cout, " "));
	auto tree{parse(res)};
	std::cout << input << " = " << tree->eval() << std::endl;

	return 0;
}


/**
 * Copyright (c) 2015 Carnegie Mellon University, Daniel Maturana <dimatura@cmu.edu>
 *
 * For License information please see the LICENSE file in the root directory.
 *
 */

#ifndef RAYCASTING_HPP_OLVFBMND
#define RAYCASTING_HPP_OLVFBMND

#include <Eigen/Core>

#include <pcl_util/point_types.hpp>

#include "scrollgrid/grid_types.hpp"
#include "scrollgrid/box.hpp"
#include "scrollgrid/ray.hpp"
#include "scrollgrid/scrollgrid3.hpp"
#include "scrollgrid/dense_array3.hpp"

namespace ca
{

/**
 * Axis-aligned bounding box intersection test.
 * Reference:
 * An Efficient and Robust Ray–Box Intersection Algorithm, Williams et al. 2004
 * tmin and tmax are updated
 */
template<typename Scalar>
bool aabb_ray_intersect(const ca::scrollgrid::Box<Scalar, 3>& box,
                        ca::scrollgrid::Ray3<Scalar> &r) {
  Scalar tmin = (box.bound(   boost::get<0>(r.sign) ).x() - r.origin.x()) * r.invdir.x();
  Scalar tmax = (box.bound( 1-boost::get<0>(r.sign) ).x() - r.origin.x()) * r.invdir.x();

  Scalar tymin = (box.bound(  boost::get<1>(r.sign) ).y() - r.origin.y()) * r.invdir.y();
  Scalar tymax = (box.bound(1-boost::get<1>(r.sign) ).y() - r.origin.y()) * r.invdir.y();

  if ((tmin > tymax) || (tymin > tmax)) { return false; }
  if (tymin > tmin) { tmin = tymin; }
  if (tymax < tmax) { tmax = tymax; }

  Scalar tzmin = (box.bound(  boost::get<2>(r.sign)).z() - r.origin.z()) * r.invdir.z();
  Scalar tzmax = (box.bound(1-boost::get<2>(r.sign)).z() - r.origin.z()) * r.invdir.z();

  if ((tmin > tzmax) || (tzmin > tmax)) { return false; }
  if (tzmin > tmin) { tmin = tzmin; }
  if (tzmax < tmax) { tmax = tzmax; }
  if (tmin > r.tmin) { r.tmin = tmin; }
  if (tmax < r.tmax) { r.tmax = tmax; }
  return true;
}

/**
 * Trace a straight line from start_pos to end_pos.
 * At each step fun(i, j, k) is called.
 *
 * NOTE start_pos and end_pos should be inside the grid
 *
 * Reference: graphics gems article
 * TODO consider DDA-type raytracing.
 */
template<class TraceFunctor>
void bresenham_trace(const Vec3Ix& start_pos,
                     const Vec3Ix& end_pos,
                     const TraceFunctor& fun) {
  // beware: vec3ix are int64_t
  int x = start_pos[0],
      y = start_pos[1],
      z = start_pos[2];
  int dx = end_pos[0] - start_pos[0],
      dy = end_pos[1] - start_pos[1],
      dz = end_pos[2] - start_pos[2];
  int sx, sy, sz;
  //X
  if ( dx>0 ) {
    sx = 1;
  } else if ( dx<0 ) {
    sx = -1;
    dx = -dx;
  } else {
    sx = 0;
  }

  //Y
  if ( dy>0 ) {
    sy = 1;
  } else if ( dy<0 ) {
    sy = -1;
    dy = -dy;
  } else {
    sy = 0;
  }

  //Z
  if ( dz>0 ) {
    sz = 1;
  } else if ( dz<0 ) {
    sz = -1;
    dz = -dz;
  } else {
    sz = 0;
  }

  int ax = 2*dx,
      ay = 2*dy,
      az = 2*dz;

  if ( ( dy <= dx ) && ( dz <= dx ) ) {
    for (int decy=ay-dx, decz=az-dx;
         ;
         x+=sx, decy+=ay, decz+=az) {
      //SetP ( grid,x,y,z,end_pos, atMax, count);
      if(!fun(x, y, z)) break;
      //Bresenham step
      if ( x==end_pos[0] ) break;
      if ( decy>=0 ) {
        decy-=ax;
        y+=sy;
      }
      if ( decz>=0 ) {
        decz-=ax;
        z+=sz;
      }
    }
  } else if ( ( dx <= dy ) && ( dz <= dy ) ) {
    //dy>=dx,dy
    for (int decx=ax-dy,decz=az-dy;
         ;
         y+=sy,decx+=ax,decz+=az ) {
      // SetP ( grid,x,y,z,end_pos, atMax, count);
      if(!fun(x, y, z)) break;
      //Bresenham step
      if ( y==end_pos[1] ) break;
      if ( decx>=0 ) {
        decx-=ay;
        x+=sx;
      }
      if ( decz>=0 ) {
        decz-=ay;
        z+=sz;
      }
    }
  } else if ( ( dx <= dz ) && ( dy <= dz ) ) {
    //dy>=dx,dy
    for (int decx=ax-dz,decy=ay-dz;
         ;
         z+=sz,decx+=ax,decy+=ay ) {
      //SetP ( grid,x,y,z,end_pos, atMax, count);
      if(!fun(x, y, z))  break;
      //Bresenham step
      if ( z==end_pos[2] ) break;
      if ( decx>=0 ) {
        decx-=az;
        x+=sx;
      } if ( decy>=0 ) {
        decy-=az;
        y+=sy;
      }
    }
  }
}

/**
 * Simply increment a counter in densearray3 for each step along the way.
 */
template<class GridScalar, class ArrayScalar>
void bresenham_trace_simple(const Vec3Ix& start_pos,
                            const Vec3Ix& end_pos,
                            const ca::ScrollGrid3<GridScalar>& grid3,
                            ca::DenseArray3<ArrayScalar>& array3
                            ) {
  //int ray_ctr = 0;
  // beware: vec3ix are int64_t
  int x = start_pos[0],
      y = start_pos[1],
      z = start_pos[2];
  int dx = end_pos[0] - start_pos[0],
      dy = end_pos[1] - start_pos[1],
      dz = end_pos[2] - start_pos[2];
  int sx, sy, sz;
  //X
  if ( dx>0 ) {
    sx = 1;
  } else if ( dx<0 ) {
    sx = -1;
    dx = -dx;
  } else {
    sx = 0;
  }

  //Y
  if ( dy>0 ) {
    sy = 1;
  } else if ( dy<0 ) {
    sy = -1;
    dy = -dy;
  } else {
    sy = 0;
  }

  //Z
  if ( dz>0 ) {
    sz = 1;
  } else if ( dz<0 ) {
    sz = -1;
    dz = -dz;
  } else {
    sz = 0;
  }

  int ax = 2*dx,
      ay = 2*dy,
      az = 2*dz;

  if ( ( dy <= dx ) && ( dz <= dx ) ) {
    for (int decy=ay-dx, decz=az-dx;
         ;
         x+=sx, decy+=ay, decz+=az) {
      mem_ix_t mem_ix = grid3.grid_to_mem(x, y, z);
      array3[mem_ix] += 1;
      //array3[mem_ix] = ray_ctr++;
      //Bresenham step
      if ( x==end_pos[0] ) break;
      if ( decy>=0 ) {
        decy-=ax;
        y+=sy;
      }
      if ( decz>=0 ) {
        decz-=ax;
        z+=sz;
      }
    }
  } else if ( ( dx <= dy ) && ( dz <= dy ) ) {
    //dy>=dx,dy
    for (int decx=ax-dy,decz=az-dy;
         ;
         y+=sy,decx+=ax,decz+=az ) {
      mem_ix_t mem_ix = grid3.grid_to_mem(x, y, z);
      array3[mem_ix] += 1;
      //array3[mem_ix] = ray_ctr++;
      //Bresenham step
      if ( y==end_pos[1] ) break;
      if ( decx>=0 ) {
        decx-=ay;
        x+=sx;
      }
      if ( decz>=0 ) {
        decz-=ay;
        z+=sz;
      }
    }
  } else if ( ( dx <= dz ) && ( dy <= dz ) ) {
    //dy>=dx,dy
    for (int decx=ax-dz,decy=ay-dz;
         ;
         z+=sz,decx+=ax,decy+=ay ) {
      grid_ix_t mem_ix = grid3.grid_to_mem(x, y, z);
      array3[mem_ix] += 1;
      //array3[mem_ix] = ray_ctr++;
      //Bresenham step
      if ( z==end_pos[2] ) break;
      if ( decx>=0 ) {
        decx-=az;
        x+=sx;
      } if ( decy>=0 ) {
        decy-=az;
        y+=sy;
      }
    }
  }
}

template<class TraceFunctor>
void bresenham_trace(const Vec2Ix& start_pos,
                     const Vec2Ix& end_pos,
                     const TraceFunctor& fun) {
    int x = start_pos[0],
      y = start_pos[1];

    int dx = end_pos[0] - start_pos[0],
      dy = end_pos[1] - start_pos[1];
    int sx, sy;
    //X
    if ( dx>0 ) {
    sx = 1;
    } else if ( dx<0 ) {
    sx = -1;
    dx = -dx;
    } else {
    sx = 0;
    }

    //Y
    if ( dy>0 ) {
    sy = 1;
    } else if ( dy<0 ) {
    sy = -1;
    dy = -dy;
    } else {
    sy = 0;
    }

    int ax = 2*dx,
      ay = 2*dy;

    if (dy <= dx){
        for (int decy=ay-dx;;
             x+=sx, decy+=ay) {
          bool end_cell = false;
          //Bresenham step
          if(!fun(x,y,end_cell)){ ROS_INFO("Breaking due to function call"); break;}

          if ( x==end_pos[0] ) break;
          if ( decy>=0 ) {
            decy-=ax;
            y+=sy;
          }
        }
    } else if ( dx <= dy ){
        for (int decx=ax-dy;;
             y+=sy,decx+=ax) {
          bool end_cell = false;
          //Bresenham step
          if(!fun(x,y,end_cell)){ break; ROS_INFO("Breaking due to function call"); break;}

          if ( y==end_pos[1] ) break;
          if ( decx>=0 ) {
            decx-=ay;
            x+=sx;
          }
        }
    }
}

}

#endif /* end of include guard: RAYCASTING_HPP_OLVFBMND */


#include <Eigen/Core>
#include "caffe2/operators/elementwise_op.h"

namespace caffe2 {

struct SqrtCPUFunctor {
  template <typename T>
  inline void
  operator()(const int n, const T* x, T* y, CPUContext* /*device_context*/) {
    EigenVectorArrayMap<T>(y, n) = ConstEigenVectorArrayMap<T>(x, n).sqrt();
  }
};

REGISTER_CPU_OPERATOR(
    Sqrt,
    UnaryElementwiseOp<TensorTypes<float>, CPUContext, SqrtCPUFunctor>);
// Input: X, output: Y
OPERATOR_SCHEMA(Sqrt)
    .NumInputs(1)
    .NumOutputs(1)
    .AllowInplace({{0, 0}})
    .IdenticalTypeAndShape()
    .SetDoc(R"DOC(
Computes the element-wise sqrt of the input.
)DOC")
    .Input(0, "X", "ND input tensor")
    .Output(0, "Y", "ND input tensor");

class GetSqrtGradient : public GradientMakerBase {
  using GradientMakerBase::GradientMakerBase;
  vector<OperatorDef> GetGradientDefs() override {
    Argument scale_arg;
    scale_arg.set_name("scale");
    scale_arg.set_f(0.5);
    return vector<OperatorDef>{CreateOperatorDef(
                                   "Scale",
                                   "",
                                   std::vector<string>{GO(0)},
                                   std::vector<string>{GI(0)},
                                   std::vector<Argument>{scale_arg}),
                               CreateOperatorDef(
                                   "Div",
                                   "",
                                   std::vector<string>{GI(0), O(0)},
                                   std::vector<string>{GI(0)})};
  }
};
REGISTER_GRADIENT(Sqrt, GetSqrtGradient);
} // namespace caffe2


#include <Eigen/Core>
using namespace Eigen;
void foo(Vector4f &u, Vector4f &v, Vector4f &w) {
  EIGEN_ASM_COMMENT("begin");
  u = v + 3 * w;
  EIGEN_ASM_COMMENT("end");
}


//
// Created by philipp on 26.12.19.
//

#ifndef FUNNELS_CPP_DYNAMICS_HH
#define FUNNELS_CPP_DYNAMICS_HH

#include <Eigen/Core>
#include <cmath>
#include <cassert>

using namespace Eigen;

class kinematic_2d_sys_t{
public:
  constexpr static const size_t _dimx=4, _dimv=2, _dimu=2;

  static void compute(MatrixXd &x, const VectorXd &u, const VectorXd &t){
    assert(x.cols()==t.size());
    assert(u.rows()==_dimu && x.rows()==_dimx && u.rows()==_dimv);
    // State vector is [x,y,dx,dy]
    const Vector2d x0 = x.block<_dimx-_dimv,1>(0,0);
    size_t n = t.size();
    // Set velocity
    // For a kinematic sys, the velocity can change instantly
    x.block(_dimx-_dimv,0,_dimv,n).colwise() = u;
    // Compute state
    for (size_t i=0; i<n; i++){
      x.block<_dimx-_dimv,1>(0,i) = x0 + u*t(i);
    }
    // Done
  }
};
#endif //FUNNELS_CPP_DYNAMICS_HH


#ifndef CX_DQMC_GREENS_REPLICA_RENYI_GS_HPP
#define CX_DQMC_GREENS_REPLICA_RENYI_GS_HPP

#include <complex>
#include <libdqmc/cx_dqmc_abstract_greens.hpp>
#include <libdqmc/la.hpp>
#include <libdqmc/cx_density.hpp>
#include <libdqmc/parameters.hpp>
#include <libdqmc/cx_workspace.hpp>
#include <libdqmc/cx_checkerboard.hpp>
#include <libdqmc/cx_interactions.hpp>
#include <libdqmc/cx_calculate_greens_ft.hpp>
#include <alps/alea.h>
#include <boost/multi_array.hpp>
#include <exception>
#include <cstring>

typedef boost::multi_array<double, 3> aux_spin_3t;

namespace cx_dqmc {

    class greens_replica_renyi_gs : public cx_dqmc::abstract_greens {

    public:
	bool first_initialization;

	cx_mat_t u_temp, t_temp;
	cx_mat ket, ket_sqr, bra, bra_sqr;
	vec ket_d, bra_d, d_temp;

	cx_mat_t Ul, Um, Ur, Tl, Tm, Tr;
	vec_t Dl, Dm, Dr;

	int det_method;
	bool fresh_sign;

	greens_replica_renyi_gs(dqmc::parameters* p_,
				cx_dqmc::workspace* ws_,
				cx_dqmc::workspace* reg_ws_,
				aux_spin_3t* s)
	    : cx_dqmc::abstract_greens(p_, ws_, reg_ws_, s) {};

	void initialize();
	void build_stack();
	int propagate(alps::Observable& stability);

	void ket_update(int start, int stop,
			int idx, int target);
	void bra_update(int start, int stop,
			int idx, int target);

	void update_remove_interaction();
	void update_add_interaction();

	void slice_sequence(int start, int stop) {};
	void slice_sequence_left(int start, int stop, cx_mat& M);
	void slice_sequence_left_renyi(int start, int stop, cx_mat& M);
	void slice_sequence_left_t(int start, int stop, cx_mat& M);
	void slice_sequence_left_renyi_t(int start, int stop, cx_mat& M);
	void slice_sequence_right(int start, int stop, cx_mat& M);

	void slice_matrix_left(int slice, cx_mat& M, bool inv = false);
	void slice_matrix_left_t(int slice, cx_mat& M, bool inv = false);
	void slice_matrix_left_renyi_t(int slice, cx_mat& M, bool inv = false);
	void slice_matrix_right(int slice, cx_mat& M, bool inv = false);

	void slice_matrix_left_renyi(int slice, cx_mat& M, bool inv = false);
	void slice_matrix_right_renyi(int slice, cx_mat& M, bool inv = false);

	void hopping_matrix_left(int slice, cx_mat& M, bool inv = false);
	void hopping_matrix_right(int slice, cx_mat& M, bool inv = false);
	void hopping_matrix_left_renyi(int slice, cx_mat& M, bool inv = false);
	void hopping_matrix_right_renyi(int slice, cx_mat& M, bool inv = false);

	void interaction_matrix_left(int slice, cx_mat& M,
				     int bond_group, bool inv = false);
	void interaction_matrix_right(int slice, cx_mat& M,
				      int bond_group, bool inv = false);

	void regularize_svd(vec& in, vec& out);

	inline void prep_bra(cx_mat& col, vec& diag, cx_mat& sqr) {
	    using namespace std;
	    bra = col.transpose();
	    bra_d = diag;
	    bra_sqr = sqr.transpose();

	    dqmc::la::thin_col_to_invertible(col, ws->mat_1);
	    Tr = ws->mat_1.transpose().eval();
	    regularize_svd(diag, Dr);
	    Ur.setIdentity();
	    Ur.block(0, 0, ws->particles, ws->particles) = sqr.transpose().eval();

	    // dqmc::la::thin_col_to_invertible(col, Ul);
	    // regularize_svd(diag, Dl);
	    // Tl.setIdentity();
	    // Tl.block(0, 0, ws->particles, ws->particles) = sqr;

	}

	inline void prep_ket(cx_mat& col, vec& diag,
			     cx_mat& sqr) {
	    using namespace std;
	    ket = col;
	    ket_d = diag;
	    ket_sqr = sqr;

	    // dqmc::la::thin_col_to_invertible(col, ws->mat_1);
	    // Tr = ws->mat_1.transpose().eval();
	    // regularize_svd(diag, Dr);
	    // Ur.setIdentity();
	    // Ur.block(0, 0, ws->particles, ws->particles) = sqr.transpose().eval();

	    dqmc::la::thin_col_to_invertible(col, Ul);
	    regularize_svd(diag, Dl);
	    Tl.setIdentity();
	    Tl.block(0, 0, ws->particles, ws->particles) = sqr;
	}

	void log_weight();
	void log_weight_full_piv();
	void calculate_greens_exact(int slice);
	void calculate_greens_general();
	void calculate_greens_half_compressed();
	void calculate_greens_basic();
	inline void calculate_greens() {
	    using namespace std;

	    if (p->escalate_stability == false) {
		if (p->basic_stability == true) {
		    calculate_greens_basic();
		} else if (p->half_stability == true) {
		    calculate_greens_half_compressed();
		} else if (p->full_stability == true) {
		    calculate_greens_general();
		}
	    } else {
		if (int(stability[idx]) == 0) {
		    calculate_greens_basic();
		} else if (int(stability[idx]) == 1) {
		    calculate_greens_half_compressed();
		} else if (int(stability[idx]) == 2) {
		    calculate_greens_general();
		}
	    }

	    if (current_slice % slices == 0) return;

	    double stability_check = check_stability();
	    stability_checks[idx] = stability_check;

	    if (stability_check > 1e-1) {
		if (int(stability[idx]) != 2) {
		    cout << p->outp << "renyi - Escalating stability at " << idx << " to "
			 << int(stability[idx] + 1) << endl;
		}
		stability[idx] = std::min(2, int(stability[idx]) + 1);
		if (idx - direction >= 0 && idx - direction < n_elements) {
		    stability[idx - direction] = std::min(2, int(stability[idx - direction]) + 1);
		}
	    }
	}

    	double check_stability();

	void enlarge(int replica, cx_mat_t& in, cx_mat_t& out);
	void enlarge(int replica, vec_t& in, vec_t& out);
	void enlarge_thin(int replica, cx_mat_t& in, cx_mat_t& out);
	void enlarge_thin(int replica, vec_t& in, vec_t& out);
	void enlarge_thin_ized_col(int replica, cx_mat_t& in, cx_mat_t& out);
	void enlarge_thin_ized_row(int replica, cx_mat_t& in, cx_mat_t& out);
    };
}
#endif


#define CATCH_CONFIG_MAIN  // This tells Catch to provide a main() - only do this in one cpp file
#include "catch.hpp"

#include <math.h>

#include <Eigen/Core>
#include <Eigen/Geometry>

#include <simulator2/world.hpp>
#include <simulator2/vision.hpp>

#include <iostream>

namespace fishbowl = cuauv::fishbowl;

// body 3-2-1
Eigen::Quaterniond euler_to_quat(double h, double p, double r)
{
    return Eigen::Quaterniond(Eigen::AngleAxisd(h, Eigen::Vector3d::UnitZ())
                            * Eigen::AngleAxisd(p, Eigen::Vector3d::UnitY())
                            * Eigen::AngleAxisd(r, Eigen::Vector3d::UnitX()));
}

TEST_CASE("SAP Camera basic functionality", "[vision]") {
    sim::world w;
    Eigen::Quaterniond pq(1, 0, 0, 0);
    Eigen::Vector3d px(-5, -3, 0);
    double f = 1;

    sim::entity_id eid = w.add_entity(sim::entity(1, 1, sim::inertia_tensor(1, 1, 1), Eigen::Quaterniond(1, 0, 0, 0)));
    sim::entity& e = w.get_entity(eid);
    e.x = Eigen::Vector3d(-4, -3, 0);

    sim::camera cam(&w, &pq, &px, euler_to_quat(-M_PI/2, 0, 0), Eigen::Vector3d(1, 1, -1), f);
    Eigen::Vector2d x;
    double r, d;

    std::tie(x, r, d) = cam.query(eid);
    REQUIRE(x[0] == Approx(0));
    REQUIRE(x[1] == Approx(1));
    REQUIRE(r != 0);

    // reposition #1

    e.x = Eigen::Vector3d(-4, -1, 0);

    std::tie(x, r, d) = cam.query(eid);
    REQUIRE(x[0] == Approx(0));
    REQUIRE(x[1] == Approx(-1));
    REQUIRE(r == 0);

    // resposition #2

    e.x = Eigen::Vector3d(-3, -4, 0);

    std::tie(x, r, d) = cam.query(eid);
    REQUIRE(x[0] == Approx(0.5));
    REQUIRE(x[1] == Approx(0.5));
    REQUIRE(r != 0);

    // change focal length

    cam.f = 0.5;
    cam.step();

    std::tie(x, r, d) = cam.query(eid);
    REQUIRE(x[0] == Approx(0.25));
    REQUIRE(x[1] == Approx(0.25));
    REQUIRE(r != 0);
}


#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <tuple>
#include <stdbool.h>
#include <bitset>
#include <string>
#include <boost/multiprecision/cpp_int.hpp>

namespace mp = boost::multiprecision;
using namespace std;

int main(void) {
    int a,b;
    cin >> a >> b;
    auto result = max(a + a - 1, max(a + b, b + b - 1));
    cout << result << endl;
    return 0;
}


#include "common.h"
#include "ConformalBlock.h"
#include "gegenbauer_polynomial.hpp"
#include <iostream>
#include <boost/math/special_functions/binomial.hpp>
#include <boost/math/special_functions/pow.hpp>
#include <boost/math/special_functions/factorials.hpp>

using namespace boost::math;

float_type& RCoefficients::GetCachedRI(int type, int n) {
    return this->cacheRI[type * MaxOrder + n -1];
}

float_type& RCoefficients::GetCachedRII(int type, int l, int n) {
    boost::unordered_map<int, float_type>::iterator it = cacheR.find(type * 64 * 64 + n * 64 + l);
    if (it != cacheR.end()) {
        return it->second;
    } else {
        cacheR[type * 64 * 64 + n * 64 + l] = std::numeric_limits<float_type>::max();
        return cacheR[type * 64 * 64 + n * 64 + l];
    }
}

float_type RCoefficients::RI(int n) {
    float_type& ret = GetCachedRI(0, n);
    if (ret != std::numeric_limits<float_type>::max()) return ret;
    ret = RI_NonDelta(n) * rising_factorial((dlt12 + 1.0 - n) / 2, n) * rising_factorial((dlt34 + 1.0 - n) / 2, n);
    return ret;
}

// the delta independent part of RI.
float_type RCoefficients::RI_NonDelta(int n) {
    float_type fac = factorial<float_type>(n);
    i64 p = ((i64)1) << n;
    float_type ret = p / (fac * fac) * n;

    if (n % 2 == 0) return -ret;
    return ret;
}

float_type RCoefficients::RI_dDelta12(int n) {
    float_type& ret = GetCachedRI(1, n);
    if (ret != std::numeric_limits<float_type>::max()) return ret;
    ret = RI_dDelta(n, dlt12, dlt34);
    return ret;
}

float_type RCoefficients::RI_dDelta34(int n) {
    float_type& ret = GetCachedRI(2, n);
    if (ret != std::numeric_limits<float_type>::max()) return ret;
    ret = RI_dDelta(n, dlt34, dlt12);
    return ret;
}

float_type RCoefficients::RI_dDelta(int n, float_type deltaDiffA, float_type deltaDiffB) {
    if (!IsInteger((deltaDiffA + 1 - n) / 2)) {
        return digammaDiff((deltaDiffA + 1 - n) / 2, n) * RI(n) * .5;
    } else {
        int x = (int)floor((deltaDiffA + 1 - n) / 2 + EPS);
        if (x > 0 || x + n <= 0) {
            return digammaDiff((deltaDiffA + 1 - n) / 2, n) * RI(n) * .5;
        } else {
            // -n + 1 <= x <= 0 need special handling because digammaDiff() has a pole and RI() is zero.
            // now, RI() ~ (x)_n, the (x)_n factor has a pole
            float_type ret = rising_factorial((deltaDiffB + 1.0 - n) / 2, n) * 0.5;
            ret *= factorial<float_type>(-x) * factorial<float_type>(x + n - 1);
            if (x % 2 != 0) ret = -ret;

            return ret * RI_NonDelta(n);
        }
    }
}

float_type RCoefficients::RII(int l, int n) {
    float_type& ret = GetCachedRII(0, l, n);
    if (ret != std::numeric_limits<float_type>::max()) return ret;
    ret = RII_NonDelta(l, n) * rising_factorial((dlt12 + 1.0 - n) / 2, n) * rising_factorial((dlt34 + 1.0 - n) / 2, n);
    return ret;
}

// the delta independent part of RII.
float_type RCoefficients::RII_NonDelta(int l, int n) {
    float_type ret = binomial_coefficient<float_type>(l, n) * n;
    ret /= factorial<float_type>(n);
    ret /= (((i64)1) << n);

    float_type h = d / 2;
    if (abs(h - 1) < EPS && l == n) {
        // if h == 1 and l == n, zero factors of numerator and denominator cancel out.
        ret /= rising_factorial(h + l - n, n);

    } else {
        ret /= rising_factorial(h + l - n, n) * rising_factorial(h + l - n - 1, n);
        ret *= rising_factorial(d + l - n - 2, n);
    }

    if (n % 2 == 0) return -ret;
    return ret;
}

float_type RCoefficients::RII_dDelta12(int l, int n) {
    float_type& ret = GetCachedRII(1, l, n);
    if (ret != std::numeric_limits<float_type>::max()) return ret;
    ret = RII_dDelta(l, n, dlt12, dlt34);
    return ret;
}

float_type RCoefficients::RII_dDelta34(int l, int n) {
    float_type& ret = GetCachedRII(2, l, n);
    if (ret != std::numeric_limits<float_type>::max()) return ret;
    ret = RII_dDelta(l, n, dlt34, dlt12);
    return ret;
}

float_type RCoefficients::RII_dDelta(int l, int n, float_type deltaDiffA, float_type deltaDiffB) {
    if (!IsInteger((deltaDiffA + 1 - n) / 2)) {
        return digammaDiff((deltaDiffA + 1 - n) / 2, n) * RII(l, n) * .5;
    } else {
        int x = (int)floor((deltaDiffA + 1 - n) / 2 + EPS);
        if (x > 0 || x + n <= 0) {
            return digammaDiff((deltaDiffA + 1 - n) / 2, n) * RII(l, n) * .5;
        } else {
            // -n + 1 <= x <= 0 need special handling because digammaDiff() has a pole and RII() is zero.
            // now, RII() ~ (x)_n, the (x)_n factor has a pole
            float_type ret = rising_factorial((deltaDiffB + 1.0 - n) / 2, n) * 0.5;
            ret *= factorial<float_type>(-x) * factorial<float_type>(x + n - 1);
            if (x % 2 != 0) ret = -ret;

            return ret * RII_NonDelta(l, n);
        }
    }
}

float_type RCoefficients::RIII(int l, int n) {
    float_type& ret = GetCachedRII(3, l, n);
    if (ret != std::numeric_limits<float_type>::max()) return ret;
    float_type h = d / 2;
    ret = rising_factorial((dlt12 + h + l - n) / 2, n) * rising_factorial((dlt12 - h - l - n + 2) / 2, n);
    ret *= rising_factorial((dlt34 + h + l - n) / 2, n) * rising_factorial((dlt34 - h - l - n + 2) / 2, n) * RIII_NonDelta(l, n);
    return ret;
}

float_type RCoefficients::RIII_NonDelta(int l, int n) {
    float_type h = d / 2;
    float_type ret;
    // if h is not integer, there is no poles.
    if (!IsInteger(h)) {
        ret = rising_factorial(h - n - 1, 2 * n);
        ret /= rising_factorial(h + l - n - 1, 2 * n) * rising_factorial(h + l - n, 2 * n);
    } else {
        int ih = (int)floor(h + EPS);
        if (n >= ih - 1 && n < ih + l - 1) {
            // in this case, the numerate part is zero, and denominator is nonzero
            return .0;
        } else if (2 * n >= l + 1 && n == ih + l - 1) {
            // in this case, the zeros of numerate and denominate cancel out.
            ret = 1.0 / binomial_coefficient<float_type>(2 * n - 1, l);
            ret /= rising_factorial(h + l - n, 2 * n);
            if (l % 2 == 1) ret = -ret;
        } else {
            return 0.0; // temperary return 0 because the following workaround leads to divergent results.

            // n > h + l - 1 case, numerator is zero but denominator is float_type-zero
            // to avoid infinity, we let h -> h + EPS
/*            float_type hh = h - EPS;
            float_type inf = rising_factorial(hh - n - 1, 2 * n) / rising_factorial(hh + l - n - 1, 2 * n);
            inf /=  rising_factorial(hh + l - n, 2 * n);
            ret *= inf;*/
        }
    }

    float_type fac = factorial<float_type>(n);
    ret *= n / (fac * fac);

    if (n % 2 == 0) return -ret;
    return ret;
}

float_type RCoefficients::RIII_dDelta12(int l, int n) {
    float_type& ret = GetCachedRII(4, l, n);
    if (ret != std::numeric_limits<float_type>::max()) return ret;
    ret = RIII_dDelta(l, n, dlt12, dlt34);
    return ret;
}

float_type RCoefficients::RIII_dDelta34(int l, int n) {
    float_type& ret = GetCachedRII(5, l, n);
    if (ret != std::numeric_limits<float_type>::max()) return ret;
    ret = RIII_dDelta(l, n, dlt34, dlt12);
    return ret;
}

float_type RCoefficients::RIII_dDelta(int l, int n, float_type deltaDiffA, float_type deltaDiffB) {
    float_type h = d / 2;
    float_type ret1;
    if (!IsInteger((deltaDiffA - h - l - n + 2) / 2)) {
        ret1 = digammaDiff((deltaDiffA - h - l - n + 2) / 2, n) * RIII(l, n);
    } else {
        int x = (int)floor((deltaDiffA - h - l - n + 2) / 2 + EPS);
        if (x > 0 || x + n <= 0) {
            ret1 = digammaDiff((deltaDiffA - h - l - n + 2) / 2, n) * RIII(l, n);
        } else {
            // -n + 1 <= x <= 0 need special handling because digammaDiff() has a pole and RII() is zero.
            // now, RII() ~ (x)_n, the (x)_n factor has a pole
            ret1 = rising_factorial((deltaDiffA + h + l - n) / 2, n);
            ret1 *= rising_factorial((deltaDiffB + h + l - n) / 2, n) * rising_factorial((deltaDiffB - h - l - n + 2) / 2, n);
            ret1 *= factorial<float_type>(-x) * factorial<float_type>(x + n - 1);
            if (x % 2 != 0) ret1 = -ret1;

            ret1 *= RIII_NonDelta(l, n);
        }
    }

    float_type ret2;
    if (!IsInteger((deltaDiffA + h + l - n) / 2)) {
        ret2 = digammaDiff((deltaDiffA + h + l - n) / 2, n) * RIII(l, n);
    } else {
        int x = (int)floor((deltaDiffA + h + l - n) / 2 + EPS);
        if (x > 0 || x + n <= 0) {
            ret2 = digammaDiff((deltaDiffA + h + l - n) / 2, n) * RIII(l, n);
        } else {
            // -n + 1 <= x <= 0 need special handling because digammaDiff() has a pole and RII() is zero.
            // now, RII() ~ (x)_n, the (x)_n factor has a pole
            ret2 = rising_factorial((deltaDiffA - h - l - n + 2) / 2, n);
            ret2 *= rising_factorial((deltaDiffB + h + l - n) / 2, n) * rising_factorial((deltaDiffB - h - l - n + 2) / 2, n);
            ret2 *= factorial<float_type>(-x) * factorial<float_type>(x + n - 1);
            if (x % 2 != 0) ret2 = -ret2;

            ret2 *= RIII_NonDelta(l, n);
        }
    }

    return (ret1 + ret2) * 0.5;
}

float_type ConformalBlockScalars::HInfinity(int l, float_type r, float_type eta) {
    float_type ret;
    if (hCache.Get(l, &ret)) return ret;

    float_type h = d / 2;
    ret = pow(1 - r * r, 1 - h);
    ret /= pow(r * r - 2 * r * eta + 1, (1 - dlt12 + dlt34) / 2);
    ret /= pow(r * r + 2 * r * eta + 1, (1 + dlt12 - dlt34) / 2);
    ret *= factorial<float_type>(l);
    ret /= (((i64)1) << l);

    if (abs(h - 1) > EPS) {
        ret /= rising_factorial(h - 1, l);
        float_type x[1];
        x[0] = eta;
        float_type *v = gegenbauer_polynomial_value(l, 1, h - 1, x);
        ret *= v[l + 0 * (l + 1)];
        delete v;
    } else if (l != 0) {
        // for the h == 1 and l == 0 case, (h-1)_l == 1 == gegenbauer(l, h-1);
        // for the h == 1 and l > 0 case, the zeros of (h-1)_l and gegenbauer(l, h-1) cancel out
        ret *= GegenbauerDAlphaAt0(l, eta) / factorial<float_type>(l - 1);
    }

    hCache.Set(l, ret);
    return ret;
}

float_type ConformalBlockScalars::HInfinity_dDelta12(int l, float_type r, float_type eta) {
    return HInfinity_dDelta(l, r, eta);
}

float_type ConformalBlockScalars::HInfinity_dDelta34(int l, float_type r, float_type eta) {
    return -HInfinity_dDelta(l, r, eta);
}

float_type ConformalBlockScalars::HInfinity_dDelta(int l, float_type r, float_type eta) {
    return 0.5 * HInfinity(l, r, eta) * log((r * r - 2 * eta * r + 1)/(r * r + 2 * eta * r + 1));
}

float_type ConformalBlockScalars::HRecursion(float_type delta, int l, float_type r, float_type eta, int order) {
    int idelta = std::numeric_limits<int>::max();
    float_type ret;
    if (IsInteger(delta)) {
        idelta = (int)floor(delta + EPS);
        if (hCache.Get(idelta, l, order, &ret)) return ret;
    }

    ret = HInfinity(l, r, eta);
    if (order == 0) return ret;

    float_type deltaAs;
    int la, na;

    // type I
    for (int n = 1; n <= order; n++) {
        deltaAs = 1 - l - n;
        la = l + n;
        na = n;
        // this if statement is only for the following case:
        // when the exchange operator is identity, i.e, delta==l==0,
        // then delta - deltaAs can be zero.
        if (abs(delta - deltaAs) < EPS) continue;
        ret += pow(4 * r, na) * rCoef->RI(n) / (delta - deltaAs) * HRecursion(deltaAs + na, la, r, eta, order - na);
    }

    // type II
    for (int n = 1; n <= order && n <= l; n++) {
        deltaAs = l + d - 1 - n;
        la = l - n;
        na = n;
        if (abs(delta - deltaAs) < EPS) continue;
        ret += pow(4 * r, na) * rCoef->RII(l, n) / (delta - deltaAs) * HRecursion(deltaAs + na, la, r, eta, order - na);
    }

    // type III
    for (int n = 1; n + n <= order; n++) {
        deltaAs = d / 2 - n;
        la = l;
        na = 2 * n;
        if (abs(delta - deltaAs) < EPS) continue;
        ret += pow(4 * r, na) * rCoef->RIII(l, n) / (delta - deltaAs) * HRecursion(deltaAs + na, la, r, eta, order - na);
    }

    if (idelta != std::numeric_limits<int>::max()) {
        hCache.Set(idelta, l, order, ret);
    }

    return ret;
}

#if CONFORMAL_BLOCK_NORMALIZATION == 1

float_type ConformalBlockScalars::NormalizationFactor(int l) {
    assert(l >= 0);
    assert(l < 63);

    float_type ret = (float_type)(((i64)1)<<l);
    if (l % 2 == 1) ret = -ret;
    if (abs(d - 2) < EPS) {
        return ret;
    } else {
        return ret * rising_factorial(d/2 - 1.0, l) / rising_factorial(d - 2.0, l);
    }
}

#endif

float_type ConformalBlockScalars::evaluate(float_type delta, int l, float_type r, float_type eta, int order) {
    hCache.ClearIfNew(r, eta);
#if CONFORMAL_BLOCK_NORMALIZATION == 1
    float_type prefactor = pow(r, delta) * NormalizationFactor(l);
    float_type hr = HRecursion(delta, l, r, eta, order);
    return  prefactor * hr;
#else
    return pow(4 * r, delta) * HRecursion(delta, l, r, eta, order);
#endif

}

float_type ConformalBlockScalars::dDelta(float_type delta, int l, float_type r, float_type eta, int order) {
#if CONFORMAL_BLOCK_NORMALIZATION == 1
    float_type ret = log(r) * evaluate(delta, l, r, eta, order);
    ret += pow(r, delta) * NormalizationFactor(l) * dHdDelta(delta, l, r, eta, order);
#else
    float_type ret = log(4 * r) * evaluate(delta, l, r, eta, order);
    ret += pow(4 * r, delta) * dHdDelta(delta, l, r, eta, order);
#endif
    return ret;
}

float_type ConformalBlockScalars::dHdDelta(float_type delta, int l, float_type r, float_type eta, int order) {
    float_type deltaAs, ret = 0.0;
    int la, na;

    // type I
    for (int n = 1; n <= order; n++) {
        deltaAs = 1 - l - n;
        la = l + n;
        na = n;
        // this if statement is only for the following case:
        // when the exchange operator is identity, i.e, delta==l==0,
        // then delta - deltaAs can be zero.
        if (abs(delta - deltaAs) < EPS) continue;
        ret -= pow(4 * r, na) * rCoef->RI(n) / ((delta - deltaAs) * (delta - deltaAs)) * HRecursion(deltaAs + na, la, r, eta, order - na);
    }

    // type II
    for (int n = 1; n <= order && n <= l; n++) {
        deltaAs = l + d - 1 - n;
        la = l - n;
        na = n;
        if (abs(delta - deltaAs) < EPS) continue;
        ret -= pow(4 * r, na) * rCoef->RII(l, n) / ((delta - deltaAs) * (delta - deltaAs)) * HRecursion(deltaAs + na, la, r, eta, order - na);
    }

    // type III
    for (int n = 1; n + n <= order; n++) {
        deltaAs = d / 2 - n;
        la = l;
        na = 2 * n;
        if (abs(delta - deltaAs) < EPS) continue;
        ret -= pow(4 * r, na) * rCoef->RIII(l, n) / ((delta - deltaAs) * (delta - deltaAs)) * HRecursion(deltaAs + na, la, r, eta, order - na);
    }

    return ret;
}

float_type ConformalBlockScalars::dDelta12(float_type delta, int l, float_type r, float_type eta, int order) {
    hDiff12Cache.ClearIfNew(r, eta);
    hCache.ClearIfNew(r, eta);
#if CONFORMAL_BLOCK_NORMALIZATION == 1
    return pow(r, delta) * NormalizationFactor(l) * dHdDelta12(delta, l, r, eta, order);
#else
    return pow(4 * r, delta) * dHdDelta12(delta, l, r, eta, order);
#endif

}

float_type ConformalBlockScalars::dHdDelta12(float_type delta, int l, float_type r, float_type eta, int order) {
    int idelta = std::numeric_limits<int>::max();
    float_type ret;
    if (IsInteger(delta)) {
        idelta = (int)floor(delta + EPS);
        if (hDiff12Cache.Get(idelta, l, order, &ret)) return ret;
    }

    ret = HInfinity_dDelta12(l, r, eta);
    if (order == 0) return ret;

    float_type deltaAs;
    int la, na;

    // type I
    for (int n = 1; n <= order; n++) {
        deltaAs = 1 - l - n;
        la = l + n;
        na = n;
        // this if statement is only for the following case:
        // when the exchange operator is identity, i.e, delta==l==0,
        // then delta - deltaAs can be zero.
        if (abs(delta - deltaAs) < EPS) continue;
        ret += pow(4 * r, na) / (delta - deltaAs) * (dHdDelta12(deltaAs + na, la, r, eta, order - na) * rCoef->RI(n) + HRecursion(deltaAs + na, la, r, eta, order - na) * rCoef->RI_dDelta12(n));
    }

    // type II
    for (int n = 1; n <= order && n <= l; n++) {
        deltaAs = l + d - 1 - n;
        la = l - n;
        na = n;
        if (abs(delta - deltaAs) < EPS) continue;
        ret += pow(4 * r, na) / (delta - deltaAs) * (dHdDelta12(deltaAs + na, la, r, eta, order - na) * rCoef->RII(l, n)+ HRecursion(deltaAs + na, la, r, eta, order - na) * rCoef->RII_dDelta12(l,n));
    }

    // type III
    for (int n = 1; n + n <= order; n++) {
        deltaAs = d / 2 - n;
        la = l;
        na = 2 * n;
        if (abs(delta - deltaAs) < EPS) continue;
        ret += pow(4 * r, na) / (delta - deltaAs) * (dHdDelta12(deltaAs + na, la, r, eta, order - na) * rCoef->RIII(l, n)+ HRecursion(deltaAs + na, la, r, eta, order - na) * rCoef->RIII_dDelta12(l,n));
    }

    if (idelta != std::numeric_limits<int>::max()) {
        hDiff12Cache.Set(idelta, l, order, ret);
    }

    return ret;
}

float_type ConformalBlockScalars::dDelta34(float_type delta, int l, float_type r, float_type eta, int order) {
    hDiff34Cache.ClearIfNew(r, eta);
    hCache.ClearIfNew(r, eta);
#if CONFORMAL_BLOCK_NORMALIZATION == 1
    return pow(r, delta) * NormalizationFactor(l) * dHdDelta34(delta, l, r, eta, order);
#else
    return pow(4 * r, delta) * dHdDelta34(delta, l, r, eta, order);
#endif
}

float_type ConformalBlockScalars::dHdDelta34(float_type delta, int l, float_type r, float_type eta, int order) {
    int idelta = std::numeric_limits<int>::max();
    float_type ret;
    if (IsInteger(delta)) {
        idelta = (int)floor(delta + EPS);
        if (hDiff34Cache.Get(idelta, l, order, &ret)) return ret;
    }

    ret = HInfinity_dDelta34(l, r, eta);
    if (order == 0) return ret;
    float_type deltaAs;
    int la, na;

    // type I
    for (int n = 1; n <= order; n++) {
        deltaAs = 1 - l - n;
        la = l + n;
        na = n;
        // this if statement is only for the following case:
        // when the exchange operator is identity, i.e, delta==l==0,
        // then delta - deltaAs can be zero.
        if (abs(delta - deltaAs) < EPS) continue;
        ret += pow(4 * r, na) / (delta - deltaAs) * (dHdDelta34(deltaAs + na, la, r, eta, order - na) * rCoef->RI(n) + HRecursion(deltaAs + na, la, r, eta, order - na) * rCoef->RI_dDelta34(n));
    }

    // type II
    for (int n = 1; n <= order && n <= l; n++) {
        deltaAs = l + d - 1 - n;
        la = l - n;
        na = n;
        if (abs(delta - deltaAs) < EPS) continue;
        ret += pow(4 * r, na) / (delta - deltaAs) * (dHdDelta34(deltaAs + na, la, r, eta, order - na) * rCoef->RII(l, n)+ HRecursion(deltaAs + na, la, r, eta, order - na) * rCoef->RII_dDelta34(l,n));
    }

    // type III
    for (int n = 1; n + n <= order; n++) {
        deltaAs = d / 2 - n;
        la = l;
        na = 2 * n;
        if (abs(delta - deltaAs) < EPS) continue;
        ret += pow(4 * r, na) / (delta - deltaAs) * (dHdDelta34(deltaAs + na, la, r, eta, order - na) * rCoef->RIII(l, n)+ HRecursion(deltaAs + na, la, r, eta, order - na) * rCoef->RIII_dDelta34(l,n));
    }

    if (idelta != std::numeric_limits<int>::max()) {
        hDiff34Cache.Set(idelta, l, order, ret);
    }

    return ret;
}


#include <iostream>
#include <fstream>
#include <sstream>
#include <stdio.h>
#include <assert.h>
#include <math.h>
#include <cmath>
#include <Eigen/IterativeLinearSolvers>
#include <Eigen/SparseQR>
#include <Eigen/SparseCholesky>
#include <Eigen/OrderingMethods>
#include "mmio.hpp"
#include "cxxopts.hpp"
#include "tree.h"
#include "partition.h"
#include "util.h"
#include "is.h"

using namespace Eigen;
using namespace std;

typedef SparseMatrix<double, 0, int> SpMat;

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

     cxxopts::Options options("spaQR", "Sparsified QR for general sparse matrices");
     options.add_options()
        ("help", "Print help")
        ("m,matrix", "Matrix file in martrix market format", cxxopts::value<string>())
        ("l,lvl","Number of levels", cxxopts::value<int>())
        // Geometry
        ("coordinates", "Coordinates MM array file. If provided, will do a geometric partitioning.", cxxopts::value<string>())
        ("n,coordinates_n", "If provided with -n, will use a tensor n^d & geometric partitioning. Overwrites --coordinates", cxxopts::value<int>()->default_value("-1"))
        ("d,coordinates_d", "If provided with -d, will use a tensor n^d & geometric partitioning. Overwrites --coordinates", cxxopts::value<int>()->default_value("-1"))
        // Partition
        ("hsl","Use bipartite matching routine from HSL to perform row ordering. Use only if compiled with USE_HSL=1 flag. Default false.", cxxopts::value<int>()->default_value("0"))
        // Sparsification
        ("t,tol", "Tolerance", cxxopts::value<double>()->default_value("1e-1"))
        ("skip", "Skip sparsification", cxxopts::value<int>()->default_value("0"))
        ("order", "Specify order of scheme (1, 1.5) Default order=1. Use 1.5 to get a more accurate scheme but uses more memory.", cxxopts::value<float>()->default_value("1"))
        ("scale", "Do scaling. Default true.", cxxopts::value<int>()->default_value("1"))
        // Iterative method
        // ("solver","Wether to use CG or GMRES or CGLS. Default GMRES for square matrices and CGLS for rectangular.", cxxopts::value<string>()->default_value("GMRES"))
        ("i,iterations","Iterative solver iterations", cxxopts::value<int>()->default_value("300"))
        ("rhs", "Provide RHS to solve in matrix market format", cxxopts::value<string>())
        ("res", "Desired relative residual for the iterative solver. Default 1e-12", cxxopts::value<double>()->default_value("1e-12"))

        // Use solvers from Eigen library
        ("useEigenLSCG","If true, run CGLS scheme with standard diagonal preconditioner from Eigen library. Default false.", cxxopts::value<int>()->default_value("0"))
        ("useEigenQR","If true, run SparseQR with default ordering from Eigen library. Default false.", cxxopts::value<int>()->default_value("0"))
        ("useCholesky","If true, run SimplicialLDL^T with AMDOrdering from Eigen library. Default false.", cxxopts::value<int>()->default_value("0"));

    auto result = options.parse(argc, argv);
    if (result.count("help")) {
        cout << options.help({"", "Group"}) << endl;
        exit(0);
    }

    if ( (!result.count("matrix"))  ) {
        cout << "--matrix is mandatory" << endl;
        exit(0);
    }

    string matrix = result["matrix"].as<string>();
    int nlevels;

    // Geometry
    string coordinates;
    int cn = result["coordinates_n"].as<int>();
    int cd = result["coordinates_d"].as<int>();
    bool geo_file = (result.count("coordinates") > 0);
    if ( (cn == -1 && cd >= 0) || (cd == -1 && cn >= 0) ) {
        cout << "cn and cd should be both provided, or none should be provided" << endl;
        return 1;
    }
    bool geo_tensor = (cn >= 0 && cd >= 0);
    bool geo = geo_file || geo_tensor;
    if(geo_file) {
        coordinates = result["coordinates"].as<string>();
    }

    // Load matrix
    SpMat A = mmio::sp_mmread<double, int>(matrix);

    if (A.rows() < A.cols()){
        cout << " <<< Warning!!! nrows < ncols. Finding QR on A.transpose() instead. " << endl;
        SpMat T = A.transpose();
        A = T;
    }

    int nrows = A.rows();
    int ncols = A.cols();
    cout << "Matrix " << matrix << " with " << nrows << " rows,  " << ncols << " columns loaded" << endl;

    // Iterative method
    bool useGMRES = (nrows == ncols) ? true : false;
    bool useCGLS = (nrows > ncols) ? true : false;
    int iterations = result["iterations"].as<int>();

    bool useEigenLSCG = result["useEigenLSCG"].as<int>();
    bool useEigenQR = result["useEigenQR"].as<int>();
    bool useCholesky = result["useCholesky"].as<int>();

    double residual = result["res"].as<double>();

    if ( (!result.count("lvl"))  ) {
        cout << "--Levels not provided" << endl;
        nlevels = ceil(log2(ncols/64));
        cout << " Levels set to ceil(log2(ncols/64)) =  " << nlevels << endl;
    }
    else{
        nlevels = result["lvl"].as<int>();
    }

    // Partition
    int hsl = result["hsl"].as<int>();

    // Sparsification parameters
    int scale = result["scale"].as<int>();
    if (nrows != ncols && scale == 0){
        cout << "Scaling necessary for rectangular matrices" << endl;
        cout << "Setting scale to 1" << endl;
        scale = 1;
    }
    double tol = result["tol"].as<double>();
    float order = result["order"].as<float>();

    // Pre-process matrix to have columns of unit norm (diagonal scaling)
    VectorXd Dentries(A.cols());
    DiagonalMatrix<double, Eigen::Dynamic> D(A.cols());

    auto pstart = wctime();
    for (int i=0; i < A.cols(); ++i) {
        double sum = 0;
       for (SpMat::InnerIterator it(A,i); it; ++it){
            sum += it.value()*it.value();
       }
       Dentries[i] = (double)(1.0/sqrt(sum));
    }

    D = Dentries.asDiagonal();
    A = A*D*10;
    auto pend = wctime();
    cout << "Pre-process time: " << elapsed(pstart, pend) << endl;

    int skip = (tol == 0 ? nlevels-1 : result["skip"].as<int>());

    // Load coordinates ?
    MatrixXd X;
    if(geo_tensor) {
        if(pow(cn, cd) != ncols) {
            cout << "Error: cn and cd where both provided, but cn^cd != N where A is NxN" << endl;
            return 1;
        }
        X = linspace_nd(cn, cd);
        cout << "Tensor coordinate matrix of size " << cn << "^" << cd << " built" << endl;
    } else if(geo_file) {
        X = mmio::dense_mmread<double>(coordinates);
        cout << "Coordinate file " << X.rows() << "x" << X.cols() << " loaded from " << coordinates << endl;
        if(X.cols() != ncols) {
            cout << "Error: coordinate file should hold a matrix of size d x N" << endl;
        }
    }

    // Tree
    Tree t(nlevels, skip);
    t.set_scale(scale);
    t.set_tol(tol);
    t.set_order(order);
    t.set_hsl(hsl);

    if (nrows == ncols) t.set_square(1); // default 0
    if(geo) t.set_Xcoo(&X);

    if (useEigenLSCG) {
        VectorXd x = VectorXd::Zero(ncols);
        VectorXd b = random(nrows,2021);

        LeastSquaresConjugateGradient<SpMat, LeastSquareDiagonalPreconditioner<double>> lscg;
        lscg.compute(A);

        LeastSquareDiagonalPreconditioner<double> diag_precond = lscg.preconditioner();

        timer cgls0 = wctime();
        auto iter = lscg_eigen(A, b, x, diag_precond, iterations, residual, true);
        timer cgls1 = wctime();

        cout << "CGLS error: " << scientific <<  (A.transpose()*(A*x-b)).norm() / (A.transpose()*b).norm() << endl;
        cout << "  CGLS: " << elapsed(cgls0, cgls1) << " s." << endl;
        cout << "<<<<CGLS=" << iter << endl;
        return 0;
    }

    if (useEigenQR){
        VectorXd x = VectorXd::Zero(ncols);
        VectorXd b = random(nrows,2021);

        A.makeCompressed();
        SparseQR<SpMat, COLAMDOrdering<int>> eigenQR;

        eigenQR.setPivotThreshold(1e-14);
        cout << "\n <<<< Using Eigen SparseQR routine..." << endl;
        timer qr0 = wctime();
        eigenQR.compute(A);
        timer qr1 = wctime();
        cout << "Time to factorize: " << elapsed(qr0, qr1) << " s." << endl;

        timer qrs0 = wctime();
        x = eigenQR.solve(b);
        timer qrs1 = wctime();
        cout << "Time to solve: " << elapsed(qrs0, qrs1) << " s." << endl;
        cout << "Error: " << scientific << (A.transpose()*(A*x-b)).norm() / (A.transpose()*b).norm() << endl;

        return 0;
    }

    if (useCholesky){
        VectorXd x = VectorXd::Zero(ncols);
        VectorXd b = random(nrows,2021);

        VectorXd Atb = A.transpose()*b;
        SpMat AtA = A.transpose()*A;

        AtA.makeCompressed();
        SimplicialLLT<SpMat, Lower, AMDOrdering<int> > eigenCholesky;
        cout << "\n <<<< Using Eigen's SimplicalLL^T routine..." << endl;

        timer qr0 = wctime();
        eigenCholesky.compute(AtA);
        timer qr1 = wctime();
        cout << "Time to factorize: " << elapsed(qr0, qr1) << " s." << endl;

        timer qrs0 = wctime();
        x = eigenCholesky.solve(Atb);
        timer qrs1 = wctime();

        cout << "Time to solve: " << elapsed(qrs0, qrs1) << " s." << endl;
        cout << "Error: " << scientific << (A.transpose()*(A*x-b)).norm() / (A.transpose()*b).norm() << endl;

        return 0;
    }

    // Partition
    t.partition(A);
    // Setup
    t.assemble(A);

    // Factorize
    int err = t.factorize();

    if (!err)
    // Run one solve
    {
         // Random b
        {
            VectorXd b = random(nrows, 2021);
            VectorXd bcopy = b;
            VectorXd x(ncols, 1);
            x.setZero();
            timer tsolv_0 = wctime();

            if (nrows == ncols){
                t.solve(bcopy, x);
                timer tsolv = wctime();
                cout << "<<<<tsolv=" << elapsed(tsolv_0, tsolv) << endl;
                cout << "One-time solve (Random b):" << endl;
                cout << "<<<<|(Ax-b)|/|b| : " << scientific <<  ((A*x-b)).norm() / (b).norm() << endl;
            }
            else {
                t.solve_nrml(A.transpose()*bcopy, x);
                timer tsolv = wctime();
                cout << "<<<<tsolv=" << elapsed(tsolv_0, tsolv) << endl;
                cout << "One-time solve (Random b):" << endl;
                cout << "<<<<|A'(Ax-b)|/|A'b| : " << scientific <<  (A.transpose()*(A*x-b)).norm() / (A.transpose()*b).norm() << endl;
            }
        }
    }

    bool verb = false;
    int iter = 0;
    if (!err)
    {
        VectorXd x = VectorXd::Zero(ncols);
        VectorXd b;
        if ((!result.count("rhs"))){
            b = random(nrows,2021);
        }
        else {
            string rhs_file = result["rhs"].as<string>();
            b = mmio::vector_mmread<double>(rhs_file);
        }
        VectorXd bcopy = b;

        if(useGMRES) {
            timer gmres0 = wctime();
            iter = gmres(A, b, x, t, iterations, iterations, residual, verb);
            timer gmres1 = wctime();
            cout << "GMRES: #iterations: " << iter << ", residual |Ax-b|/|b|: " << (A*x-b).norm() / b.norm() << endl;
            cout << "  GMRES: " << elapsed(gmres0, gmres1) << " s." << endl;
            cout << "<<<<GMRES=" << iter << endl;
        }
        else if(useCGLS){
            timer cg0 = wctime();

            Index max_iters = (long)iterations;
            iter = cgls(A, b, x, t, max_iters, residual, verb);
            cout << "CGLS: #iterations: " << iter << ", residual |A'(Ax-b)|/|A'(b)|: " << (A.transpose()*(A*x-b)).norm() / (A.transpose()*b).norm() << endl;
            timer cg1 = wctime();
            cout << "  CGLS: " << elapsed(cg0, cg1) << " s." << endl;
            cout << "<<<<CGLS=" << iter << endl;
        }
    }

  return 0;
}


///////////////////////////////////////////////////////////////////////////////
// importance_sampling::scale_to_finite_sum.hpp                              //
//                                                                           //
//  Copyright 2009 Erwann Rogard. 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)         //
///////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_DETAIL_IMPORTANCE_SAMPLING_WEIGHTS_SCALE_TO_FINITE_SUM_HPP_ER_2009
#define BOOST_STATISTICS_DETAIL_IMPORTANCE_SAMPLING_WEIGHTS_SCALE_TO_FINITE_SUM_HPP_ER_2009
#include <numeric>
#include <boost/lambda/lambda.hpp>
#include <boost/range.hpp>
#include <boost/statistics/detail/importance_sampling/weights/find_scale_to_finite_sum.hpp>

namespace boost{
namespace statistics{
namespace detail{
namespace importance_sampling{

    // Scales each element [b,e) by the smallest factor, c, such that the sum
    // is finite.
    //
    // [ Warning ] c is not insensitive to permutations of [b,e), due
    // to non-associativity in the fp system
    template<typename InIt>
    typename iterator_value<InIt>::type
    scale_to_finite_sum(InIt b,InIt e,
        typename iterator_value<InIt>::type low,
        typename iterator_value<InIt>::type high
    );

    template<typename InIt>
    typename iterator_value<InIt>::type
    scale_to_finite_sum(InIt b,InIt e);

    // Implementation //

    template<typename InIt>
    typename iterator_value<InIt>::type
    scale_to_finite_sum(InIt b,InIt e,
        typename iterator_value<InIt>::type low,
        typename iterator_value<InIt>::type high
    ){
        typedef typename iterator_value<InIt>::type val_;
        val_ c = find_scale_finite_sum(
            b,e,low,high
        );
        std::transform(
            b,
            e,
            b,
            boost::lambda::_1 / c
        );
        return c;
    }

    template<typename InIt>
    typename iterator_value<InIt>::type
    scale_to_finite_sum(InIt b,InIt e){
        typedef typename iterator_value<InIt>::type val_;
        val_ c = find_scale_to_finite_sum(b,e);
        std::transform(
            b,
            e,
            b,
            boost::lambda::_1 / c
        );
        return c;
    }

}// importance_weights
}// detail
}// statistics
}// boost

#endif


//Author: Dr. Shantanu Shahane
#ifndef postprocessing_functions_H_ /* Include guard */
#define postprocessing_functions_H_
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <float.h>
#include <string.h>
#include <iostream>
#include <vector>
#include <Eigen/Dense>
#include <unsupported/Eigen/SparseExtra>
#include <Eigen/SparseLU>
#include <Eigen/OrderingMethods>
#include <Eigen/Core>
#include <unistd.h>
#include "_hypre_utilities.h"
#include "HYPRE_krylov.h"
#include "HYPRE.h"
#include "HYPRE_parcsr_ls.h"
#include "general_functions.hpp"
#include "class.hpp"
using namespace std;

double calc_boundary_flux(POINTS &points, PARAMETERS &parameters, Eigen::VectorXd &phi_x, Eigen::VectorXd &phi_y, int bc_tag);

double calc_boundary_flux(POINTS &points, PARAMETERS &parameters, Eigen::VectorXd &phi_x, Eigen::VectorXd &phi_y, Eigen::VectorXd &phi_z, int bc_tag);

void write_simulation_details(POINTS &points, CLOUD &cloud, PARAMETERS &parameters);

void write_iteration_details(PARAMETERS &parameters);

void calc_navier_stokes_residuals_2D(POINTS &points, PARAMETERS &parameters, Eigen::VectorXd &u, Eigen::VectorXd &v, Eigen::VectorXd &p);

void calc_navier_stokes_residuals_2D(POINTS &points, PARAMETERS &parameters, Eigen::VectorXd &u, Eigen::VectorXd &v, Eigen::VectorXd &p, Eigen::VectorXd &body_force_x, Eigen::VectorXd &body_force_y);

void calc_navier_stokes_errors_2D(POINTS &points, PARAMETERS &parameters, Eigen::VectorXd &u_ana, Eigen::VectorXd &v_ana, Eigen::VectorXd &p_ana, Eigen::VectorXd &u_num, Eigen::VectorXd &v_num, Eigen::VectorXd &p_num);

void calc_navier_stokes_residuals_3D(POINTS &points, PARAMETERS &parameters, Eigen::VectorXd &u, Eigen::VectorXd &v, Eigen::VectorXd &w, Eigen::VectorXd &p);

void calc_navier_stokes_residuals_3D(POINTS &points, PARAMETERS &parameters, Eigen::VectorXd &u, Eigen::VectorXd &v, Eigen::VectorXd &w, Eigen::VectorXd &p, Eigen::VectorXd &body_force_x, Eigen::VectorXd &body_force_y, Eigen::VectorXd &body_force_z);

void calc_navier_stokes_errors_3D(POINTS &points, PARAMETERS &parameters, Eigen::VectorXd &u_ana, Eigen::VectorXd &v_ana, Eigen::VectorXd &w_ana, Eigen::VectorXd &p_ana, Eigen::VectorXd &u_num, Eigen::VectorXd &v_num, Eigen::VectorXd &w_num, Eigen::VectorXd &p_num);

void write_navier_stokes_errors_2D(POINTS &points, PARAMETERS &parameters, Eigen::VectorXd &u_ana, Eigen::VectorXd &v_ana, Eigen::VectorXd &p_ana, Eigen::VectorXd &u_num, Eigen::VectorXd &v_num, Eigen::VectorXd &p_num);

void write_navier_stokes_residuals_2D(POINTS &points, PARAMETERS &parameters, Eigen::VectorXd &u, Eigen::VectorXd &v, Eigen::VectorXd &p, string output_file_suffix);

void write_tecplot_steady_variables(POINTS &points, PARAMETERS &parameters, vector<string> &variable_names, vector<Eigen::VectorXd *> &variable_pointers);

void write_tecplot_temporal_variables_header(POINTS &points, PARAMETERS &parameters, vector<string> &variable_names);

void write_tecplot_temporal_variables(POINTS &points, PARAMETERS &parameters, vector<string> &variable_names, vector<Eigen::VectorXd *> &variable_pointers, int it);

void write_csv_xyz(vector<double> &vect, PARAMETERS &parameters, const char *file_name);

void write_csv_temporal_data_init(int size, const char *file_name);

void write_csv_temporal_data(Eigen::VectorXd &data, double time, const char *file_name);

#endif


// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2016 Michael Rabinovich
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#include "flip_avoiding_line_search.h"
#include "line_search.h"
#include "PI.h"

#include <Eigen/Dense>
#include <vector>

namespace igl
{
  namespace flip_avoiding
  {
    //---------------------------------------------------------------------------
    // x - array of size 3
    // In case 3 real roots: => x[0], x[1], x[2], return 3
    //         2 real roots: x[0], x[1],          return 2
    //         1 real root : x[0], x[1] ± i*x[2], return 1
    // http://math.ivanovo.ac.ru/dalgebra/Khashin/poly/index.html
    IGL_INLINE int SolveP3(std::vector<double>& x,double a,double b,double c)
    { // solve cubic equation x^3 + a*x^2 + b*x + c
      using namespace std;
      double a2 = a*a;
        double q  = (a2 - 3*b)/9;
      double r  = (a*(2*a2-9*b) + 27*c)/54;
        double r2 = r*r;
      double q3 = q*q*q;
      double A,B;
        if(r2<q3)
        {
          double t=r/sqrt(q3);
          if( t<-1) t=-1;
          if( t> 1) t= 1;
          t=acos(t);
          a/=3; q=-2*sqrt(q);
          x[0]=q*cos(t/3)-a;
          x[1]=q*cos((t+(2*igl::PI))/3)-a;
          x[2]=q*cos((t-(2*igl::PI))/3)-a;
          return(3);
        }
        else
        {
          A =-pow(fabs(r)+sqrt(r2-q3),1./3);
          if( r<0 ) A=-A;
          B = A==0? 0 : q/A;

          a/=3;
          x[0] =(A+B)-a;
          x[1] =-0.5*(A+B)-a;
          x[2] = 0.5*sqrt(3.)*(A-B);
          if(fabs(x[2])<1e-14)
          {
            x[2]=x[1]; return(2);
          }
          return(1);
        }
    }

    IGL_INLINE double get_smallest_pos_quad_zero(double a,double b, double c)
    {
      using namespace std;
      double t1, t2;
      if(std::abs(a) > 1.0e-10)
      {
        double delta_in = pow(b, 2) - 4 * a * c;
        if(delta_in <= 0)
        {
          return INFINITY;
        }

        double delta = sqrt(delta_in); // delta >= 0
        if(b >= 0) // avoid subtracting two similar numbers
        {
          double bd = - b - delta;
          t1 = 2 * c / bd;
          t2 = bd / (2 * a);
        }
        else
        {
          double bd = - b + delta;
          t1 = bd / (2 * a);
          t2 = (2 * c) / bd;
        }

        assert (std::isfinite(t1));
        assert (std::isfinite(t2));

        if(a < 0) std::swap(t1, t2); // make t1 > t2
        // return the smaller positive root if it exists, otherwise return infinity
        if(t1 > 0)
        {
          return t2 > 0 ? t2 : t1;
        }
        else
        {
          return INFINITY;
        }
      }
      else
      {
        if(b == 0) return INFINITY; // just to avoid divide-by-zero
        t1 = -c / b;
        return t1 > 0 ? t1 : INFINITY;
      }
    }

    IGL_INLINE double get_min_pos_root_2D(const Eigen::MatrixXd& uv,
                                          const Eigen::MatrixXi& F,
                                          Eigen::MatrixXd& d,
                                          int f)
    {
      using namespace std;
    /*
          Finding the smallest timestep t s.t a triangle get degenerated (<=> det = 0)
          The following code can be derived by a symbolic expression in matlab:

          Symbolic matlab:
          U11 = sym('U11');
          U12 = sym('U12');
          U21 = sym('U21');
          U22 = sym('U22');
          U31 = sym('U31');
          U32 = sym('U32');

          V11 = sym('V11');
          V12 = sym('V12');
          V21 = sym('V21');
          V22 = sym('V22');
          V31 = sym('V31');
          V32 = sym('V32');

          t = sym('t');

          U1 = [U11,U12];
          U2 = [U21,U22];
          U3 = [U31,U32];

          V1 = [V11,V12];
          V2 = [V21,V22];
          V3 = [V31,V32];

          A = [(U2+V2*t) - (U1+ V1*t)];
          B = [(U3+V3*t) - (U1+ V1*t)];
          C = [A;B];

          solve(det(C), t);
          cf = coeffs(det(C),t); % Now cf(1),cf(2),cf(3) holds the coefficients for the polynom. at order c,b,a
        */

      int v1 = F(f,0); int v2 = F(f,1); int v3 = F(f,2);
      // get quadratic coefficients (ax^2 + b^x + c)
      const double& U11 = uv(v1,0);
      const double& U12 = uv(v1,1);
      const double& U21 = uv(v2,0);
      const double& U22 = uv(v2,1);
      const double& U31 = uv(v3,0);
      const double& U32 = uv(v3,1);

      const double& V11 = d(v1,0);
      const double& V12 = d(v1,1);
      const double& V21 = d(v2,0);
      const double& V22 = d(v2,1);
      const double& V31 = d(v3,0);
      const double& V32 = d(v3,1);

      double a = V11*V22 - V12*V21 - V11*V32 + V12*V31 + V21*V32 - V22*V31;
      double b = U11*V22 - U12*V21 - U21*V12 + U22*V11 - U11*V32 + U12*V31 + U31*V12 - U32*V11 + U21*V32 - U22*V31 - U31*V22 + U32*V21;
      double c = U11*U22 - U12*U21 - U11*U32 + U12*U31 + U21*U32 - U22*U31;

      return get_smallest_pos_quad_zero(a,b,c);
    }

    IGL_INLINE double get_min_pos_root_3D(const Eigen::MatrixXd& uv,
                                          const Eigen::MatrixXi& F,
                                          Eigen::MatrixXd& direc,
                                          int f)
    {
      using namespace std;
      /*
          Searching for the roots of:
            +-1/6 * |ax ay az 1|
                    |bx by bz 1|
                    |cx cy cz 1|
                    |dx dy dz 1|
          Every point ax,ay,az has a search direction a_dx,a_dy,a_dz, and so we add those to the matrix, and solve the cubic to find the step size t for a 0 volume
          Symbolic matlab:
            syms a_x a_y a_z a_dx a_dy a_dz % tetrahedera point and search direction
            syms b_x b_y b_z b_dx b_dy b_dz
            syms c_x c_y c_z c_dx c_dy c_dz
            syms d_x d_y d_z d_dx d_dy d_dz
            syms t % Timestep var, this is what we're looking for

            a_plus_t = [a_x,a_y,a_z] + t*[a_dx,a_dy,a_dz];
            b_plus_t = [b_x,b_y,b_z] + t*[b_dx,b_dy,b_dz];
            c_plus_t = [c_x,c_y,c_z] + t*[c_dx,c_dy,c_dz];
            d_plus_t = [d_x,d_y,d_z] + t*[d_dx,d_dy,d_dz];

            vol_mat = [a_plus_t,1;b_plus_t,1;c_plus_t,1;d_plus_t,1]
            //cf = coeffs(det(vol_det),t); % Now cf(1),cf(2),cf(3),cf(4) holds the coefficients for the polynom
            [coefficients,terms] = coeffs(det(vol_det),t); % terms = [ t^3, t^2, t, 1], Coefficients hold the coeff we seek
      */
      int v1 = F(f,0); int v2 = F(f,1); int v3 = F(f,2); int v4 = F(f,3);
      const double& a_x = uv(v1,0);
      const double& a_y = uv(v1,1);
      const double& a_z = uv(v1,2);
      const double& b_x = uv(v2,0);
      const double& b_y = uv(v2,1);
      const double& b_z = uv(v2,2);
      const double& c_x = uv(v3,0);
      const double& c_y = uv(v3,1);
      const double& c_z = uv(v3,2);
      const double& d_x = uv(v4,0);
      const double& d_y = uv(v4,1);
      const double& d_z = uv(v4,2);

      const double& a_dx = direc(v1,0);
      const double& a_dy = direc(v1,1);
      const double& a_dz = direc(v1,2);
      const double& b_dx = direc(v2,0);
      const double& b_dy = direc(v2,1);
      const double& b_dz = direc(v2,2);
      const double& c_dx = direc(v3,0);
      const double& c_dy = direc(v3,1);
      const double& c_dz = direc(v3,2);
      const double& d_dx = direc(v4,0);
      const double& d_dy = direc(v4,1);
      const double& d_dz = direc(v4,2);

      // Find solution for: a*t^3 + b*t^2 + c*d +d = 0
      double a = a_dx*b_dy*c_dz - a_dx*b_dz*c_dy - a_dy*b_dx*c_dz + a_dy*b_dz*c_dx + a_dz*b_dx*c_dy - a_dz*b_dy*c_dx - a_dx*b_dy*d_dz + a_dx*b_dz*d_dy + a_dy*b_dx*d_dz - a_dy*b_dz*d_dx - a_dz*b_dx*d_dy + a_dz*b_dy*d_dx + a_dx*c_dy*d_dz - a_dx*c_dz*d_dy - a_dy*c_dx*d_dz + a_dy*c_dz*d_dx + a_dz*c_dx*d_dy - a_dz*c_dy*d_dx - b_dx*c_dy*d_dz + b_dx*c_dz*d_dy + b_dy*c_dx*d_dz - b_dy*c_dz*d_dx - b_dz*c_dx*d_dy + b_dz*c_dy*d_dx;

      double b = a_dy*b_dz*c_x - a_dy*b_x*c_dz - a_dz*b_dy*c_x + a_dz*b_x*c_dy + a_x*b_dy*c_dz - a_x*b_dz*c_dy - a_dx*b_dz*c_y + a_dx*b_y*c_dz + a_dz*b_dx*c_y - a_dz*b_y*c_dx - a_y*b_dx*c_dz + a_y*b_dz*c_dx + a_dx*b_dy*c_z - a_dx*b_z*c_dy - a_dy*b_dx*c_z + a_dy*b_z*c_dx + a_z*b_dx*c_dy - a_z*b_dy*c_dx - a_dy*b_dz*d_x + a_dy*b_x*d_dz + a_dz*b_dy*d_x - a_dz*b_x*d_dy - a_x*b_dy*d_dz + a_x*b_dz*d_dy + a_dx*b_dz*d_y - a_dx*b_y*d_dz - a_dz*b_dx*d_y + a_dz*b_y*d_dx + a_y*b_dx*d_dz - a_y*b_dz*d_dx - a_dx*b_dy*d_z + a_dx*b_z*d_dy + a_dy*b_dx*d_z - a_dy*b_z*d_dx - a_z*b_dx*d_dy + a_z*b_dy*d_dx + a_dy*c_dz*d_x - a_dy*c_x*d_dz - a_dz*c_dy*d_x + a_dz*c_x*d_dy + a_x*c_dy*d_dz - a_x*c_dz*d_dy - a_dx*c_dz*d_y + a_dx*c_y*d_dz + a_dz*c_dx*d_y - a_dz*c_y*d_dx - a_y*c_dx*d_dz + a_y*c_dz*d_dx + a_dx*c_dy*d_z - a_dx*c_z*d_dy - a_dy*c_dx*d_z + a_dy*c_z*d_dx + a_z*c_dx*d_dy - a_z*c_dy*d_dx - b_dy*c_dz*d_x + b_dy*c_x*d_dz + b_dz*c_dy*d_x - b_dz*c_x*d_dy - b_x*c_dy*d_dz + b_x*c_dz*d_dy + b_dx*c_dz*d_y - b_dx*c_y*d_dz - b_dz*c_dx*d_y + b_dz*c_y*d_dx + b_y*c_dx*d_dz - b_y*c_dz*d_dx - b_dx*c_dy*d_z + b_dx*c_z*d_dy + b_dy*c_dx*d_z - b_dy*c_z*d_dx - b_z*c_dx*d_dy + b_z*c_dy*d_dx;

      double c = a_dz*b_x*c_y - a_dz*b_y*c_x - a_x*b_dz*c_y + a_x*b_y*c_dz + a_y*b_dz*c_x - a_y*b_x*c_dz - a_dy*b_x*c_z + a_dy*b_z*c_x + a_x*b_dy*c_z - a_x*b_z*c_dy - a_z*b_dy*c_x + a_z*b_x*c_dy + a_dx*b_y*c_z - a_dx*b_z*c_y - a_y*b_dx*c_z + a_y*b_z*c_dx + a_z*b_dx*c_y - a_z*b_y*c_dx - a_dz*b_x*d_y + a_dz*b_y*d_x + a_x*b_dz*d_y - a_x*b_y*d_dz - a_y*b_dz*d_x + a_y*b_x*d_dz + a_dy*b_x*d_z - a_dy*b_z*d_x - a_x*b_dy*d_z + a_x*b_z*d_dy + a_z*b_dy*d_x - a_z*b_x*d_dy - a_dx*b_y*d_z + a_dx*b_z*d_y + a_y*b_dx*d_z - a_y*b_z*d_dx - a_z*b_dx*d_y + a_z*b_y*d_dx + a_dz*c_x*d_y - a_dz*c_y*d_x - a_x*c_dz*d_y + a_x*c_y*d_dz + a_y*c_dz*d_x - a_y*c_x*d_dz - a_dy*c_x*d_z + a_dy*c_z*d_x + a_x*c_dy*d_z - a_x*c_z*d_dy - a_z*c_dy*d_x + a_z*c_x*d_dy + a_dx*c_y*d_z - a_dx*c_z*d_y - a_y*c_dx*d_z + a_y*c_z*d_dx + a_z*c_dx*d_y - a_z*c_y*d_dx - b_dz*c_x*d_y + b_dz*c_y*d_x + b_x*c_dz*d_y - b_x*c_y*d_dz - b_y*c_dz*d_x + b_y*c_x*d_dz + b_dy*c_x*d_z - b_dy*c_z*d_x - b_x*c_dy*d_z + b_x*c_z*d_dy + b_z*c_dy*d_x - b_z*c_x*d_dy - b_dx*c_y*d_z + b_dx*c_z*d_y + b_y*c_dx*d_z - b_y*c_z*d_dx - b_z*c_dx*d_y + b_z*c_y*d_dx;

      double d = a_x*b_y*c_z - a_x*b_z*c_y - a_y*b_x*c_z + a_y*b_z*c_x + a_z*b_x*c_y - a_z*b_y*c_x - a_x*b_y*d_z + a_x*b_z*d_y + a_y*b_x*d_z - a_y*b_z*d_x - a_z*b_x*d_y + a_z*b_y*d_x + a_x*c_y*d_z - a_x*c_z*d_y - a_y*c_x*d_z + a_y*c_z*d_x + a_z*c_x*d_y - a_z*c_y*d_x - b_x*c_y*d_z + b_x*c_z*d_y + b_y*c_x*d_z - b_y*c_z*d_x - b_z*c_x*d_y + b_z*c_y*d_x;

      if (std::abs(a)<=1.e-10)
      {
        return get_smallest_pos_quad_zero(b,c,d);
      }
      b/=a; c/=a; d/=a; // normalize it all
      std::vector<double> res(3);
      int real_roots_num = SolveP3(res,b,c,d);
      switch (real_roots_num)
      {
        case 1:
          return (res[0] >= 0) ? res[0]:INFINITY;
        case 2:
        {
          double max_root = std::max(res[0],res[1]); double min_root = std::min(res[0],res[1]);
          if (min_root > 0) return min_root;
          if (max_root > 0) return max_root;
          return INFINITY;
        }
        case 3:
        default:
        {
          std::sort(res.begin(),res.end());
          if (res[0] > 0) return res[0];
          if (res[1] > 0) return res[1];
          if (res[2] > 0) return res[2];
          return INFINITY;
        }
      }
    }

    IGL_INLINE double compute_max_step_from_singularities(const Eigen::MatrixXd& uv,
                                                          const Eigen::MatrixXi& F,
                                                          Eigen::MatrixXd& d)
    {
      using namespace std;
      double max_step = INFINITY;

      // The if statement is outside the for loops to avoid branching/ease parallelizing
      if (uv.cols() == 2)
      {
        for (int f = 0; f < F.rows(); f++)
        {
          double min_positive_root = get_min_pos_root_2D(uv,F,d,f);
          max_step = std::min(max_step, min_positive_root);
        }
      }
      else
      { // volumetric deformation
        for (int f = 0; f < F.rows(); f++)
        {
          double min_positive_root = get_min_pos_root_3D(uv,F,d,f);
          max_step = std::min(max_step, min_positive_root);
        }
      }
      return max_step;
    }
  }
}

IGL_INLINE double igl::flip_avoiding_line_search(
  const Eigen::MatrixXi F,
  Eigen::MatrixXd& cur_v,
  Eigen::MatrixXd& dst_v,
  std::function<double(Eigen::MatrixXd&)> energy,
  double cur_energy)
{
  using namespace std;
  Eigen::MatrixXd d = dst_v - cur_v;

  double min_step_to_singularity = igl::flip_avoiding::compute_max_step_from_singularities(cur_v,F,d);
  double max_step_size = std::min(1., min_step_to_singularity*0.8);

  return igl::line_search(cur_v,d,max_step_size, energy, cur_energy);
}

#ifdef IGL_STATIC_LIBRARY
#endif


#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <boost/program_options.hpp>
#include "IsingModel.h"

namespace opt = boost::program_options;

int main(int argc, char** argv) {
    int rows, cols, nT, nH, nR, nS, frames, skip;
    double Ti, Tf, Hi, Hf, J, mu, kB;
    bool sim, vid;

    opt::options_description params("Ising Model Parameters");

    params.add_options()
        ("help,h", "show usage")
        ("rows,r", opt::value< int >(&rows)->default_value(15), "number of rows")
        ("cols,c", opt::value< int >(&cols)->default_value(15), "number of columns")
        ("nR", opt::value< int >(&nR)->default_value(1000), "number of relaxation steps")
        ("nS", opt::value< int >(&nS)->default_value(1000), "sample size")
        ("Ti,i", opt::value<double>(&Ti)->default_value(1.5), "initial temperature")
        ("Tf", opt::value<double>(&Tf)->default_value(3.5), "final temperature")
        ("Hi", opt::value<double>(&Hi)->default_value(0.), "initial magnetic field")
        ("Hf", opt::value<double>(&Hf)->default_value(1.), "final magnetic field")
        ("nT,n", opt::value< int >(&nT)->default_value(1000), "number of time steps")
        ("nH", opt::value< int >(&nH)->default_value(1), "number of magnetic field steps")
        ("frames,f", opt::value< int >(&frames)->default_value(1000), "number of frames")
        ("skip,s", opt::value< int >(&skip)->default_value(1), "number of updates between frames")
        ("J", opt::value<double>(&J)->default_value(1.), "ferromagnetic coupling constant")
        ("kB", opt::value<double>(&kB)->default_value(1.), "Boltzmann's constant")
        ("mu", opt::value<double>(&mu)->default_value(1.), "magnetic moment")
        ("vid", opt::value<bool>(&vid)->default_value(true), "create a visualization of the relaxation algorithm")
        ("sim", opt::value<bool>(&sim)->default_value(true), "run simulation and save results to a compressed numpy (npz) file")
        ;

    opt::variables_map vm;
    opt::store(opt::parse_command_line(argc, argv, params), vm);

    if (vm.count("help")) {
        std::cout << params << std::endl;
        return 1;
    }
    else {
        opt::notify(vm);

        std::cout << "\n2D Ising Model Simulation" << std::endl;
        std::cout << "=========================" << std::endl;

        if (sim) { // sim and vid are mutually exclusive, with sim taking precedence

            std::cout << "Running Simulation:" << std::endl;
            IsingModel ising(rows, cols);
            ising.simulate(Ti, Tf, nT, Hi, Hf, nH, nR, nS);

        }
        else {
            std::cout << "Creating Visualization:" << std::endl;
            IsingModel ising(rows, cols);
            ising.visualize(frames, skip, Ti, Hi);
        }

        std::cout << "\n\nDone!" << std::endl;
        std::cout << "=========================" << std::endl;
        return 0;
    }
}