/*
 *  Copyright (c) 2010, INRIA, Project ALICE
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *  * Neither the name of the ALICE Project-Team nor the names of its
 *  contributors may be used to endorse or promote products derived from this
 *  software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 *  If you modify this software, you should include a notice giving the
 *  name of the person performing the modification, the date of modification,
 *  and the reason for such modification.
 *
 *  Contact: Bruno Levy
 *
 *     Bruno.Levy@inria.fr
 *     http://alice.loria.fr
 *
 *     ALICE Project
 *     INRIA Lorraine,
 *     Campus Scientifique, BP 239
 *     54506 VANDOEUVRE LES NANCY CEDEX
 *     FRANCE
 *
 */

#include <LpCVT/algebra/F_Lp.h>
#include <LpCVT/algebra/voro_func.h>

#ifdef CVT_MULTITHREAD
#include <LpCVT/common/processor.h>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#endif

namespace Geex {

   //==========================================================================

    // Internal version: uses pointers instead of std::vectors for 'sym' and 'C'
    // (used by both monothread and multithread implementations)

    double compute_F_Lp_internal(
        bool volumic,                  // IN: false for surface meshing, true for volume meshing
        unsigned int p,                // IN: Lp norm to be used
        Mesh* mesh,                    // IN: the PLC
        unsigned int nb,               // IN: number of integration simplices
        const int* sym,                      // IN: 10 integers per integration simplex:
                                       //   - index of x0
                                       //   - symbolic representation of C1,C2,C3 (3 integers each)
        const vec3* C,                       // IN: C vertices of integration simplices (3 per integration simplex)
        const std::vector<vec3>& X,    // IN: vertices
        const std::vector<plane3>& Q,  // IN: boundary facets
        const std::vector<mat3>& M,    // IN: anisotropy matrix (one per integration simplex)
        std::vector<double>& g         // OUT: gradient
    ) {
        assert(p >= 2 && p <= 16) ;
        assert((p/2)*2 == p) ;
        double f = 0.0 ;
        if(volumic) {
            switch(p) {
            case 2: {
                VoroFunc< IntegrationSimplex<2,TetVolume> > F ;
                f = F.eval(mesh,nb,sym,C,X,Q,M,g) ;
            } break ;
            case 4: {
                VoroFunc< IntegrationSimplex<4,TetVolume> > F ;
                f = F.eval(mesh,nb,sym,C,X,Q,M,g) ;
            } break ;
            case 6: {
                VoroFunc< IntegrationSimplex<6,TetVolume> > F ;
                f = F.eval(mesh,nb,sym,C,X,Q,M,g) ;
            } break ;
            case 8: {
                VoroFunc< IntegrationSimplex<8,TetVolume> > F ;
                f = F.eval(mesh,nb,sym,C,X,Q,M,g) ;
            } break ;
            case 10: {
                VoroFunc< IntegrationSimplex<10,TetVolume> > F ;
                f = F.eval(mesh,nb,sym,C,X,Q,M,g) ;
            } break ;
            case 12: {
                VoroFunc< IntegrationSimplex<12,TetVolume> > F ;
                f = F.eval(mesh,nb,sym,C,X,Q,M,g) ;
            } break ;
            case 14: {
                VoroFunc< IntegrationSimplex<14,TetVolume> > F ;
                f = F.eval(mesh,nb,sym,C,X,Q,M,g) ;
            } break ;
            case 16: {
                VoroFunc< IntegrationSimplex<16,TetVolume> > F ;
                f = F.eval(mesh,nb,sym,C,X,Q,M,g) ;
            } break ;
            }
        } else {
            switch(p) {
            case 2: {
                VoroFunc< IntegrationSimplex<2,TriArea> > F ;
                f = F.eval(mesh,nb,sym,C,X,Q,M,g) ;
            } break ;
            case 4: {
                VoroFunc< IntegrationSimplex<4,TriArea> > F ;
                f = F.eval(mesh,nb,sym,C,X,Q,M,g) ;
            } break ;
            case 6: {
                VoroFunc< IntegrationSimplex<6,TriArea> > F ;
                f = F.eval(mesh,nb,sym,C,X,Q,M,g) ;
            } break ;
            case 8: {
                VoroFunc< IntegrationSimplex<8,TriArea> > F ;
                f = F.eval(mesh,nb,sym,C,X,Q,M,g) ;
            } break ;
            case 10: {
                VoroFunc< IntegrationSimplex<10,TriArea> > F ;
                f = F.eval(mesh,nb,sym,C,X,Q,M,g) ;
            } break ;
            case 12: {
                VoroFunc< IntegrationSimplex<12,TriArea> > F ;
                f = F.eval(mesh,nb,sym,C,X,Q,M,g) ;
            } break ;
            case 14: {
                VoroFunc< IntegrationSimplex<14,TriArea> > F ;
                f = F.eval(mesh,nb,sym,C,X,Q,M,g) ;
            } break ;
            case 16: {
                VoroFunc< IntegrationSimplex<16,TriArea> > F ;
                f = F.eval(mesh,nb,sym,C,X,Q,M,g) ;
            } break ;
            }
        }
        return f ;
    }

//=====================================================================

#ifdef CVT_MULTITHREAD

    /**
     * Stores all the parameters required to evaluate F_Lp on a set of integration
     * simplices. Manages also a local storage for the gradient.
     */
    class F_Lp_thread {
    public:
        void set_parameters(
            bool volumic_in,                  // IN: false for surface meshing, true for volume meshing
            unsigned int p_in,                // IN: Lp norm to be used
            Mesh* mesh_in,                    // IN: the PLC
            unsigned int nb_in,               // IN: number of integration simplices
            const int* sym_in,                // IN: 10 integers per integration simplex:
                                              //   - index of x0
                                              //   - symbolic representation of C1,C2,C3 (3 integers each)
            const vec3* C_in,                 // IN: C vertices of integration simplices (3 per integration simplex)
            const std::vector<vec3>& X_in,    // IN: vertices
            const std::vector<plane3>& Q_in,  // IN: boundary facets
            const std::vector<mat3>& M_in     // IN: anisotropy matrix (one per integration simplex)
        ) {
            volumic = volumic_in ;
            p = p_in ;
            mesh = mesh_in ;
            nb = nb_in ;
            sym = sym_in ;
            C = C_in ;
            X = &X_in ;
            Q = &Q_in ;
            M = &M_in ;
            g.resize(X->size()*3) ;
        }

        void run() {
            std::fill(g.begin(), g.end(), 0.0) ;
            f = compute_F_Lp_internal(volumic, p, mesh, nb, sym, C, *X, *Q, *M, g) ;
        }

        bool volumic ;
        unsigned int p ;
        Mesh* mesh ;
        unsigned int nb ;
        const int* sym ;
        const vec3* C ;
        const std::vector<vec3>* X ;
        const std::vector<plane3>* Q ;
        const std::vector<mat3>* M ;
        double f ;
        std::vector<double> g ;
    } ;

    /**
     * User API function,
     * multithread implementation.
     * 1) Partitions the set of integration simplices,
     * 2) Calls compute_F_Lp_internal() in parallel,
     * 3) Gathers the result.
     */
    double compute_F_Lp(
        bool volumic,                  // IN: false for surface meshing, true for volume meshing
        unsigned int p,                // IN: Lp norm to be used
        Mesh* mesh,                    // IN: the PLC
        const std::vector<int>& sym,   // IN: 10 integers per integration simplex:
                                       //   - index of x0
                                       //   - symbolic representation of C1,C2,C3 (3 integers each)
        const std::vector<vec3>& C,    // IN: C vertices of integration simplices (3 per integration simplex)
        const std::vector<vec3>& X,    // IN: vertices
        const std::vector<plane3>& Q,  // IN: boundary facets
        const std::vector<mat3>& M,    // IN: anisotropy matrix (one per integration simplex)
        std::vector<double>& g         // OUT: gradient
    ) {
        // Get number of cores from Processor class.
        unsigned int nb_threads = Processor::number_of_cores() ;
        std::cerr << "Evaluating F-Lp, using " << nb_threads << " threads " << std::endl ;
        std::vector<F_Lp_thread> threads(nb_threads) ;

        unsigned int nb_integration_simplices = (unsigned int)sym.size() / 10 ;
        unsigned int remaining = nb_integration_simplices ;
        unsigned int batch_size = nb_integration_simplices / nb_threads ;

        // Partition work (i.e. I and C arrays) into nb_threads blocs
        const int*  cur_I = &sym[0] ;
        const vec3* cur_C = &C[0] ;
        for(unsigned int i=0; i<nb_threads-1; i++) {
            threads[i].set_parameters(volumic, p, mesh, batch_size, cur_I, cur_C, X, Q, M) ;
            cur_I += batch_size * 10 ;
            cur_C += batch_size * 3 ;
            remaining -= batch_size ;
        }
        threads[threads.size()-1].set_parameters(volumic, p, mesh, remaining, cur_I, cur_C, X, Q, M) ;

        // Run the threads in parallel, using boost threads
        //   Note: Intel TBB has lower thread creation overhead (has a thread pool),
        // but we use here boost since LpCVT depends on CGAL that also depends on boost
        // (and for large meshes, thread creation time is negligible).
        boost::thread_group threads_impl ;
        for(unsigned int i=0; i<threads.size(); i++) {
            threads_impl.create_thread(boost::bind(&F_Lp_thread::run, &threads[i])) ;
        }
        threads_impl.join_all() ; // Waits for termination of all threads.

        // Gather the result
        double result = 0 ;
        std::fill(g.begin(), g.end(), 0.0) ;
        for(unsigned int i=0; i<threads.size(); i++) {
            result += threads[i].f ;
            for(unsigned int j=0; j<g.size(); j++) {
                g[j] += threads[i].g[j] ;
            }
        }
        return result ;
    }

#else

    /**
     * User API function,
     * monothread implementation.
     * This is just a wrapper around compute_F_Lp_internal()
     */
    double compute_F_Lp(
        bool volumic,                  // IN: false for surface meshing, true for volume meshing
        unsigned int p,                // IN: Lp norm to be used
        Mesh* mesh,                    // IN: the PLC
        const std::vector<int>& sym,   // IN: 10 integers per integration simplex:
                                       //   - index of x0
                                       //   - symbolic representation of C1,C2,C3 (3 integers each)
        const std::vector<vec3>& C,    // IN: C vertices of integration simplices (3 per integration simplex)
        const std::vector<vec3>& X,    // IN: vertices
        const std::vector<plane3>& Q,  // IN: boundary facets
        const std::vector<mat3>& M,    // IN: anisotropy matrix (one per integration simplex)
        std::vector<double>& g         // OUT: gradient
    ) {
        unsigned int nb = sym.size() / 10 ;
        assert(sym.size() == nb*10) ;
        assert(C.size() == nb*3) ;
        return compute_F_Lp_internal(
            volumic, p, mesh, nb,
            &sym[0], &C[0], X, Q, M, g
        ) ;
    }

#endif

}


//=======================================================================
// Copyright 1997, 1998, 1999, 2000 University of Notre Dame.
// Authors: Andrew Lumsdaine, Lie-Quan Lee, Jeremy G. Siek
//
// 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)
//=======================================================================

#include <boost/config.hpp>
#include <iostream>
#include <algorithm>
#include <boost/graph/adjacency_list.hpp>

using namespace std;
using namespace boost;

/*
  Edge Basics

  This example demonstrates the GGCL Edge interface

  There is not much to the Edge interface. Basically just two
  functions to access the source and target vertex:

  source(e)
  target(e)

  and one associated type for the vertex type:

  edge_traits<Edge>::vertex_type

  Sample output:

  (0,1) (0,2) (0,3) (0,4) (2,0) (2,4) (3,0) (3,1)

 */

template <class Graph>
struct exercise_edge {
  exercise_edge(Graph& g) : G(g) {}

  typedef typename boost::graph_traits<Graph>::edge_descriptor Edge;
  typedef typename boost::graph_traits<Graph>::vertex_descriptor Vertex;
  void operator()(Edge e) const
  {
    //begin
    // Get the associated vertex type out of the edge using the
    // edge_traits class
    // Use the source() and target() functions to access the vertices
    // that belong to Edge e
    Vertex src = source(e, G);
    Vertex targ = target(e, G);

    // print out the vertex id's just because
    cout << "(" << src << "," << targ << ") ";
    //end
  }

  Graph& G;
};

int
main()
{
  typedef adjacency_list<> MyGraph;

  typedef pair<int,int> Pair;
  Pair edge_array[8] = { Pair(0,1), Pair(0,2), Pair(0,3), Pair(0,4),
                         Pair(2,0), Pair(3,0), Pair(2,4), Pair(3,1) };

  // Construct a graph using the edge_array (passing in pointers
  // (iterators) to the beginning and end of the array), and
  // specifying the number of vertices as 5
  MyGraph G(5);
  for (int i=0; i<8; ++i)
    add_edge(edge_array[i].first, edge_array[i].second, G);

  // Use the STL for_each algorithm to "exercise" all of the edges in
  // the graph
  for_each(edges(G).first, edges(G).second, exercise_edge<MyGraph>(G));
  cout << endl;
  return 0;
}


//------------------------------------------------------------------------------
// Construct a flaw distribution for a set of nodes according to a Weibull
// (power-law) distribution.
//------------------------------------------------------------------------------
#include "weibullFlawDistributionOwen.hh"
#include "Utilities/globalNodeIDs.hh"
#include "Utilities/mortonOrderIndices.hh"
#include "NodeList/FluidNodeList.hh"
#include "Field/Field.hh"
#include "Field/FieldList.hh"
#include "DataBase/DataBase.hh"
#include "Distributed/Communicator.hh"
#include "Utilities/allReduce.hh"

#include <boost/functional/hash.hpp>  // hash_combine

#include <set>
#include <algorithm>
#include <limits>
#include <unordered_map>
#include <random>

using std::unordered_map;
using std::vector;
using std::string;
using std::pair;
using std::make_pair;
using std::cout;
using std::cerr;
using std::endl;
using std::min;
using std::max;
using std::abs;

namespace Spheral {

//------------------------------------------------------------------------------
// This version uses my own algorithm, stochastically seeding flaws in the range
// [0, epsmax] where epsmax is chosen per node based on the nodal volume.
//------------------------------------------------------------------------------
template<typename Dimension>
Field<Dimension, vector<double> >
weibullFlawDistributionOwen(const unsigned seed,
                            const double kWeibull,
                            const double mWeibull,
                            const FluidNodeList<Dimension>& nodeList,
                            const int minFlawsPerNode,
                            const double volumeMultiplier,
                            const Field<Dimension, int>& mask) {

  // Pre-conditions.
  REQUIRE(kWeibull >= 0.0);
  REQUIRE(mWeibull > 0.0);
  REQUIRE(minFlawsPerNode > 0);
  REQUIRE(mask.nodeListPtr() == &nodeList);

  typedef typename Dimension::Scalar Scalar;
  typedef KeyTraits::Key Key;

  // Prepare the result.
  Field<Dimension, vector<double> > flaws("Weibull flaw distribution",
                                          nodeList);

  // Find the unique ordering to the nodes.
  DataBase<Dimension> db;
  db.appendNodeList(const_cast<FluidNodeList<Dimension>&>(nodeList));
  FieldList<Dimension, Key> keyList = mortonOrderIndices(db);
  const auto nglobal = db.globalNumInternalNodes();

  // Is there anything to do?
  if (nglobal > 0) {
    const auto nlocal = nodeList.numInternalNodes();

    // Identify the rank and number of domains.
    const auto procID = Process::getRank();

    // State for this NodeList.
    const Field<Dimension, Scalar>& mass = nodeList.mass();
    const Field<Dimension, Scalar>& rho = nodeList.massDensity();

    // Construct a random number generator for each point.
    // Note we hash with the ordering key to generate a unique but reproducible sequence for each point.
    vector<std::mt19937> gens(nlocal);
#pragma omp parallel for
    for (auto i = 0u; i < nlocal; ++i) {
      Key seedi = seed;
      boost::hash_combine(seedi, keyList(0,i));
      gens[i].seed(seedi);
    }
    vector<std::uniform_real_distribution<double>> uniform01(nlocal, std::uniform_real_distribution<double>(0.0, 1.0));

    // Find the minimum and maximum node volumes.
    double Vmin = std::numeric_limits<double>::max(),
           Vmax = std::numeric_limits<double>::min();
    for (auto i = 0u; i != nodeList.numInternalNodes(); ++i) {
      if (mask(i) == 1) {
        const double Vi = mass(i)/rho(i);
        Vmin = min(Vmin, Vi);
        Vmax = max(Vmax, Vi);
      }
    }
    Vmin = allReduce(Vmin*volumeMultiplier, MPI_MIN, Communicator::communicator());
    Vmax = allReduce(Vmax*volumeMultiplier, MPI_MAX, Communicator::communicator());
    CHECK(Vmin > 0.0);
    CHECK(Vmax >= Vmin);

    // Compute the maximum strain we expect for the minimum volume.
    const double epsMax2m = minFlawsPerNode/(kWeibull*Vmin);  // epsmax ** m

    // Based on this compute the maximum number of flaws any node will have.  We'll use this to
    // spin the random number generator without extra communiction.
    const auto maxFlawsPerNode = std::max(1u, unsigned(kWeibull*Vmax*epsMax2m + 0.5));

    // Generate the flaws on each node indepedently.
    const double mInv = 1.0/mWeibull;
    unsigned minNumFlaws = std::numeric_limits<int>::max();
    unsigned maxNumFlaws = 0;
    unsigned totalNumFlaws = 0;
    double epsMin = std::numeric_limits<double>::max();
    double epsMax = std::numeric_limits<double>::min();
    double sumFlaws = 0.0;
#pragma omp parallel for
    for (auto i = 0u; i < nlocal; ++i) {
      if (mask(i) == 1) {
        CHECK(rho(i) > 0.0);
        const auto Vi = mass(i)/rho(i) * volumeMultiplier;
        CHECK(Vi > 0.0);
        const auto numFlawsi = std::max(1u, std::min(maxFlawsPerNode, unsigned(kWeibull*Vi*epsMax2m + 0.5)));
        const auto Ai = numFlawsi/(kWeibull*Vi);
        CHECK(Ai > 0.0);
        for (auto j = 0u; j < numFlawsi; ++j) {
          flaws(i).push_back(pow(Ai * uniform01[i](gens[i]), mInv));
        }
        // Sort the flaws on each node by energy.
        sort(flaws(i).begin(), flaws(i).end());
#pragma omp critical
        {
          minNumFlaws = min(minNumFlaws, unsigned(flaws(i).size()));
          maxNumFlaws = max(maxNumFlaws, unsigned(flaws(i).size()));
          totalNumFlaws += flaws(i).size();
          epsMin = min(epsMin, flaws(i).front());
          epsMax = max(epsMax, flaws(i).back());
          for (auto j = 0u; j < flaws(i).size(); ++j) sumFlaws += flaws(i)[j];
        }
      }
    }

    // Some diagnostic output.
    const auto nused = std::max(1, mask.sumElements());
    if (nglobal > 0) {
      minNumFlaws = allReduce(minNumFlaws, MPI_MIN, Communicator::communicator());
      maxNumFlaws = allReduce(maxNumFlaws, MPI_MAX, Communicator::communicator());
      totalNumFlaws = allReduce(totalNumFlaws, MPI_SUM, Communicator::communicator());
      epsMin = allReduce(epsMin, MPI_MIN, Communicator::communicator());
      epsMax = allReduce(epsMax, MPI_MAX, Communicator::communicator());
      sumFlaws = allReduce(sumFlaws, MPI_SUM, Communicator::communicator());
    }
    if (procID == 0) {
      cerr << "weibullFlawDistributionOwen: Min num flaws per node: " << minNumFlaws << endl
           << "                             Max num flaws per node: " << maxNumFlaws << endl
           << "                             Total num flaws       : " << totalNumFlaws << endl
           << "                             Avg flaws per node    : " << totalNumFlaws / nused << endl
           << "                             Min flaw strain       : " << epsMin << endl
           << "                             Max flaw strain       : " << epsMax << endl
           << "                             Avg node failure      : " << sumFlaws / nused << endl;
    }

    // That's it.
    BEGIN_CONTRACT_SCOPE
    {
      for (int i = 0; i != (int)nodeList.numInternalNodes(); ++i) {
        if (mask(i) == 1) {
          for (vector<double>::const_iterator itr = flaws(i).begin() + 1;
               itr != flaws(i).end();
               ++itr) ENSURE(*itr >= *(itr - 1));
        }
      }
    }
    END_CONTRACT_SCOPE
  }

  return flaws;
}

}


#include <boost/test/unit_test.hpp>

#include <boost/format.hpp>

#include <set>
#include <iostream>

// using namespace std;
using namespace crave;

using boost::format;

BOOST_FIXTURE_TEST_SUITE(Context_t, Context_Fixture)

BOOST_AUTO_TEST_CASE(multiple_solver_instances) {
  Generator gen1, gen2;
  Variable<int> r1, r2;
  gen1(r1 < 6);
  gen2(r2 < 6);
  gen1.next();
  gen2.next();
}

BOOST_AUTO_TEST_CASE(constants) {
  Variable<unsigned> x;
  Generator gen(x == 135421);
  gen();
  BOOST_CHECK_EQUAL(gen[x], 135421);
}

BOOST_AUTO_TEST_CASE(boolean) {
  Variable<bool> b;

  Generator gen;
  gen(b == b)();  // create a new assignment

  bool b1 = gen[b];
  // printf("result: b1=%d\n", b1);
  gen(b != b1)();
  bool b2 = gen[b];
  // printf("result: b2=%d\n", b2);

  BOOST_CHECK_THROW(gen(b != b2)(), std::runtime_error);
}

BOOST_AUTO_TEST_CASE(by_reference) {
  unsigned b = 0;
  Variable<unsigned> a;

  Generator gen(a == reference(b));

  while (gen.next()) {
    unsigned av = gen[a];
    BOOST_REQUIRE_EQUAL(av, b);
    ++b;
    if (b > 10) break;
  }
}

// temporaly fix a variable to a certain value using the assign operator
BOOST_AUTO_TEST_CASE(named_reference) {
  unsigned bv = 0;
  Variable<unsigned> a, c;
  ReadReference<unsigned> b(bv);

  Generator gen(a == b);
  gen(c != b);

  while (gen.next()) {
    unsigned av = gen[a];
    unsigned cv = gen[c];
    BOOST_REQUIRE_EQUAL(av, bv);
    BOOST_REQUIRE_NE(cv, bv);
    ++bv;
    if (bv > 10) break;
  }
}

BOOST_AUTO_TEST_CASE(soft_constraint_t) {
  Generator gen;
  Variable<int> r;
  gen(r < 6);
  gen.soft(r == 2);

  BOOST_REQUIRE(gen.next());
  BOOST_REQUIRE_EQUAL(gen[r], 2);

  gen.soft(r == 3);

  BOOST_REQUIRE(gen.next());
}

BOOST_AUTO_TEST_CASE(randv_test) {
  Generator gen;
  randv<int> a(NULL);
  randv<int> b(NULL);
  std::cout << "init: a = " << a << ", b = " << b << std::endl;
  gen(4 <= a() && a() <= 6)
  (9 <= a() + b() && a() + b() <= 11)(b() % 2 == 0);
  int count = 0;
  while (gen.next()) {
    ++count;
    std::cout << "result: a = " << a << ", b = " << b << std::endl;
    gen(a() != a || b() != b);

    BOOST_REQUIRE_LE(count, 10);
  }

  BOOST_REQUIRE_EQUAL(count, 4);
}

BOOST_AUTO_TEST_CASE(randv_var_ref_mixed_test) {
  Generator gen;
  randv<int> a(0);
  Variable<int> b;
  gen(4 <= a() && a() <= 6)
  (9 <= a() + b && a() + b <= 11)(b % 2 == 0);
  unsigned count = 0;
  while (gen.next()) {
    ++count;
    std::cout << "result: a = " << a << ", b = " << gen[b] << std::endl;
    gen(a() != a || b != gen[b]);

    BOOST_REQUIRE_LE(count, 10);
  }
  BOOST_REQUIRE_EQUAL(count, 4);
}

BOOST_AUTO_TEST_CASE(alu) {
  Variable<unsigned> op;
  Variable<unsigned> a;
  Variable<unsigned> b;

  Generator gen;
  gen(a < 16)
  (b < 16)(op < 4)             // 4 opcodes
      (op != 0 || a + b < 16)  // no add overflow
      (op != 1 || a > b)       // no sub underflow
      (op != 2 || a * b < 16)  // no m overflow
      (op != 3 || b != 0)      // div valid
      ;
  gen();  // create a new assignment

  // printf("result: op=%d, a=%d, b=%d\n", gen[op], gen[a], gen[b]);
}

BOOST_AUTO_TEST_CASE(alu_enum) {
  VariableDefaultSolver::bypass_constraint_analysis = true;

  Variable<unsigned> op;
  Variable<unsigned> a;
  Variable<unsigned> b;

  Generator gen;
  gen(a < 16u)
  (b < 16u)(op < 4u)             // 4 opcodes
      (op != 0u || a + b < 16u)  // no add overflow
      (op != 1u || a > b)        // no sub underflow
      (op != 2u || a * b < 16u)  // no m overflow
      (op != 3u || b != 0u)      // div valid
      ;

  unsigned count = 0;
  while (gen.next()) {
    ++count;
    gen(op != gen[op] || a != gen[a] || b != gen[b]);

    BOOST_REQUIRE_LE(count, 600);
  }
  BOOST_REQUIRE_EQUAL(count, 572);

  VariableDefaultSolver::bypass_constraint_analysis = false;
}

BOOST_AUTO_TEST_CASE(pythagoras) {
  Variable<unsigned long long> a;
  Variable<unsigned long long> b;
  Variable<unsigned long long> c;

  Generator gen;
  gen(a * a + b * b == c * c)();  // create a new assignment

  unsigned long long av = gen[a];
  unsigned long long bv = gen[b];
  unsigned long long cv = gen[c];

  BOOST_CHECK_EQUAL(av * av + bv * bv, cv * cv);
}

BOOST_AUTO_TEST_CASE(negative_var) {
  randv<int> a(NULL);
  randv<int> b(NULL);

  Generator gen;
  gen(a() + b() <= 120)
  (a() > 120);

  gen();

  std::cout << format("result: a=%d, b=%d\n") % a % b;

  BOOST_CHECK(a + b <= 120);
}

BOOST_AUTO_TEST_CASE(signed_less_zero) {
  randv<int> a(NULL);

  Generator gen;
  gen(a() < 0);

  gen();

  std::cout << format("result: a=%d\n") % a;

  BOOST_CHECK(a < 0);
}

BOOST_AUTO_TEST_CASE(mixed_bv_width_1) {
  randv<unsigned long> a(NULL);
  randv<unsigned short> b(NULL);

  Generator gen;
  gen(a() + b() >= 120);

  gen.next();

  std::cout << format("result: a=%d, b=%d\n") % a % b;

  BOOST_CHECK(a + b >= 120);
}

BOOST_AUTO_TEST_CASE(mixed_bv_width_2) {
  VariableDefaultSolver::bypass_constraint_analysis = true;

  randv<signed char> a(NULL);
  Generator gen;
  gen(a() < 10);

  std::set<signed char> generated;
  for (int iterations = 0; gen.next(); ++iterations) {
    generated.insert(a);
    gen(a() != a);

    BOOST_REQUIRE_LT(iterations, 150);
  }

  BOOST_CHECK_EQUAL(generated.size(), 138);

  VariableDefaultSolver::bypass_constraint_analysis = false;
}

BOOST_AUTO_TEST_CASE(mixed_bv_width_3) {
  randv<short> a(NULL);
  Generator gen;
  gen(a() < 10);
  gen(a() > -10);

  std::set<short> generated;
  for (unsigned iterations = 0; gen.next(); ++iterations) {
    generated.insert(a);
    gen(a() != a);

    BOOST_REQUIRE_LT(iterations, 20);
  }

  BOOST_CHECK_EQUAL(generated.size(), 19);
}

BOOST_AUTO_TEST_CASE(mixed_bv_width_4) {
  randv<int> a(NULL);
  Generator gen;
  gen(a() < (signed char)10);
  gen(a() > (short)-10);

  std::set<int> generated;
  for (unsigned iterations = 0; gen.next(); ++iterations) {
    generated.insert(a);
    gen(a() != a);

    BOOST_REQUIRE_LT(iterations, 20);
  }

  BOOST_CHECK_EQUAL(generated.size(), 19);
}

BOOST_AUTO_TEST_CASE(mixed_bv_width_5) {
  randv<short> a(NULL);
  randv<signed char> b(NULL);
  Generator gen;
  gen(-3 <= a() && a() <= 3);
  gen(-3 <= b() && b() <= 3);
  gen((-2 <= a() + b()) && (a() + b() <= 2));

  int cnt = 0;
  while (gen.next()) {
    cnt++;
    gen((a() != a) || (b() != b));

    BOOST_REQUIRE_LT(cnt, 300);
  }

  int cnt1 = 0;
  for (short i = -3; i <= 3; i++)
    for (signed char j = -3; j <= 3; j++)
      if ((-2 <= i + j) && (i + j <= 2)) cnt1++;

  BOOST_CHECK_EQUAL(cnt, cnt1);
}

BOOST_AUTO_TEST_CASE(mixed_bv_width_6) {
  randv<short> a(0);
  randv<signed char> b(0);

  Generator gen;
  gen(-3 <= a() && a() <= 3)
  (-3 <= b() && b() <= 3)(a() * b() % 6 == 0);

  int cnt = 0;
  for (int i = -3; i <= 3; i++)
    for (int j = -3; j <= 3; j++)
      if (i * j % 6 == 0) cnt++;

  int cnt1 = 0;
  while (gen.next()) {
    cnt1++;
    BOOST_REQUIRE_EQUAL(a * b % 6, 0);
    gen(a() != a || b() != b);
  }

  BOOST_REQUIRE_EQUAL(cnt, cnt1);
}

BOOST_AUTO_TEST_CASE(dist_of_boolean25) {
  randv<bool> a(0);
  int counter = 0;

  Generator gen;
  gen(dist(a(), distribution<bool>::create(0.25)));
  for (unsigned i = 0; i < 1000; i++) {
    BOOST_REQUIRE(gen.next());
    if (a) {
      ++counter;
    } else {
      --counter;
    }
  }

  BOOST_REQUIRE_LT(counter, -425);
  BOOST_REQUIRE_GT(counter, -575);
}

BOOST_AUTO_TEST_CASE(dist_of_boolean50) {
  randv<bool> a(0);
  int counter = 0;

  Generator gen;
  gen(dist(a(), distribution<bool>::create(0.5)));
  for (unsigned i = 0; i < 1000; i++) {
    BOOST_REQUIRE(gen.next());
    if (a) {
      ++counter;
    } else {
      --counter;
    }
  }

  BOOST_REQUIRE_LT(counter, 75);
  BOOST_REQUIRE_GT(counter, -75);
}

BOOST_AUTO_TEST_CASE(dist_of_boolean75) {
  randv<bool> a(0);
  int counter = 0;

  Generator gen;
  gen(dist(a(), distribution<bool>::create(0.75)));
  for (unsigned i = 0; i < 1000; i++) {
    BOOST_REQUIRE(gen.next());
    if (a) {
      ++counter;
    } else {
      --counter;
    }
  }

  BOOST_REQUIRE_LT(counter, 575);
  BOOST_REQUIRE_GT(counter, 425);
}

BOOST_AUTO_TEST_SUITE_END()  // Context

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


// #define _GLIBCXX_DEBUG
#include <bits/stdc++.h>
#include <boost/lexical_cast.hpp>

#define FOR(i, a, b) for (int i = (a); i < int(b); ++i)
#define RFOR(i, a, b) for (int i = (b)-1; i >= int(a); --i)
#define REP(i, n) FOR(i, 0, n)
#define REP1(i, n) FOR(i, 1, int(n) + 1)
#define RREP(i, n) RFOR(i, 0, n)
#define RREP1(i, n) RFOR(i, 1, int(n) + 1)
#define ALL(c) begin(c), end(c)

int _ = (
#ifndef LOCAL
    std::cin.tie(nullptr), std::ios::sync_with_stdio(false),
#endif
    std::cout.precision(10), std::cout.setf(std::ios::fixed));

using ll = long long;
using ull = unsigned long long;
using ld = long double;
template <typename T> using vec = std::vector<T>;
using namespace std;

bool ok(int N, int M, const vec<int> &s, const vec<int> &c, const string &t) {
  if (t.length() != N) {
    return false;
  }
  for (int j = 0; j < M; j++) {
    if (t[s[j] - 1] != boost::lexical_cast<char>(c[j])) {
      return false;
    }
  }
  return true;
}

string solve(int N, int M, const vec<int> &s, const vec<int> &c) {
  for (int i = 0; i < 1000; i++) {
    string t = boost::lexical_cast<string>(i);
    if (ok(N, M, s, c, t)) {
      return t;
    }
  }
  return string("-1");
}

int main() {
  int N, M;
  cin >> N >> M;
  vec<int> s(M), c(M);
  REP(i, M) { cin >> s[i] >> c[i]; }
  cout << solve(N, M, s, c) << endl;
  return 0;
}


// Copyright (C) 2009-2012 Lorenzo Caminiti
// Distributed under the Boost Software License, Version 1.0
// (see accompanying file LICENSE_1_0.txt or a copy at
// http://www.boost.org/LICENSE_1_0.txt)
// Home at http://www.boost.org/libs/local_function

#include <boost/config.hpp>
#ifdef BOOST_NO_CXX11_VARIADIC_MACROS
#   error "variadic macros required"
#else

#include <boost/local_function.hpp>
#include <boost/detail/lightweight_test.hpp>
#include <vector>
#include <algorithm>

int main(void) {
    //[add_inline
    int sum = 0, factor = 10;

    void BOOST_LOCAL_FUNCTION(const bind factor, bind& sum, int num) {
        sum += factor * num;
    } BOOST_LOCAL_FUNCTION_NAME(inline add) // Inlining.

    std::vector<int> v(100);
    std::fill(v.begin(), v.end(), 1);

    for(size_t i = 0; i < v.size(); ++i) add(v[i]); // Cannot use for_each.
    //]

    BOOST_TEST(sum == 1000);
    return boost::report_errors();
}

#endif // VARIADIC_MACROS


#include <df/image/backprojection.h>

#include <assert.h>
#include <Eigen/Core>

#include <df/camera/cameraModel.h>

#include <df/util/macros.h>

namespace df {

template <typename Scalar,
          template <typename> class CamModelT>
void backproject(const HostTensor2<Scalar> & depthMap,
                 HostTensor2<Eigen::UnalignedVec3<Scalar> > & vertMap,
                 CamModelT<Scalar> & cameraModel) {

    typedef Eigen::Matrix<Scalar,2,1,Eigen::DontAlign> Vec2;

    const uint width = depthMap.dimensionSize(0);
    const uint height = depthMap.dimensionSize(1);

    for (uint y = 0; y < height; ++y) {
        for (uint x = 0; x < width; ++x) {
            const Scalar depth = depthMap(x,y);
            vertMap(x,y) = cameraModel.unproject(Vec2(x,y),depth);
        }
    }

}

#define BACKPROJECT_EXPLICIT_INSTANTIATION(type,camera)                   \
    template void backproject(const HostTensor2<type> &,                  \
                              HostTensor2<Eigen::UnalignedVec3<type> > &, \
                              camera##CameraModel<type> &)

ALL_CAMERAS_AND_TYPES_INSTANTIATION(BACKPROJECT_EXPLICIT_INSTANTIATION);

} // namespace df


// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.

// This file was modified by Oracle on 2013, 2014, 2015, 2017, 2018, 2019.
// Modifications copyright (c) 2013-2019 Oracle and/or its affiliates.

// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

// 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)

#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HELPERS_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HELPERS_HPP

#include <boost/geometry/algorithms/detail/direction_code.hpp>
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
#include <boost/geometry/algorithms/detail/recalculate.hpp>
#include <boost/geometry/core/assert.hpp>
#include <boost/geometry/geometries/segment.hpp> // referring_segment
#include <boost/geometry/policies/relate/direction.hpp>
#include <boost/geometry/policies/relate/intersection_points.hpp>
#include <boost/geometry/policies/relate/tupled.hpp>
#include <boost/geometry/policies/robustness/rescale_policy_tags.hpp>
#include <boost/geometry/strategies/intersection_result.hpp>

namespace boost { namespace geometry {

#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace overlay {

enum turn_position { position_middle, position_front, position_back };

template <typename Point, typename SegmentRatio>
struct turn_operation_linear
    : public turn_operation<Point, SegmentRatio>
{
    turn_operation_linear()
        : position(position_middle)
        , is_collinear(false)
    {}

    turn_position position;
    bool is_collinear; // valid only for Linear geometry
};

template
<
    typename TurnPointCSTag,
    typename UniqueSubRange1,
    typename UniqueSubRange2,
    typename SideStrategy
>
struct side_calculator
{
    typedef typename UniqueSubRange1::point_type point1_type;
    typedef typename UniqueSubRange2::point_type point2_type;

    inline side_calculator(UniqueSubRange1 const& range_p,
                           UniqueSubRange2 const& range_q,
                           SideStrategy const& side_strategy)
        : m_side_strategy(side_strategy)
        , m_range_p(range_p)
        , m_range_q(range_q)
    {}

    inline int pk_wrt_p1() const { return m_side_strategy.apply(get_pi(), get_pj(), get_pk()); }
    inline int pk_wrt_q1() const { return m_side_strategy.apply(get_qi(), get_qj(), get_pk()); }
    inline int qk_wrt_p1() const { return m_side_strategy.apply(get_pi(), get_pj(), get_qk()); }
    inline int qk_wrt_q1() const { return m_side_strategy.apply(get_qi(), get_qj(), get_qk()); }

    inline int pk_wrt_q2() const { return m_side_strategy.apply(get_qj(), get_qk(), get_pk()); }
    inline int qk_wrt_p2() const { return m_side_strategy.apply(get_pj(), get_pk(), get_qk()); }

    // Necessary when rescaling turns off:
    inline int qj_wrt_p1() const { return m_side_strategy.apply(get_pi(), get_pj(), get_qj()); }
    inline int qj_wrt_p2() const { return m_side_strategy.apply(get_pj(), get_pk(), get_qj()); }
    inline int pj_wrt_q1() const { return m_side_strategy.apply(get_qi(), get_qj(), get_pj()); }
    inline int pj_wrt_q2() const { return m_side_strategy.apply(get_qj(), get_qk(), get_pj()); }

    inline point1_type const& get_pi() const { return m_range_p.at(0); }
    inline point1_type const& get_pj() const { return m_range_p.at(1); }
    inline point1_type const& get_pk() const { return m_range_p.at(2); }

    inline point2_type const& get_qi() const { return m_range_q.at(0); }
    inline point2_type const& get_qj() const { return m_range_q.at(1); }
    inline point2_type const& get_qk() const { return m_range_q.at(2); }

    // Used side-strategy, owned by the calculator,
    // created from .get_side_strategy()
    SideStrategy m_side_strategy;

    // Used ranges - owned by get_turns or (for robust points) by intersection_info_base
    UniqueSubRange1 const& m_range_p;
    UniqueSubRange2 const& m_range_q;
};

template<typename Point, typename UniqueSubRange, typename RobustPolicy>
struct robust_subrange_adapter
{
    typedef Point point_type;

    robust_subrange_adapter(UniqueSubRange const& unique_sub_range,
                     Point const& robust_point_i, Point const& robust_point_j,
                     RobustPolicy const& robust_policy)

        : m_unique_sub_range(unique_sub_range)
        , m_robust_policy(robust_policy)
        , m_robust_point_i(robust_point_i)
        , m_robust_point_j(robust_point_j)
        , m_k_retrieved(false)
    {}

    std::size_t size() const { return m_unique_sub_range.size(); }

    //! Get precalculated point
    Point const& at(std::size_t index) const
    {
        BOOST_GEOMETRY_ASSERT(index < size());
        switch (index)
        {
            case 0 : return m_robust_point_i;
            case 1 : return m_robust_point_j;
            case 2 : return get_point_k();
            default : return m_robust_point_i;
        }
    }

private :
    Point const& get_point_k() const
    {
        if (! m_k_retrieved)
        {
            geometry::recalculate(m_robust_point_k, m_unique_sub_range.at(2), m_robust_policy);
            m_k_retrieved = true;
        }
        return m_robust_point_k;
    }

    UniqueSubRange const& m_unique_sub_range;
    RobustPolicy const& m_robust_policy;

    Point const& m_robust_point_i;
    Point const& m_robust_point_j;
    mutable Point m_robust_point_k;

    mutable bool m_k_retrieved;
};

template
<
    typename UniqueSubRange1, typename UniqueSubRange2,
    typename RobustPolicy
>
struct robust_point_calculator
{
    typedef typename geometry::robust_point_type
        <
            typename UniqueSubRange1::point_type, RobustPolicy
        >::type robust_point1_type;
    typedef typename geometry::robust_point_type
        <
            typename UniqueSubRange2::point_type, RobustPolicy
        >::type robust_point2_type;

    inline robust_point_calculator(UniqueSubRange1 const& range_p,
                                   UniqueSubRange2 const& range_q,
                                   RobustPolicy const& robust_policy)
        : m_robust_policy(robust_policy)
        , m_range_p(range_p)
        , m_range_q(range_q)
        , m_pk_retrieved(false)
        , m_qk_retrieved(false)
    {
        // Calculate pi,pj and qi,qj which are almost always necessary
        // But don't calculate pk/qk yet, which is retrieved (taking
        // more time) and not always necessary.
        geometry::recalculate(m_rpi, range_p.at(0), robust_policy);
        geometry::recalculate(m_rpj, range_p.at(1), robust_policy);
        geometry::recalculate(m_rqi, range_q.at(0), robust_policy);
        geometry::recalculate(m_rqj, range_q.at(1), robust_policy);
    }

    inline robust_point1_type const& get_rpk() const
    {
        if (! m_pk_retrieved)
        {
            geometry::recalculate(m_rpk, m_range_p.at(2), m_robust_policy);
            m_pk_retrieved = true;
        }
        return m_rpk;
    }
    inline robust_point2_type const& get_rqk() const
    {
        if (! m_qk_retrieved)
        {
            geometry::recalculate(m_rqk, m_range_q.at(2), m_robust_policy);
            m_qk_retrieved = true;
        }
        return m_rqk;
    }

    robust_point1_type m_rpi, m_rpj;
    robust_point2_type m_rqi, m_rqj;

private :
    RobustPolicy const& m_robust_policy;
    UniqueSubRange1 const& m_range_p;
    UniqueSubRange2 const& m_range_q;

    // On retrieval
    mutable robust_point1_type m_rpk;
    mutable robust_point2_type m_rqk;
    mutable bool m_pk_retrieved;
    mutable bool m_qk_retrieved;
};

// Default version (empty - specialized below)
template
<
    typename UniqueSubRange1, typename UniqueSubRange2,
    typename TurnPoint, typename UmbrellaStrategy,
    typename RobustPolicy,
    typename Tag = typename rescale_policy_type<RobustPolicy>::type
>
class intersection_info_base {};

// Version with rescaling, having robust points
template
<
    typename UniqueSubRange1, typename UniqueSubRange2,
    typename TurnPoint, typename UmbrellaStrategy,
    typename RobustPolicy
>
class intersection_info_base<UniqueSubRange1, UniqueSubRange2,
        TurnPoint, UmbrellaStrategy, RobustPolicy, rescale_policy_tag>
{
    typedef robust_point_calculator
    <
        UniqueSubRange1, UniqueSubRange2,
        RobustPolicy
    >
    robust_calc_type;

public:
    typedef segment_intersection_points
    <
        TurnPoint,
        geometry::segment_ratio<boost::long_long_type>
    > intersection_point_type;
    typedef policies::relate::segments_tupled
        <
            policies::relate::segments_intersection_points
                <
                    intersection_point_type
                >,
            policies::relate::segments_direction
        > intersection_policy_type;

    typedef typename intersection_policy_type::return_type result_type;

    typedef typename robust_calc_type::robust_point1_type robust_point1_type;
    typedef typename robust_calc_type::robust_point2_type robust_point2_type;

    typedef robust_subrange_adapter<robust_point1_type, UniqueSubRange1, RobustPolicy> robust_subrange1;
    typedef robust_subrange_adapter<robust_point2_type, UniqueSubRange2, RobustPolicy> robust_subrange2;

    typedef typename cs_tag<TurnPoint>::type cs_tag;

    typedef typename UmbrellaStrategy::side_strategy_type side_strategy_type;
    typedef side_calculator<cs_tag, robust_subrange1, robust_subrange2,
             side_strategy_type> side_calculator_type;

    typedef side_calculator
        <
            cs_tag, robust_subrange2, robust_subrange1,
            side_strategy_type
        > robust_swapped_side_calculator_type;

    intersection_info_base(UniqueSubRange1 const& range_p,
                           UniqueSubRange2 const& range_q,
                           UmbrellaStrategy const& umbrella_strategy,
                           RobustPolicy const& robust_policy)
        : m_range_p(range_p)
        , m_range_q(range_q)
        , m_robust_calc(range_p, range_q, robust_policy)
        , m_robust_range_p(range_p, m_robust_calc.m_rpi, m_robust_calc.m_rpj, robust_policy)
        , m_robust_range_q(range_q, m_robust_calc.m_rqi, m_robust_calc.m_rqj, robust_policy)
        , m_side_calc(m_robust_range_p, m_robust_range_q,
                      umbrella_strategy.get_side_strategy())
        , m_result(umbrella_strategy.apply(range_p, range_q,
                       intersection_policy_type(),
                       m_robust_range_p, m_robust_range_q))
    {}

    inline bool p_is_last_segment() const { return m_range_p.is_last_segment(); }
    inline bool q_is_last_segment() const { return m_range_q.is_last_segment(); }

    inline robust_point1_type const& rpi() const { return m_robust_calc.m_rpi; }
    inline robust_point1_type const& rpj() const { return m_robust_calc.m_rpj; }
    inline robust_point1_type const& rpk() const { return m_robust_calc.get_rpk(); }

    inline robust_point2_type const& rqi() const { return m_robust_calc.m_rqi; }
    inline robust_point2_type const& rqj() const { return m_robust_calc.m_rqj; }
    inline robust_point2_type const& rqk() const { return m_robust_calc.get_rqk(); }

    inline side_calculator_type const& sides() const { return m_side_calc; }

    robust_swapped_side_calculator_type get_swapped_sides() const
    {
        robust_swapped_side_calculator_type result(
                            m_robust_range_q, m_robust_range_p,
                            m_side_calc.m_side_strategy);
        return result;
    }

private :

    // Owned by get_turns
    UniqueSubRange1 const& m_range_p;
    UniqueSubRange2 const& m_range_q;

    // Owned by this class
    robust_calc_type m_robust_calc;
    robust_subrange1 m_robust_range_p;
    robust_subrange2 m_robust_range_q;
    side_calculator_type m_side_calc;

protected :
    result_type m_result;
};

// Version without rescaling
template
<
    typename UniqueSubRange1, typename UniqueSubRange2,
    typename TurnPoint, typename UmbrellaStrategy,
    typename RobustPolicy
>
class intersection_info_base<UniqueSubRange1, UniqueSubRange2,
        TurnPoint, UmbrellaStrategy, RobustPolicy, no_rescale_policy_tag>
{
public:

    typedef segment_intersection_points<TurnPoint> intersection_point_type;
    typedef policies::relate::segments_tupled
        <
            policies::relate::segments_intersection_points
                <
                    intersection_point_type
                >,
            policies::relate::segments_direction
        > intersection_policy_type;

    typedef typename intersection_policy_type::return_type result_type;

    typedef typename UniqueSubRange1::point_type point1_type;
    typedef typename UniqueSubRange2::point_type point2_type;

    typedef typename UmbrellaStrategy::cs_tag cs_tag;

    typedef typename UmbrellaStrategy::side_strategy_type side_strategy_type;
    typedef side_calculator<cs_tag, UniqueSubRange1, UniqueSubRange2, side_strategy_type> side_calculator_type;

    typedef side_calculator
        <
            cs_tag, UniqueSubRange2, UniqueSubRange1,
            side_strategy_type
        > swapped_side_calculator_type;

    intersection_info_base(UniqueSubRange1 const& range_p,
                           UniqueSubRange2 const& range_q,
                           UmbrellaStrategy const& umbrella_strategy,
                           no_rescale_policy const& )
        : m_range_p(range_p)
        , m_range_q(range_q)
        , m_side_calc(range_p, range_q,
                      umbrella_strategy.get_side_strategy())
        , m_result(umbrella_strategy.apply(range_p, range_q, intersection_policy_type()))
    {}

    inline bool p_is_last_segment() const { return m_range_p.is_last_segment(); }
    inline bool q_is_last_segment() const { return m_range_q.is_last_segment(); }

    inline point1_type const& rpi() const { return m_side_calc.get_pi(); }
    inline point1_type const& rpj() const { return m_side_calc.get_pj(); }
    inline point1_type const& rpk() const { return m_side_calc.get_pk(); }

    inline point2_type const& rqi() const { return m_side_calc.get_qi(); }
    inline point2_type const& rqj() const { return m_side_calc.get_qj(); }
    inline point2_type const& rqk() const { return m_side_calc.get_qk(); }

    inline side_calculator_type const& sides() const { return m_side_calc; }

    swapped_side_calculator_type get_swapped_sides() const
    {
        swapped_side_calculator_type result(
            m_range_q, m_range_p,
            m_side_calc.m_side_strategy);
        return result;
    }

private :
    // Owned by get_turns
    UniqueSubRange1 const& m_range_p;
    UniqueSubRange2 const& m_range_q;

    // Owned by this class
    side_calculator_type m_side_calc;

protected :
    result_type m_result;
};

template
<
    typename UniqueSubRange1, typename UniqueSubRange2,
    typename TurnPoint,
    typename UmbrellaStrategy,
    typename RobustPolicy
>
class intersection_info
    : public intersection_info_base<UniqueSubRange1, UniqueSubRange2,
        TurnPoint, UmbrellaStrategy, RobustPolicy>
{
    typedef intersection_info_base<UniqueSubRange1, UniqueSubRange2,
        TurnPoint, UmbrellaStrategy, RobustPolicy> base;

public:

    typedef typename UniqueSubRange1::point_type point1_type;
    typedef typename UniqueSubRange2::point_type point2_type;

    typedef UmbrellaStrategy intersection_strategy_type;
    typedef typename UmbrellaStrategy::side_strategy_type side_strategy_type;
    typedef typename UmbrellaStrategy::cs_tag cs_tag;

    typedef typename base::side_calculator_type side_calculator_type;
    typedef typename base::result_type result_type;

    typedef typename boost::tuples::element<0, result_type>::type i_info_type; // intersection_info
    typedef typename boost::tuples::element<1, result_type>::type d_info_type; // dir_info

    intersection_info(UniqueSubRange1 const& range_p,
                      UniqueSubRange2 const& range_q,
                      UmbrellaStrategy const& umbrella_strategy,
                      RobustPolicy const& robust_policy)
        : base(range_p, range_q,
               umbrella_strategy, robust_policy)
        , m_intersection_strategy(umbrella_strategy)
        , m_robust_policy(robust_policy)
    {}

    inline result_type const& result() const { return base::m_result; }
    inline i_info_type const& i_info() const { return base::m_result.template get<0>(); }
    inline d_info_type const& d_info() const { return base::m_result.template get<1>(); }

    inline side_strategy_type get_side_strategy() const
    {
        return m_intersection_strategy.get_side_strategy();
    }

    // TODO: it's more like is_spike_ip_p
    inline bool is_spike_p() const
    {
        if (base::p_is_last_segment())
        {
            return false;
        }
        if (base::sides().pk_wrt_p1() == 0)
        {
            // p:  pi--------pj--------pk
            // or: pi----pk==pj

            if (! is_ip_j<0>())
            {
                return false;
            }

            // TODO: why is q used to determine spike property in p?
            bool const has_qk = ! base::q_is_last_segment();
            int const qk_p1 = has_qk ? base::sides().qk_wrt_p1() : 0;
            int const qk_p2 = has_qk ? base::sides().qk_wrt_p2() : 0;

            if (qk_p1 == -qk_p2)
            {
                if (qk_p1 == 0)
                {
                    // qk is collinear with both p1 and p2,
                    // verify if pk goes backwards w.r.t. pi/pj
                    return direction_code<cs_tag>(base::rpi(), base::rpj(), base::rpk()) == -1;
                }

                // qk is at opposite side of p1/p2, therefore
                // p1/p2 (collinear) are opposite and form a spike
                return true;
            }
        }

        return false;
    }

    inline bool is_spike_q() const
    {
        if (base::q_is_last_segment())
        {
            return false;
        }

        // See comments at is_spike_p
        if (base::sides().qk_wrt_q1() == 0)
        {
            if (! is_ip_j<1>())
            {
                return false;
            }

            // TODO: why is p used to determine spike property in q?
            bool const has_pk = ! base::p_is_last_segment();
            int const pk_q1 = has_pk ? base::sides().pk_wrt_q1() : 0;
            int const pk_q2 = has_pk ? base::sides().pk_wrt_q2() : 0;

            if (pk_q1 == -pk_q2)
            {
                if (pk_q1 == 0)
                {
                    return direction_code<cs_tag>(base::rqi(), base::rqj(), base::rqk()) == -1;
                }

                return true;
            }
        }

        return false;
    }

private:
    template <std::size_t OpId>
    bool is_ip_j() const
    {
        int arrival = d_info().arrival[OpId];
        bool same_dirs = d_info().dir_a == 0 && d_info().dir_b == 0;

        if (same_dirs)
        {
            if (i_info().count == 2)
            {
                return arrival != -1;
            }
            else
            {
                return arrival == 0;
            }
        }
        else
        {
            return arrival == 1;
        }
    }

    UmbrellaStrategy const& m_intersection_strategy;
    RobustPolicy const& m_robust_policy;
};

}} // namespace detail::overlay
#endif // DOXYGEN_NO_DETAIL

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HELPERS_HPP


#include <frovedis.hpp>
#include <frovedis/matrix/crs_matrix.hpp>

#include <boost/program_options.hpp>

using namespace boost;
using namespace frovedis;
using namespace std;

vector<size_t> reduce_word_count(const vector<size_t>& l,
                                 const vector<size_t>& r) {
  vector<size_t> ret(l.size());
  for(size_t i = 0; i < l.size(); i++) {
    ret[i] = l[i] + r[i];
  }
  return ret;
}

vector<size_t> each_word_count(crs_matrix_local<int>& mat) {
  vector<size_t> ret(mat.local_num_col);
  size_t* retp = &ret[0];
  size_t* idxp = &mat.idx[0];
  for(size_t r = 0; r < mat.local_num_row; r++) {
#pragma cdir nodep
    for(size_t c = mat.off[r]; c < mat.off[r+1]; c++) {
      retp[idxp[c]]++;
    }
  }
  return ret;
}

struct shrink {
  shrink(){}
  shrink(int m, vector<size_t> w) :
    min_article_length(m), word_to_remove(w) {}
  crs_matrix_local<int> operator()(crs_matrix_local<int>& mat,
                                   vector<size_t>& removed_article) {
    vector<size_t> id_change(mat.local_num_col);
    size_t remove_idx = 0;
    size_t current_idx = 0;
    for(size_t i = 0; i < id_change.size(); i++) {
      if(remove_idx < word_to_remove.size() &&
         i == word_to_remove[remove_idx]) {
        id_change[i] = -1;
        remove_idx++;
      } else {
        id_change[i] = current_idx++;
      }
    }
    crs_matrix_local<int> ret; // already ret.off[0] == 0
    for(size_t r = 0; r < mat.local_num_row; r++) {
      size_t word_count = 0;
      for(size_t c = mat.off[r]; c < mat.off[r+1]; c++) {
        if(!binary_search(word_to_remove.begin(), word_to_remove.end(),
                          mat.idx[c])) word_count++;
      }
      if(word_count < static_cast<size_t>(min_article_length)) {
        removed_article.push_back(r);
      } else {
        size_t tmpoff = 0;
        for(size_t c = mat.off[r]; c < mat.off[r+1]; c++) {
          if(!binary_search(word_to_remove.begin(), word_to_remove.end(),
                            mat.idx[c])) {
            ret.idx.push_back(id_change[mat.idx[c]]);
            ret.val.push_back(mat.val[c]);
            tmpoff++;
          }
        }
        ret.off.push_back(tmpoff + *(ret.off.end() - 1));
      }
    }
    ret.set_local_num(mat.local_num_col - word_to_remove.size());
    return ret;
  }
  int min_article_length;
  vector<size_t> word_to_remove;
  SERIALIZE(min_article_length, word_to_remove);
};

/*
  assume that document x term matrix
  (i.e. document is row, term is column)

  contents of matrix are all int
 */
void shrink_mat(const string& input_matrix,
                const string& input_dictionary,
                const string& input_article,
                const string& output_matrix,
                const string& output_dictionary,
                const string& output_article,
                int min_word_used,
                int min_article_length) {
  vector<string> dictionary, article;
  ifstream dicstr(input_dictionary.c_str());
  string line;
  while(getline(dicstr, line)) {
    dictionary.push_back(line);
  }
  ifstream artstr(input_article.c_str());
  while(getline(artstr, line)) {
    article.push_back(line);
  }
  auto mat = make_crs_matrix_load<int>(input_matrix, dictionary.size());
  auto word_count = mat.data.map(each_word_count).reduce(reduce_word_count);
  vector<size_t> word_to_remove;
  for(size_t i = 0; i < word_count.size(); i++) {
    if(word_count[i] < static_cast<size_t>(min_word_used))
      word_to_remove.push_back(i);
  }
  auto removed_article_nloc = make_node_local_allocate<vector<size_t>>();
  crs_matrix<int> shrinked
    (mat.data.map<crs_matrix_local<int>>(shrink(min_article_length,
                                                word_to_remove),
                                         removed_article_nloc));
  // crs_get_local_num_row<T> is defined in crs_matrix.hpp
  auto local_num_rows = shrinked.data.map(crs_get_local_num_row<int,size_t,size_t>).gather();
  size_t num_row = 0;
  for(size_t i = 0; i < local_num_rows.size(); i++)
    num_row += local_num_rows[i];
  shrinked.set_num(num_row, mat.num_col - word_to_remove.size());

  vector<string> shrinked_dictionary, shrinked_article;
  size_t remove_idx = 0;
  for(size_t i = 0; i < dictionary.size(); i++) {
    if(remove_idx < word_to_remove.size() && i == word_to_remove[remove_idx]) {
      remove_idx++;
    } else {
      shrinked_dictionary.push_back(dictionary[i]);
    }
  }

  auto original_local_num_rows =
    mat.data.map(crs_get_local_num_row<int,size_t,size_t>).gather();
  auto local_removed_articles = removed_article_nloc.gather();
  vector<size_t> global_removed_article;
  size_t offset = 0;
  for(size_t i = 0; i < local_removed_articles.size(); i++) {
    for(size_t j = 0; j < local_removed_articles[i].size(); j++) {
      global_removed_article.push_back(local_removed_articles[i][j] + offset);
    }
    offset += original_local_num_rows[i];
  }
  remove_idx = 0;
  for(size_t i = 0; i < article.size(); i++) {
    if(remove_idx < global_removed_article.size()
       && i == global_removed_article[remove_idx]) {
      remove_idx++;
    } else {
      shrinked_article.push_back(article[i]);
    }
  }
  ofstream shrinked_dicstr(output_dictionary.c_str());
  for(auto i: shrinked_dictionary) shrinked_dicstr << i << "\n";
  ofstream shrinked_artstr(output_article.c_str());
  for(auto i: shrinked_article) shrinked_artstr << i << "\n";
  shrinked.save(output_matrix);
}

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

  using namespace boost::program_options;

  options_description opt("option");
  opt.add_options()
    ("help,h", "print help")
    ("input-matrix", value<string>(), "input matrix file")
    ("input-dictionary", value<string>(), "input dictionary file")
    ("input-article", value<string>(), "input article file")
    ("output-matrix", value<string>(), "output matrix file")
    ("output-dictionary", value<string>(), "output dictionary file")
    ("output-article", value<string>(), "output article file")
    ("min-word-used", value<int>(), "words used less than this is omitted")
    ("min-article-length", value<int>(), "article whose words is less than this is omitted ");

  variables_map argmap;
  store(command_line_parser(argc,argv).options(opt).allow_unregistered().
        run(), argmap);
  notify(argmap);

  string input_matrix, input_dictionary, input_article;
  string output_matrix, output_dictionary, output_article;
  int min_word_used, min_article_length;

  if(argmap.count("help")){
    cerr << opt << endl;
    exit(1);
  }

  if(argmap.count("input-matrix")){
    input_matrix = argmap["input-matrix"].as<string>();
  } else {
    cerr << "input matrix file is not specified" << endl;
    cerr << opt << endl;
    exit(1);
  }

  if(argmap.count("input-dictionary")){
    input_dictionary = argmap["input-dictionary"].as<string>();
  } else {
    cerr << "input dictionary file is not specified" << endl;
    cerr << opt << endl;
    exit(1);
  }

  if(argmap.count("input-article")){
    input_article = argmap["input-article"].as<string>();
  } else {
    cerr << "input article file is not specified" << endl;
    cerr << opt << endl;
    exit(1);
  }

  if(argmap.count("output-matrix")){
    output_matrix = argmap["output-matrix"].as<string>();
  } else {
    cerr << "output matrix file is not specified" << endl;
    cerr << opt << endl;
    exit(1);
  }

  if(argmap.count("output-dictionary")){
    output_dictionary = argmap["output-dictionary"].as<string>();
  } else {
    cerr << "output dictionary file is not specified" << endl;
    cerr << opt << endl;
    exit(1);
  }

  if(argmap.count("output-article")){
    output_article = argmap["output-article"].as<string>();
  } else {
    cerr << "output article file is not specified" << endl;
    cerr << opt << endl;
    exit(1);
  }

  if(argmap.count("min-word-used")){
    min_word_used = argmap["min-word-used"].as<int>();
  } else {
    cerr << "min-word-used is not specified" << endl;
    cerr << opt << endl;
    exit(1);
  }

  if(argmap.count("min-article-length")){
    min_article_length = argmap["min-article-length"].as<int>();
  } else {
    cerr << "min-article-length is not specified" << endl;
    cerr << opt << endl;
    exit(1);
  }

  shrink_mat(input_matrix, input_dictionary, input_article,
             output_matrix, output_dictionary, output_article,
             min_word_used, min_article_length);
}


//==============================================================================
//         Copyright 2003 - 2011 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef BOOST_SIMD_TOOLBOX_ARITHMETIC_FUNCTIONS_SIMD_COMMON_HYPOT_HPP_INCLUDED
#define BOOST_SIMD_TOOLBOX_ARITHMETIC_FUNCTIONS_SIMD_COMMON_HYPOT_HPP_INCLUDED

#include <boost/simd/toolbox/arithmetic/functions/hypot.hpp>
#include <boost/simd/include/functions/simd/tofloat.hpp>
#include <boost/simd/include/functions/simd/abs.hpp>
#include <boost/simd/include/functions/simd/min.hpp>
#include <boost/simd/include/functions/simd/max.hpp>
#include <boost/simd/include/functions/simd/is_inf.hpp>
#include <boost/simd/include/functions/simd/is_nan.hpp>
#include <boost/simd/include/functions/simd/logical_and.hpp>
#include <boost/simd/include/functions/simd/logical_or.hpp>
#include <boost/simd/include/functions/simd/plus.hpp>
#include <boost/simd/include/functions/simd/minus.hpp>
#include <boost/simd/include/functions/simd/unary_minus.hpp>
#include <boost/simd/include/functions/simd/multiplies.hpp>
#include <boost/simd/include/functions/simd/bitwise_and.hpp>
#include <boost/simd/include/functions/simd/bitwise_or.hpp>
#include <boost/simd/include/functions/simd/logical_or.hpp>
#include <boost/simd/include/functions/simd/exponent.hpp>
#include <boost/simd/include/functions/simd/ldexp.hpp>
#include <boost/simd/include/functions/simd/if_else.hpp>
#include <boost/simd/include/functions/simd/is_greater.hpp>
#include <boost/simd/include/functions/simd/is_less.hpp>
#include <boost/simd/include/functions/simd/sqrt.hpp>
#include <boost/simd/include/functions/simd/any.hpp>
#include <boost/simd/include/functions/simd/bitwise_cast.hpp>
#include <boost/simd/include/constants/zero.hpp>
#include <boost/simd/include/constants/inf.hpp>
#include <boost/simd/include/functions/simd/max.hpp>
#include <boost/simd/include/constants/int_splat.hpp>
#include <boost/simd/sdk/simd/logical.hpp>
#include <boost/dispatch/meta/as_floating.hpp>
#include <boost/dispatch/meta/as_integer.hpp>

namespace boost { namespace simd { namespace ext
{
  BOOST_SIMD_FUNCTOR_IMPLEMENTATION( boost::simd::tag::hypot_, tag::cpu_
                            , (A0)(X)
                            , ((simd_<arithmetic_<A0>,X>))((simd_<arithmetic_<A0>,X>))
                            )
  {

    typedef typename dispatch::meta::as_floating<A0>::type result_type;

    BOOST_SIMD_FUNCTOR_CALL_REPEAT(2)
    {
      return boost::simd::hypot(tofloat(a0), tofloat(a1));
    }
  };

  BOOST_SIMD_FUNCTOR_IMPLEMENTATION(boost::simd::tag::hypot_, tag::cpu_,
                              (A0)(X),
                              ((simd_<floating_<A0>,X>))
                              ((simd_<floating_<A0>,X>))
                            )
  {
    template < class T, class I = typename dispatch::meta::as_integer<T, signed>::type>
    struct hypot_ctnts {};

    template <class I, class CAT>
    struct hypot_ctnts<simd::native<float, CAT>, I>
    {
      typedef I  int_type;
      static inline int_type C1(){ return boost::simd::integral_constant<int_type, 50>();};
      static inline int_type C2(){ return boost::simd::integral_constant<int_type, 60>();};
      static inline int_type MC1(){ return boost::simd::integral_constant<int_type, -50>();};
      static inline int_type MC2(){ return boost::simd::integral_constant<int_type, -60>();};
      static inline int_type C3(){ return boost::simd::integral_constant<int_type, 0x00800000>();};
      static inline int_type M1(){ return boost::simd::integral_constant<int_type, 0xfffff000>();};
    };

    template <class I, class CAT>
    struct hypot_ctnts<simd::native<double, CAT>, I>
    {
      typedef I  int_type;
      static inline int_type C1(){ return boost::simd::integral_constant<int_type, 500>();};
      static inline int_type C2(){ return boost::simd::integral_constant<int_type, 600>();};
      static inline int_type MC1(){ return boost::simd::integral_constant<int_type, -500>();};
      static inline int_type MC2(){ return boost::simd::integral_constant<int_type, -600>();};
      static inline int_type C3(){ return boost::simd::integral_constant<int_type, 0x0010000000000000ll>();}
      static inline int_type M1(){ return boost::simd::integral_constant<int_type, 0xffffffff00000000ll>();};
    };

    typedef typename dispatch::meta::as_floating<A0>::type result_type;

    BOOST_SIMD_FUNCTOR_CALL_REPEAT(2)
    {
      typedef typename dispatch::meta::as_integer<result_type>::type itype;
      result_type r =  boost::simd::abs(a0);
      result_type i =  boost::simd::abs(a1);
      itype e =  exponent(boost::simd::max(i, r));
      return  if_else( logical_or(logical_and(is_nan(a0), is_inf(a1)),
                                  logical_and(is_nan(a1), is_inf(a0))),
                       Inf<result_type>(),
                       ldexp(sqrt(sqr(ldexp(r, -e))+sqr(ldexp(i, -e))), e)
                       );
//       typedef typename meta::as_logical<A0>::type bA0;
//       typedef typename dispatch::meta::as_integer<A0, signed>::type int_type;
//       typedef typename meta::as_logical<int_type>::type bint_type;
//       typedef hypot_ctnts<A0, int_type> cts;
//       A0 x =  boost::simd::abs(a0);
//       A0 y =  boost::simd::abs(a1);
//       bA0 tinf = is_inf(x+y);
//       A0 a =  boost::simd::max(x, y);
//       A0 b =  boost::simd::min(x, y);
//       int_type ea =   exponent(a);
//       int_type eb  =  exponent(b);
//       bint_type te1 = gt(ea,cts::C1());
//       bint_type te2 = lt(eb,cts::MC1());
//       bool te3 = boost::simd::any(logical_or(te1, te2));
//       int_type e = Zero<int_type>();
//       if (te3)
//       {
//         e = select(te1, cts::MC2(), e);
//         e = select(te2, cts::C1(),  e);
//         a =  ldexp(a, e);
//         b =  ldexp(b, e);
//       }
//       A0 w = a-b;
//       bA0 test =  gt(w,b);
//       A0 t1 = a& cts::M1();
//       A0 t2 = a-t1;
//       A0 w1_2  = (t1*t1-(b*(-b)-t2*(a+t1)));
//       A0 y1 = b& cts::M1();
//       A0 y2 = b - y1;
//       t1 = bitwise_cast<A0>(bitwise_cast<int_type>(a)+cts::C3()) ;
//       t2 = (a+a) - t1;
//       A0 w2_2  = (t1*y1-(w*(-w)-(t1*y2+t2*b)));
//       w =  select(test, w1_2, w2_2);
//       w = boost::simd::sqrt(w);
//       if (te3) w = ldexp(w, -e);
//       return select(tinf, Inf<A0>(), w);
    }
  };
} } }

#endif


#include <math.h>
#include <sstream>
#include <algorithm>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include "cPathFinderReader.h"
#include "cRunWatch.h"
#include "cMaze.h"

namespace raven
{
    namespace graph
    {

        eCalculation
        cPathFinderReader::open(
            const std::string &fname)
        {
            myFormat = eCalculation::none;
            myFile.close();
            myFile.open(fname);
            if (!myFile.is_open())
                return eCalculation::not_open;
            std::string line;
            getline(myFile, line);
            if (line.find("format") != 0)
                return eCalculation::none;
            if (line.find("costs") != -1)
            {
                costs();
                myFinder.path();
                std::cout << myFinder.pathText() << "\n";
                return eCalculation::costs;
            }
            else if (line.find("water") != -1)
            {
                bool pulse;
                auto t = valves(pulse);
                myFinder.waterValves(t, pulse);
                std::cout << myFinder.pathText() << "\n";
                return eCalculation::costs;
            }
            else if (line.find("spans") != -1)
            {
                costs();
                myFinder.span();
                std::cout << myFinder.spanText() << "\n";
                return eCalculation::spans;
            }
            else if (line.find("sales") != -1)
            {
                raven::set::cRunWatch::Start();
                myFinder.tsp(sales());
                raven::set::cRunWatch::Report();
                return eCalculation::sales;
            }
            else if (line.find("cams") != -1)
            {
                costs(false);
                myFinder.cams();
                return eCalculation::cams;
            }
            else if (line.find("cliques") != -1)
            {
                costs(false);
                return eCalculation::cliques;
            }
            else if (line.find("multiflows") != -1)
            {
                myFormat = eCalculation::multiflows;
                costs();
                myFinder.multiflows();
            }
            else if (line.find("equiflows") != -1)
            {
                myFormat = eCalculation::flows;
                costs();
                myFinder.equiflows();
            }
            else if (line.find("flows") != -1)
            {
                costs();
                myFinder.flows();
                return eCalculation::flows;
            }
            else if (line.find("hills") != -1)
            {
                myFinder.hills(orthogonalGrid());
                return eCalculation::hills;
            }
            else if (line.find("prerequisites") != -1)
            {
                myFormat = eCalculation::reqs;
                myFinder.PreReqs(singleParentTree());
            }
            else if (line.find("karupTimer") != -1)
            {
                KarupRandom();
                raven::set::cRunWatch::Start();
                myFinder.karup();
                raven::set::cRunWatch::Report();
            }
            else if (line.find("karup") != -1)
            {
                nodeCosts();
                myFinder.karup();
            }
            else if (line.find("amazon") != -1)
            {
                std::cout << "->amazon\n";
                links();
                std::cout << "<-amazon\n";
                raven::set::cRunWatch::Start();
                {
                    raven::set::cRunWatch aWatcher("DFS");
                    myFinder.depthFirst(0, [](int v) {});
                }
                raven::set::cRunWatch::Report();
            }
            else if (line.find("bonesi") != -1)
            {
                myFormat = eCalculation::bonesi;
                bonesi();
            }
            else if (line.find("collision") != -1)
            {
                collision();
                myFinder.collision();
                if (myFinder.nodeCount() < 100)
                    myFormat = eCalculation::costs;
                else
                {
                    // suppress visual display of large graphs
                    myFormat = eCalculation::collision;
                }
            }
            else if (line.find("maze") != -1)
            {
                cMaze maze;
                maze.read( myFile );
                maze.graph( myFinder );
                myFinder.path();
                myFormat = eCalculation::costs;
            }
            return myFormat;
        }

        std::vector<std::string> cPathFinderReader::ParseSpaceDelimited(
            const std::string &l)
        {
            std::vector<std::string> token;
            std::stringstream sst(l);
            std::string a;
            while (getline(sst, a, ' '))
                token.push_back(a);

            token.erase(
                remove_if(
                    token.begin(),
                    token.end(),
                    [](std::string t)
                    {
                        return (t.empty());
                    }),
                token.end());

            return token;
        }

        void cPathFinderReader::costs(
            bool weights,
            bool directed)
        {
            myFinder.clear();
            if (directed)
                myFinder.directed();
            int cost;
            int maxNegCost = 0;
            std::string line;
            while (std::getline(myFile, line))
            {
                std::cout << line << "\n";
                auto token = ParseSpaceDelimited(line);
                if (!token.size())
                    continue;
                switch (token[0][0])
                {
                case 'g':
                    if (myFinder.linkCount())
                        throw std::runtime_error("cPathFinderReader:: g ( directed ) must occurr before any links");
                    myFinder.directed();
                    break;

                case 'l':
                    if (weights && (token.size() != 4))
                        throw std::runtime_error("cPathFinder::read bad link line");
                    else if (3 > token.size() || token.size() > 4)
                        throw std::runtime_error("cPathFinder::read bad link line");
                    if (weights)
                        cost = atof(token[3].c_str());
                    else
                        cost = 1;
                    if (cost < maxNegCost)
                        maxNegCost = cost;
                    myFinder.addLink(
                        token[1],
                        token[2],
                        cost);
                    break;

                case 's':
                    if (token.size() != 2)
                        throw std::runtime_error("cPathFinder::read bad start line");
                    if (myFormat == eCalculation::multiflows)
                        myFinder.addSource(token[1]);
                    else
                        myFinder.start(token[1]);
                    break;

                case 'e':
                    if (token.size() != 2)
                        throw std::runtime_error("cPathFinder::read bad end line");
                    myFinder.end(token[1]);
                    break;
                }
            }
            if (maxNegCost < 0)
            {
                std::cout << "Negative link costs present\n"
                          << "Adding positive offset to all link costs\n";
                //        myFinder.makeCostsPositive(maxNegCost);
            }

            //std::cout << "<-costs\n" <<myFinder.linksText() << "\n";
        }

        std::vector<int> cPathFinderReader::valves(
            bool &pulse)
        {
            std::vector<int> valveTimes;
            myFinder.clear();

            int cost;
            int maxNegCost = 0;
            std::string line;
            while (std::getline(myFile, line))
            {
                std::cout << line << "\n";
                auto token = ParseSpaceDelimited(line);
                if (!token.size())
                    continue;
                switch (token[0][0])
                {
                case 'g':
                    if (myFinder.linkCount())
                        throw std::runtime_error("cPathFinderReader:: g ( directed ) must occurr before any links");
                    myFinder.directed();
                    break;

                case 'l':
                    // link
                    if (valveTimes.size())
                        throw std::runtime_error("cPathFinder::read valve times must be after pipe specs");
                    if (token.size() != 4)
                        throw std::runtime_error("cPathFinder::read bad link line");

                    cost = atof(token[3].c_str());

                    if (cost < maxNegCost)
                        maxNegCost = cost;
                    myFinder.addLink(
                        token[1],
                        token[2],
                        cost);
                    break;

                case 's':
                    if (token.size() != 2)
                        throw std::runtime_error("cPathFinder::read bad start line");
                    if (myFormat == eCalculation::multiflows)
                        myFinder.addSource(token[1]);
                    else
                        myFinder.start(token[1]);
                    break;

                case 'e':
                    if (token.size() != 2)
                        throw std::runtime_error("cPathFinder::read bad end line");
                    myFinder.end(token[1]);
                    break;

                case 't':
                    // valve timings
                    if (token.size() < 2)
                        throw std::runtime_error("cPathFinder::read bad time line");
                    token.erase(token.begin());
                    if (token[0][0] == 'p')
                        pulse = true;
                    else if (token[0][0] == 'a')
                        pulse = false;
                    else
                        throw std::runtime_error("cPathFinder::read valve times must specify 'pulse' or 'always'");
                    token.erase(token.begin());
                    valveTimes.resize(myFinder.nodeCount());
                    int nodeIndex = 1;
                    for (auto &s : token)
                        valveTimes[myFinder.find(std::to_string(nodeIndex++))] = atoi(s.c_str());
                    break;
                }
            }
            if (maxNegCost < 0)
            {
                std::cout << "Negative link costs present\n"
                          << "Adding positive offset to all link costs\n";
                //        myFinder.makeCostsPositive(maxNegCost);
            }

            //std::cout << "<-costs\n" <<myFinder.linksText() << "\n";

            return valveTimes;
        }

        void cPathFinderReader::links()
        {
            myFinder.clear();
            myFinder.directed();
            myFinder.makeNodes(403394);
            std::string line;
            while (std::getline(myFile, line))
            {
                //std::cout << line << " | ";
                auto token = ParseSpaceDelimited(line);
                myFinder.addLinkFast(
                    atoi(token[0].c_str()),
                    atoi(token[1].c_str()));
            }
            //std::cout << "<-costs\n" <<myFinder.linksText() << "\n";
        }
        void cPathFinderReader::bonesi()
        {
            myFinder.clear();
            myFinder.directed();

            // read  JSON into property tree
            boost::property_tree::ptree tree;
            read_json(myFile, tree);

            // loop over source nodes
            for (auto &src : tree)
            {
                // loop over destination nodes
                for (auto &dst : tree.get_child(src.first).get_child("next"))
                {
                    // add link between source and destination
                    myFinder.addLink(src.first, dst.second.data());
                }
            }
        }
        void cPathFinderReader::nodeCosts()
        {
            myFinder.clear();
            int cost;
            std::string line;
            while (std::getline(myFile, line))
            {
                std::cout << line << "\n";
                auto token = ParseSpaceDelimited(line);
                if (!token.size())
                    continue;
                switch (token[0][0])
                {
                case 'n':
                    if (token.size() != 3)
                        throw std::runtime_error(
                            "bad node line");
                    myFinder.node(myFinder.findoradd(token[1])).myCost = atoi(token[2].c_str());
                    break;
                case 'l':
                    if (token.size() != 3)
                        throw std::runtime_error(
                            "bad link line");
                    myFinder.addLink(
                        token[1],
                        token[2],
                        1);
                    break;
                }
            }
        }

        void cPathFinderReader::KarupRandom()
        {
            const int nodeCount = 5e5;

            myFinder.makeNodes(2 * nodeCount);

            // generate a and b nodes
            for (int x = 0; x < nodeCount; x++)
            {
                myFinder.node(x).myName = "a" + std::to_string(x);
                myFinder.node(nodeCount + x).myName = "b" + std::to_string(x);
            }
            // link each b node to two random a nodes
            for (int x = 0; x < nodeCount; x++)
            {
                int a = rand() % (int)nodeCount;
                myFinder.addLink(nodeCount + x, a);
                a = rand() % (int)nodeCount;
                myFinder.addLink(nodeCount + x, a);
            }

            //std::cout << myFinder.linksText();
        }

        void cPathFinderReader::collision()
        {
            std::string line;
            getline(myFile, line);
            auto token = ParseSpaceDelimited(line);
            if (!token.size())
                throw std::runtime_error(
                    "cPathFinderReader::collision input format error");
            if (token[0] == "random")
            {
                const int nodeCount = atoi(token[1].c_str());
                myFinder.directed();
                myFinder.makeNodes(nodeCount);

                // link each node to one other chosen at random
                for (int x = 0; x < nodeCount; x++)
                    myFinder.addLink(x, rand() % nodeCount);
            }
            else
                costs(false,true);
        }

        std::vector<int> cPathFinderReader::sales()
        {
            struct sCity
            {
                int x;
                int y;
                std::string name;
            };
            std::vector<sCity> vCity;
            sCity city;

            std::vector<int> visit;

            enum class eInput
            {
                unknown,
                city,
                link,
                manhatten,
            } input = eInput::unknown;

            myFinder.clear();

            std::string line;
            while (std::getline(myFile, line))
            {
                std::cout << line << "\n";
                auto token = ParseSpaceDelimited(line);
                if (!token.size())
                    continue;
                if (token.size() != 4)
                    throw std::runtime_error("Input line must have 4 tokens");

                switch (token[0][0])
                {
                case 'm':
                    input = eInput::manhatten;
                    break;
                case 'c':
                    if (input == eInput::unknown)
                        input = eInput::city;
                    else if (input != eInput::city && input != eInput::manhatten)
                        throw std::runtime_error("Mixed input formats");
                    city.x = atoi(token[1].c_str());
                    city.y = atoi(token[2].c_str());
                    city.name = token[3];
                    vCity.push_back(city);
                    break;
                case 'l':
                    if (input == eInput::unknown)
                        input = eInput::link;
                    else if (input == eInput::city)
                        throw std::runtime_error("Mixed input formats");
                    myFinder.addLink(
                        myFinder.findoradd(token[1]),
                        myFinder.findoradd(token[2]),
                        atoi(token[3].c_str()));
                    break;
                case 'v':
                    token.erase(token.begin());
                    if (token.size() > myFinder.nodeCount())
                        throw std::runtime_error("More visit requests than nodes");
                    for (auto &v : token)
                    {
                        int iv = myFinder.find(v);
                        if (iv < 0)
                            throw std::runtime_error("Visit request to non existent node");
                        v.push_back(iv);
                    }
                    break;
                default:
                    std::cout << "ignored\n";
                    break;
                }
            }

            switch (input)
            {
            case eInput::city:
            {
                // link all the cities by the pythagorian distance between them
                for (int c1 = 0; c1 < vCity.size(); c1++)
                {
                    for (int c2 = c1 + 1; c2 < vCity.size(); c2++)
                    {
                        float dx = vCity[c1].x - vCity[c2].x;
                        float dy = vCity[c1].y - vCity[c2].y;
                        float d = sqrt(dx * dx + dy * dy);
                        // std::cout << vCity[c1].name << " " << vCity[c2].name << " "
                        //           << dx << " " << dy << " " << d << "\n";
                        myFinder.addLink(
                            vCity[c1].name,
                            vCity[c2].name,
                            d);
                    }
                }
            }
            break;

            case eInput::manhatten:
            {
                raven::set::cRunWatch aWatcher("CalculateManhattenDistances");
                for (int c1 = 0; c1 < vCity.size(); c1++)
                {
                    for (int c2 = 0; c2 < vCity.size(); c2++)
                    {
                        if (vCity[c1].name == vCity[c2].name)
                            continue;
                        // manhatten distance
                        float d =
                            fabs(vCity[c1].x - vCity[c2].x) + fabs(vCity[c1].y - vCity[c2].y);
                        myFinder.addLink(
                            vCity[c2].name,
                            vCity[c1].name,
                            d);
                    }
                }
            }
            break;

            default:
            {
                // add links with infinite cost between unlinked cities
                // myFinder.makeComplete();
            }
            break;
            }

            return visit;
        }
        std::vector<std::vector<float>> cPathFinderReader::orthogonalGrid()
        {
            if (!myFile.is_open())
                throw std::runtime_error(
                    "cPathFinderReader::orthogonalGrid file not open");

            const bool fDirected = true;

            myFinder.clear();
            myFinder.directed(fDirected);

            std::vector<std::vector<float>> grid;
            int RowCount = 0;
            int ColCount = -1;
            int start = -1;
            int end = -1;
            std::string line;
            while (std::getline(myFile, line))
            {
                std::cout << line << "\n";
                auto token = ParseSpaceDelimited(line);
                if (!token.size())
                    continue;
                switch (token[0][0])
                {
                case 'o':
                {
                    if (ColCount == -1)
                        ColCount = token.size() - 1;
                    else if (token.size() - 1 != ColCount)
                        throw std::runtime_error("Bad column count");
                    std::vector<float> row;
                    for (int k = 1; k < token.size(); k++)
                        row.push_back(atof(token[k].c_str()));
                    grid.push_back(row);
                }
                break;
                case 's':
                    if (token.size() != 3)
                        throw std::runtime_error("Bad start");
                    if (ColCount == -1)
                        throw std::runtime_error("Start node must be at end");
                    start =
                        (atoi(token[2].c_str()) - 1) * ColCount + atoi(token[1].c_str()) - 1;
                    break;
                case 'e':
                    if (token.size() != 3)
                        throw std::runtime_error("Bad end");
                    if (ColCount == -1)
                        throw std::runtime_error("End node must be at end");
                    end =
                        (atoi(token[2].c_str()) - 1) * ColCount + atoi(token[1].c_str()) - 1;
                    break;
                }
            }
            RowCount = grid.size();
            // std::cout << "<-cHillPathFinder::read " << myRowCount << "\n";

            // add nodes at each grid cell
            for (int row = 0; row < RowCount; row++)
            {
                for (int col = 0; col < ColCount; col++)
                {
                    int n = myFinder.findoradd(
                        myFinder.orthogonalGridNodeName(row, col));
                }
            }
            myFinder.start(start);
            myFinder.end(end);

            // link cells orthogonally
            for (int row = 0; row < RowCount; row++)
                for (int col = 0; col < ColCount; col++)
                {
                    int n = row * ColCount + col;

                    if (fDirected)
                    {
                        if (col > 0)
                        {
                            int left = row * ColCount + col - 1;
                            myFinder.addLink(n, left, 1);
                        }
                    }
                    if (col < ColCount - 1)
                    {
                        int right = row * ColCount + col + 1;
                        myFinder.addLink(n, right, 1);
                    }
                    if (fDirected)
                    {
                        if (row > 0)
                        {
                            int up = (row - 1) * ColCount + col;
                            myFinder.addLink(n, up, 1);
                        }
                    }
                    if (row < RowCount - 1)
                    {
                        int down = (row + 1) * ColCount + col;
                        myFinder.addLink(n, down, 1);
                    }
                }
            return grid;
        }
        std::vector<std::string> cPathFinderReader::singleParentTree()
        {
            myFinder.clear();
            if (!myFile.is_open())
                throw std::runtime_error(
                    "cPathFinderReader::singleParentTree file not open");
            std::string line;
            while (std::getline(myFile, line))
            {
                auto token = ParseSpaceDelimited(line);
                if (!token.size())
                    continue;
                switch (token[0][0])
                {

                case 't':
                {
                    token.erase(token.begin());
                    int child = 0;
                    for (auto &t : token)
                    {
                        if (!child)
                        {
                            child++;
                            continue;
                        }
                        myFinder.addLink(
                            myFinder.findoradd(t),
                            myFinder.findoradd(std::to_string(child++)),
                            1);
                    }
                }
                break;

                case 'a':
                    token.erase(token.begin());
                    return token;
                }
            }
            throw std::runtime_error("no A input");
        }
    }
}