// Copyright (C) 2006 The Trustees of Indiana University.

// Use, modification and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

//  Authors: Brian Barrett
//           Nick Edmonds
//           Andrew Lumsdaine

#include <boost/graph/use_mpi.hpp>
#include <boost/config.hpp>
#include <boost/throw_exception.hpp>
#include <boost/graph/distributed/adjacency_list.hpp>
#include <boost/property_map/parallel/distributed_property_map.hpp>
#include <boost/graph/distributed/mpi_process_group.hpp>
#include <boost/graph/distributed/concepts.hpp>
#include <boost/graph/distributed/connected_components_parallel_search.hpp>
#include <boost/graph/distributed/connected_components.hpp>
#include <boost/graph/rmat_graph_generator.hpp>
#include <boost/random/linear_congruential.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/property_map/vector_property_map.hpp>

#include <iostream>
#include <cstdlib>
#include <iomanip>

#ifdef BOOST_NO_EXCEPTIONS
void
boost::throw_exception(std::exception const& ex)
{
  std::cout << ex.what() << std::endl;
  abort();
}
#endif

using namespace boost;
using boost::graph::distributed::mpi_process_group;

typedef double time_type;

inline time_type get_time()
{
  return MPI_Wtime();
}

std::string print_time(time_type t)
{
  std::ostringstream out;
  out << std::setiosflags(std::ios::fixed) << std::setprecision(2) << t;
  return out.str();
}

void
test_filtered_rmat_cc(int n, int m, double a, double b, double c, double d)
{
  mpi_process_group pg;
  std::size_t id = process_id(pg);

  if (id == 0) printf("INFO: Params: n=%d, m=%d, a=%f, b=%f, c=%f, d=%f.\n",
                      n, m, a, b, c, d);

  typedef parallel::variant_distribution<mpi_process_group> Distribution;
  Distribution distrib = parallel::block(pg, n);

  typedef adjacency_list<vecS,
        distributedS<mpi_process_group, vecS>,
        undirectedS> Graph;

  typedef scalable_rmat_iterator<mpi_process_group, Distribution, rand48, Graph>
    RMATIter;

  if (id == 0) printf("INFO: Generating graph.\n");

  rand48 gen;
  time_type gen_start = get_time();
  Graph g(RMATIter(pg, distrib, gen, n, m, a, b, c, d, true),
          RMATIter(), n, pg, distrib);
  time_type gen_end = get_time();
  std::cout << "INFO: Graph Gen time: " << print_time(gen_end - gen_start) << std::endl;

  synchronize(g);

  if (id == 0) printf("INFO: Starting connected components.\n");

  std::vector<int> local_components_vec(num_vertices(g));
  typedef iterator_property_map<std::vector<int>::iterator, property_map<Graph, vertex_index_t>::type> ComponentMap;
  ComponentMap component(local_components_vec.begin(), get(vertex_index, g));

  time_type start = get_time();
  int num_components = connected_components_ps(g, component);
  time_type end = get_time();

  if (process_id(g.process_group()) == 0) {
      std::cout << "INFO: Test Complete. components found = " << num_components
                << ", time = " << print_time(end - start) << "s." << std::endl;
      printf("RESULT: %d %d %d %f %f %f %f %f\n",
             num_processes(pg), n, m, a, b, c, d, end - start);
  }

}

int
main(int argc, char* argv[])
{
  mpi::environment env(argc, argv);

  if (argc < 7)
    test_filtered_rmat_cc(40, 200, 0.58, 0.19, 0.19, 0.04);
  else
    test_filtered_rmat_cc(atoi(argv[1]), atoi(argv[2]),
                          atof(argv[3]), atof(argv[4]),
                          atof(argv[5]), atof(argv[6]));

  return 0;
}


//
//  Copyright (C) 2004-2019 Greg Landrum and Rational Discovery LLC
//
//   @@ All Rights Reserved @@
//  This file is part of the RDKit.
//  The contents are covered by the terms of the BSD license
//  which is included in the file license.txt, found at the root
//  of the RDKit source tree.
//
#include "BoundsMatrix.h"
#include "DistGeomUtils.h"
#include "DistViolationContrib.h"
#include "ChiralViolationContrib.h"
#include "FourthDimContrib.h"
#include <Numerics/Matrix.h>
#include <Numerics/SymmMatrix.h>
#include <Numerics/Vector.h>
#include <RDGeneral/Invariant.h>
#include <Numerics/EigenSolvers/PowerEigenSolver.h>
#include <RDGeneral/utils.h>
#include <ForceField/ForceField.h>
#include <ForceField/UFF/DistanceConstraint.h>
#include <ForceField/UFF/AngleConstraint.h>
#include <ForceField/UFF/Inversion.h>
#include <GraphMol/ForceFieldHelpers/CrystalFF/TorsionPreferences.h>
#include <GraphMol/ForceFieldHelpers/CrystalFF/TorsionAngleM6.h>
#include <boost/dynamic_bitset.hpp>

namespace DistGeom {
const double EIGVAL_TOL = 0.001;

double pickRandomDistMat(const BoundsMatrix &mmat,
                         RDNumeric::SymmMatrix<double> &distMat, int seed) {
  if (seed > 0) {
    RDKit::getRandomGenerator(seed);
  }
  return pickRandomDistMat(mmat, distMat, RDKit::getDoubleRandomSource());
}

double pickRandomDistMat(const BoundsMatrix &mmat,
                         RDNumeric::SymmMatrix<double> &distMat,
                         RDKit::double_source_type &rng) {
  // make sure the sizes match up
  unsigned int npt = mmat.numRows();
  CHECK_INVARIANT(npt == distMat.numRows(), "Size mismatch");

  double largestVal = -1.0;
  double *ddata = distMat.getData();
  for (unsigned int i = 1; i < npt; i++) {
    unsigned int id = i * (i + 1) / 2;
    for (unsigned int j = 0; j < i; j++) {
      double ub = mmat.getUpperBound(i, j);
      double lb = mmat.getLowerBound(i, j);
      CHECK_INVARIANT(ub >= lb, "");
      double rval = rng();
      // std::cerr<<i<<"-"<<j<<": "<<rval<<std::endl;
      double d = lb + (rval) * (ub - lb);
      ddata[id + j] = d;
      if (d > largestVal) {
        largestVal = d;
      }
    }
  }
  return largestVal;
}

bool computeInitialCoords(const RDNumeric::SymmMatrix<double> &distMat,
                          RDGeom::PointPtrVect &positions, bool randNegEig,
                          unsigned int numZeroFail, int seed) {
  if (seed > 0) {
    RDKit::getRandomGenerator(seed);
  }
  return computeInitialCoords(distMat, positions,
                              RDKit::getDoubleRandomSource(), randNegEig,
                              numZeroFail);
}
bool computeInitialCoords(const RDNumeric::SymmMatrix<double> &distMat,
                          RDGeom::PointPtrVect &positions,
                          RDKit::double_source_type &rng, bool randNegEig,
                          unsigned int numZeroFail) {
  unsigned int N = distMat.numRows();
  unsigned int nPt = positions.size();
  CHECK_INVARIANT(nPt == N, "Size mismatch");

  unsigned int dim = positions.front()->dimension();

  const double *data = distMat.getData();
  RDNumeric::SymmMatrix<double> sqMat(N), T(N, 0.0);
  RDNumeric::DoubleMatrix eigVecs(dim, N);
  RDNumeric::DoubleVector eigVals(dim);

  double *sqDat = sqMat.getData();

  unsigned int dSize = distMat.getDataSize();
  double sumSqD2 = 0.0;
  for (unsigned int i = 0; i < dSize; i++) {
    sqDat[i] = data[i] * data[i];
    sumSqD2 += sqDat[i];
  }
  sumSqD2 /= (N * N);

  RDNumeric::DoubleVector sqD0i(N, 0.0);
  double *sqD0iData = sqD0i.getData();
  for (unsigned int i = 0; i < N; i++) {
    for (unsigned int j = 0; j < N; j++) {
      sqD0iData[i] += sqMat.getVal(i, j);
    }
    sqD0iData[i] /= N;
    sqD0iData[i] -= sumSqD2;

    if ((sqD0iData[i] < EIGVAL_TOL) && (N > 3)) {
      return false;
    }
  }

  for (unsigned int i = 0; i < N; i++) {
    for (unsigned int j = 0; j <= i; j++) {
      double val = 0.5 * (sqD0iData[i] + sqD0iData[j] - sqMat.getVal(i, j));
      T.setVal(i, j, val);
    }
  }
  unsigned int nEigs = (dim < N) ? dim : N;
  RDNumeric::EigenSolvers::powerEigenSolver(nEigs, T, eigVals, eigVecs,
                                            (int)(sumSqD2 * N));

  double *eigData = eigVals.getData();
  bool foundNeg = false;
  unsigned int zeroEigs = 0;
  for (unsigned int i = 0; i < dim; i++) {
    if (eigData[i] > EIGVAL_TOL) {
      eigData[i] = sqrt(eigData[i]);
    } else if (fabs(eigData[i]) < EIGVAL_TOL) {
      eigData[i] = 0.0;
      zeroEigs++;
    } else {
      foundNeg = true;
    }
  }
  if ((foundNeg) && (!randNegEig)) {
    return false;
  }

  if ((zeroEigs >= numZeroFail) && (N > 3)) {
    return false;
  }

  for (unsigned int i = 0; i < N; i++) {
    RDGeom::Point *pt = positions[i];
    for (unsigned int j = 0; j < dim; ++j) {
      if (eigData[j] >= 0.0) {
        (*pt)[j] = eigData[j] * eigVecs.getVal(j, i);
      } else {
        // std::cerr<<"!!! "<<i<<"-"<<j<<": "<<eigData[j]<<std::endl;
        (*pt)[j] = 1.0 - 2.0 * rng();
      }
    }
  }
  return true;
}

bool computeRandomCoords(RDGeom::PointPtrVect &positions, double boxSize,
                         int seed) {
  if (seed > 0) {
    RDKit::getRandomGenerator(seed);
  }
  return computeRandomCoords(positions, boxSize,
                             RDKit::getDoubleRandomSource());
}
bool computeRandomCoords(RDGeom::PointPtrVect &positions, double boxSize,
                         RDKit::double_source_type &rng) {
  CHECK_INVARIANT(boxSize > 0.0, "bad boxSize");

  for (auto pt : positions) {
    for (unsigned int i = 0; i < pt->dimension(); ++i) {
      (*pt)[i] = boxSize * (rng() - 0.5);
    }
  }
  return true;
}

ForceFields::ForceField *constructForceField(
    const BoundsMatrix &mmat, RDGeom::PointPtrVect &positions,
    const VECT_CHIRALSET &csets, double weightChiral, double weightFourthDim,
    std::map<std::pair<int, int>, double> *extraWeights, double basinSizeTol,
    boost::dynamic_bitset<> *fixedPts) {
  unsigned int N = mmat.numRows();
  CHECK_INVARIANT(N == positions.size(), "");
  auto *field = new ForceFields::ForceField(positions[0]->dimension());
  for (unsigned int i = 0; i < N; i++) {
    field->positions().push_back(positions[i]);
  }

  for (unsigned int i = 1; i < N; i++) {
    for (unsigned int j = 0; j < i; j++) {
      if (fixedPts != nullptr && (*fixedPts)[i] && (*fixedPts)[j]) {
        continue;
      }
      double w = 1.0;
      double l = mmat.getLowerBound(i, j);
      double u = mmat.getUpperBound(i, j);
      bool includeIt = false;
      if (extraWeights) {
        std::map<std::pair<int, int>, double>::const_iterator mapIt;
        mapIt = extraWeights->find(std::make_pair(i, j));
        if (mapIt != extraWeights->end()) {
          w = mapIt->second;
          includeIt = true;
        }
      }
      if (u - l <= basinSizeTol) {
        includeIt = true;
      }
      if (includeIt) {
        auto *contrib = new DistViolationContrib(field, i, j, u, l, w);
        field->contribs().push_back(ForceFields::ContribPtr(contrib));
      }
    }
  }

  // now add chiral constraints
  if (weightChiral > 1.e-8) {
    for (const auto &cset : csets) {
      auto *contrib =
          new ChiralViolationContrib(field, cset.get(), weightChiral);
      field->contribs().push_back(ForceFields::ContribPtr(contrib));
    }
  }

  // finally the contribution from the fourth dimension if we need to
  if ((field->dimension() == 4) && (weightFourthDim > 1.e-8)) {
    for (unsigned int i = 0; i < N; i++) {
      auto *contrib = new FourthDimContrib(field, i, weightFourthDim);
      field->contribs().push_back(ForceFields::ContribPtr(contrib));
    }
  }
  return field;
}  // constructForceField

ForceFields::ForceField *construct3DForceField(
    const BoundsMatrix &mmat, RDGeom::Point3DPtrVect &positions,
    const ForceFields::CrystalFF::CrystalFFDetails &etkdgDetails) {
  unsigned int N = mmat.numRows();
  CHECK_INVARIANT(N == positions.size(), "");
  CHECK_INVARIANT(etkdgDetails.expTorsionAtoms.size() ==
                      etkdgDetails.expTorsionAngles.size(),
                  "");
  auto *field = new ForceFields::ForceField(positions[0]->dimension());
  for (unsigned int i = 0; i < N; ++i) {
    field->positions().push_back(positions[i]);
  }

  // keep track which atoms are 1,2- or 1,3-restrained
  boost::dynamic_bitset<> atomPairs(N * N);

  // torsion constraints
  for (unsigned int t = 0; t < etkdgDetails.expTorsionAtoms.size(); ++t) {
    int i = etkdgDetails.expTorsionAtoms[t][0];
    int j = etkdgDetails.expTorsionAtoms[t][1];
    int k = etkdgDetails.expTorsionAtoms[t][2];
    int l = etkdgDetails.expTorsionAtoms[t][3];
    if (i < j)
      atomPairs[i * N + j] = 1;
    else
      atomPairs[j * N + i] = 1;
    // etkdgDetails.expTorsionAngles[t][0] = (signs, V's)
    auto *contrib = new ForceFields::CrystalFF::TorsionAngleContribM6(
        field, i, j, k, l, etkdgDetails.expTorsionAngles[t].second,
        etkdgDetails.expTorsionAngles[t].first);
    field->contribs().push_back(ForceFields::ContribPtr(contrib));
  }  // torsion constraints

  // improper torsions / out-of-plane bend / inversion
  double oobForceScalingFactor = 10.0;
  for (const auto &improperAtom : etkdgDetails.improperAtoms) {
    std::vector<int> n(4);
    for (unsigned int i = 0; i < 3; ++i) {
      n[1] = 1;
      switch (i) {
        case 0:
          n[0] = 0;
          n[2] = 2;
          n[3] = 3;
          break;

        case 1:
          n[0] = 0;
          n[2] = 3;
          n[3] = 2;
          break;

        case 2:
          n[0] = 2;
          n[2] = 3;
          n[3] = 0;
          break;
      }
      auto *contrib = new ForceFields::UFF::InversionContrib(
          field, improperAtom[n[0]], improperAtom[n[1]], improperAtom[n[2]],
          improperAtom[n[3]], improperAtom[4],
          static_cast<bool>(improperAtom[5]), oobForceScalingFactor);
      field->contribs().push_back(ForceFields::ContribPtr(contrib));
    }
  }

  double fdist = 100.0;  // force constant
  // 1,2 distance constraints
  for (const auto &bnd : etkdgDetails.bonds) {
    unsigned int i = bnd.first;
    unsigned int j = bnd.second;
    if (i < j)
      atomPairs[i * N + j] = 1;
    else
      atomPairs[j * N + i] = 1;
    double d = ((*positions[i]) - (*positions[j])).length();
    double l = d - 0.01;
    double u = d + 0.01;
    auto *contrib = new ForceFields::UFF::DistanceConstraintContrib(
        field, i, j, l, u, fdist);
    field->contribs().push_back(ForceFields::ContribPtr(contrib));
  }

  // 1,3 distance constraints
  for (const auto &angle : etkdgDetails.angles) {
    unsigned int i = angle[0];
    unsigned int j = angle[1];
    unsigned int k = angle[2];
    if (i < j)
      atomPairs[i * N + j] = 1;
    else
      atomPairs[j * N + i] = 1;
    // check for triple bonds
    if (angle[3]) {
      auto *contrib = new ForceFields::UFF::AngleConstraintContrib(
          field, i, j, k, 179.0, 180.0, fdist);
      field->contribs().push_back(ForceFields::ContribPtr(contrib));
    } else {
      double d = ((*positions[i]) - (*positions[k])).length();
      double l = d - 0.01;
      double u = d + 0.01;
      auto *contrib = new ForceFields::UFF::DistanceConstraintContrib(
          field, i, k, l, u, fdist);
      field->contribs().push_back(ForceFields::ContribPtr(contrib));
    }
  }

  // minimum distance for all other atom pairs
  fdist = 10.0;
  for (unsigned int i = 1; i < N; ++i) {
    for (unsigned int j = 0; j < i; ++j) {
      if (!atomPairs[j * N + i]) {
        double l = mmat.getLowerBound(i, j);
        double u = mmat.getUpperBound(i, j);
        auto *contrib = new ForceFields::UFF::DistanceConstraintContrib(
            field, i, j, l, u, fdist);
        field->contribs().push_back(ForceFields::ContribPtr(contrib));
      }
    }
  }

  return field;
}  // construct3DForceField

ForceFields::ForceField *constructPlain3DForceField(
    const BoundsMatrix &mmat, RDGeom::Point3DPtrVect &positions,
    const ForceFields::CrystalFF::CrystalFFDetails &etkdgDetails) {
  unsigned int N = mmat.numRows();
  CHECK_INVARIANT(N == positions.size(), "");
  CHECK_INVARIANT(etkdgDetails.expTorsionAtoms.size() ==
                      etkdgDetails.expTorsionAngles.size(),
                  "");
  auto *field = new ForceFields::ForceField(positions[0]->dimension());
  for (unsigned int i = 0; i < N; ++i) {
    field->positions().push_back(positions[i]);
  }

  // keep track which atoms are 1,2- or 1,3-restrained
  boost::dynamic_bitset<> atomPairs(N * N);

  // torsion constraints
  for (unsigned int t = 0; t < etkdgDetails.expTorsionAtoms.size(); ++t) {
    int i = etkdgDetails.expTorsionAtoms[t][0];
    int j = etkdgDetails.expTorsionAtoms[t][1];
    int k = etkdgDetails.expTorsionAtoms[t][2];
    int l = etkdgDetails.expTorsionAtoms[t][3];
    if (i < j)
      atomPairs[i * N + j] = 1;
    else
      atomPairs[j * N + i] = 1;
    // etkdgDetails.expTorsionAngles[t][0] = (signs, V's)
    auto *contrib = new ForceFields::CrystalFF::TorsionAngleContribM6(
        field, i, j, k, l, etkdgDetails.expTorsionAngles[t].second,
        etkdgDetails.expTorsionAngles[t].first);
    field->contribs().push_back(ForceFields::ContribPtr(contrib));
  }  // torsion constraints

  double fdist = 100.0;  // force constant
  // 1,2 distance constraints
  for (const auto &bnd : etkdgDetails.bonds) {
    unsigned int i = bnd.first;
    unsigned int j = bnd.second;
    if (i < j)
      atomPairs[i * N + j] = 1;
    else
      atomPairs[j * N + i] = 1;
    double d = ((*positions[i]) - (*positions[j])).length();
    double l = d - 0.01;
    double u = d + 0.01;
    auto *contrib = new ForceFields::UFF::DistanceConstraintContrib(
        field, i, j, l, u, fdist);
    field->contribs().push_back(ForceFields::ContribPtr(contrib));
  }

  // 1,3 distance constraints
  for (unsigned int a = 1; a < etkdgDetails.angles.size(); ++a) {
    unsigned int i = etkdgDetails.angles[a][0];
    unsigned int j = etkdgDetails.angles[a][2];
    if (i < j)
      atomPairs[i * N + j] = 1;
    else
      atomPairs[j * N + i] = 1;
    double d = ((*positions[i]) - (*positions[j])).length();
    double l = d - 0.01;
    double u = d + 0.01;
    auto *contrib = new ForceFields::UFF::DistanceConstraintContrib(
        field, i, j, l, u, fdist);
    field->contribs().push_back(ForceFields::ContribPtr(contrib));
  }

  // minimum distance for all other atom pairs
  fdist = 10.0;
  for (unsigned int i = 1; i < N; ++i) {
    for (unsigned int j = 0; j < i; ++j) {
      if (!atomPairs[j * N + i]) {
        double l = mmat.getLowerBound(i, j);
        double u = mmat.getUpperBound(i, j);
        auto *contrib = new ForceFields::UFF::DistanceConstraintContrib(
            field, i, j, l, u, fdist);
        field->contribs().push_back(ForceFields::ContribPtr(contrib));
      }
    }
  }

  return field;
}  // constructPlain3DForceField

ForceFields::ForceField *construct3DImproperForceField(
    const BoundsMatrix &mmat, RDGeom::Point3DPtrVect &positions,
    const std::vector<std::vector<int>> &improperAtoms,
    const std::vector<int> &atomNums) {
  (void)atomNums;
  unsigned int N = mmat.numRows();
  CHECK_INVARIANT(N == positions.size(), "");
  auto *field = new ForceFields::ForceField(positions[0]->dimension());
  for (unsigned int i = 0; i < N; ++i) {
    field->positions().push_back(positions[i]);
  }

  // improper torsions / out-of-plane bend / inversion
  double oobForceScalingFactor = 10.0;
  for (const auto &improperAtom : improperAtoms) {
    std::vector<int> n(4);
    for (unsigned int i = 0; i < 3; ++i) {
      n[1] = 1;
      switch (i) {
        case 0:
          n[0] = 0;
          n[2] = 2;
          n[3] = 3;
          break;

        case 1:
          n[0] = 0;
          n[2] = 3;
          n[3] = 2;
          break;

        case 2:
          n[0] = 2;
          n[2] = 3;
          n[3] = 0;
          break;
      }
      auto *contrib = new ForceFields::UFF::InversionContrib(
          field, improperAtom[n[0]], improperAtom[n[1]], improperAtom[n[2]],
          improperAtom[n[3]], improperAtom[4],
          static_cast<bool>(improperAtom[5]), oobForceScalingFactor);
      field->contribs().push_back(ForceFields::ContribPtr(contrib));
    }
  }

  return field;

}  // construct3DImproperForceField
}  // namespace DistGeom


//        Copyright The Authors 2018.
//    Distributed under the 3-Clause BSD License.
//    (See accompanying file LICENSE or copy at
//   https://opensource.org/licenses/BSD-3-Clause)

#include <sstream>  // for ToString() implementation

#include <boost/multiprecision/cpp_int.hpp>

#include <p2p/kademlia/routing.h>

namespace blocxxi {
namespace p2p {
namespace kademlia {

using boost::multiprecision::cpp_int_backend;
using boost::multiprecision::number;
using boost::multiprecision::unchecked;
using boost::multiprecision::unsigned_magnitude;

using uint161_t = number<cpp_int_backend<KEYSIZE_BITS + 1, KEYSIZE_BITS + 1,
                                         unsigned_magnitude, unchecked, void>>;

RoutingTable::RoutingTable(Node node, std::size_t ksize)
    : my_node_(std::move(node)), ksize_(ksize) {
  AddInitialBucket();
}

std::size_t RoutingTable::NodesCount() const {
  std::size_t total = 0;
  for (auto &kb : buckets_) total += kb.Size().first;
  return total;
}

std::size_t RoutingTable::BucketsCount() const { return buckets_.size(); }

bool RoutingTable::Empty() const { return buckets_.front().Empty(); }

// Calculate the index of the bucket to receive the new node.
// Theoretically, node is supposed to go into the bucket covering the distance
// range [2^i, 2^i+1) which corresponds to index i which is also the same
// value returned by LogDistance().
//
// However, as we are splitting buckets as they become full instead of having
// 160 buckets, the target bucket index will be calculated as the min(i,
// num_buckets - 1), favoring to push new nodes to the bucket with the biggest
// distance from our node unless they are actually closer.
std::size_t RoutingTable::GetBucketIndexFor(const Node::IdType &id) const {
  // When we get here, the routing table should have been properly initialized
  // and thus has at least the initial bucket.
  auto num_buckets = buckets_.size();
  ASAP_ASSERT(num_buckets > 0);

  auto bucket = buckets_.begin();
  while (bucket != buckets_.end()) {
    if (bucket->CanHoldNode(id)) {
      std::size_t bucket_index =
          static_cast<std::size_t>(std::distance(buckets_.begin(), bucket));
      ASLOG(trace, "{} belongs to bucket index={}", id.ToBitStringShort(),
            bucket_index);
      return bucket_index;
    }
    ++bucket;
  }
  ASAP_ASSERT_FAIL();
  return num_buckets - 1;
}

bool RoutingTable::AddPeer(Node &&node) {
  ASLOG(trace, "ADD CONTACT [ {} ]: {} / logdist: {}", this->ToString(),
        node.Id().ToBitStringShort(), my_node_.LogDistanceTo(node));
  // Don't add our node
  if (my_node_ == node) {
    ASLOG(debug, "Unexpected attempt to add our node to the routing table.");
    return true;
  }
  auto bucket_index = GetBucketIndexFor(node.Id());
  auto bucket = buckets_.begin();
  std::advance(bucket, bucket_index);
  // bucket->DumpBucketToLog();

  // This will return true unless the bucket is full
  if (bucket->AddNode(std::move(node))) {
    ASLOG(trace, "Buckets [ {}]", this->ToString());
    return true;
  }

  // Section 4.2
  // General rule for split: if the bucket has this routing table's node in its
  // range or if the depth is not congruent to 0 mod 5

  // Can we split?
  auto can_split = false;

  // auto bucket_depth_check = bucket->Depth() < DEPTH_B;
  auto shared_prefix_test =
      (bucket->Depth() < DEPTH_B) && ((bucket->Depth() % DEPTH_B) != 0);

  // Only the last bucket has in range the routing node (my_node_)
  auto bucket_has_in_range_my_node = (bucket_index == (buckets_.size() - 1));
  can_split |= bucket_has_in_range_my_node;
  ASLOG(trace, "|| bucket has in range my node? {} --> split={}",
        bucket_has_in_range_my_node, can_split);
  can_split |= shared_prefix_test;
  ASLOG(trace, "|| shared prefix size % {} != 0? {} --> split={}", DEPTH_B,
        shared_prefix_test, can_split);
  can_split &= (buckets_.size() < 160);
  ASLOG(trace, "&& we have {} less than 160 buckets? {} --> split={}",
        buckets_.size(), buckets_.size() < 160, can_split);

  // Should we split?
  // Don't split the first bucket unles it's the first split ever
  can_split &= !(buckets_.size() > 1 && bucket_index == 0);
  ASLOG(trace, "&& not the first bucket? {} --> split={}",
        !(buckets_.size() > 1 && bucket_index == 0), can_split);

  if (can_split) {
    std::pair<KBucket, KBucket> pair = bucket->Split();
    bucket = buckets_.insert(bucket, std::move(pair.second));
    bucket = buckets_.insert(bucket, std::move(pair.first));
    buckets_.erase(bucket + 2);
    DumpToLog();
    return true;
  }

  return false;
}

void RoutingTable::RemovePeer(const Node &node) {
  auto bucket_index = GetBucketIndexFor(node.Id());
  auto bucket = buckets_.begin();
  std::advance(bucket, bucket_index);
  bucket->RemoveNode(node);
}

bool RoutingTable::PeerTimedOut(Node const &peer) {
  // Find the peer in the routing table, starting from the last bucket as it is
  // most likely to contain the peer
  for (auto bucket = buckets_.rbegin(); bucket != buckets_.rend(); ++bucket) {
    for (auto bn = bucket->begin(); bn != bucket->end(); ++bn) {
      if (bn->Id() == peer.Id()) {
        bn->IncFailuresCount();
        ASLOG(debug, "node {} failed to respond for {} times", bn->ToString(),
              bn->FailuresCount());
        if (bn->IsStale()) {
          bucket->RemoveNode(bn);
          return true;
        } else {
          return false;
        }
      }
    }
  }
  return false;
}

std::vector<Node> RoutingTable::FindNeighbors(Node::IdType const &id,
                                              std::size_t k) const {
  ASLOG(trace, "try to find up to {} neighbors for {}", k,
        id.ToBitStringShort());
  auto cmp = [&id](Node const &a, Node const &b) {
    return a.DistanceTo(id) < b.DistanceTo(id);
  };
  auto neighbors = std::set<Node, decltype(cmp)>(cmp);
  auto count = 0U;

  auto bucket_index = GetBucketIndexFor(id);
  auto bucket = buckets_.begin();
  std::advance(bucket, bucket_index);
  auto left = bucket;
  auto right = (bucket != buckets_.end()) ? bucket + 1 : bucket;

  auto current_bucket = bucket;

  bool use_left = true;
  bool has_more = true;
  while (has_more) {
    has_more = false;
    for (auto const &neighbor : *current_bucket) {
      // Exclude the node
      if (neighbor.Id() != id) {
        ++count;
        ASLOG(debug, "found neighbor(count={}) {}", count,
              neighbor.Id().ToBitStringShort());
        neighbors.insert(neighbor);
        if (count == k) goto Done;
      } else {
        ASLOG(debug, "skip caller node from neighbors list");
      }
    }

    // If one bucket does not have enough, fill by alternating left and right
    // buckets until we collect the requested number of neighbors
    if (right == buckets_.end()) use_left = true;
    if (left != buckets_.begin()) {
      has_more = true;
      if (use_left) {
        --left;
        current_bucket = left;
        use_left = false;
        continue;
      }
    }
    if (right != buckets_.end()) {
      has_more = true;
      current_bucket = right;
      ++right;
    }
    use_left = true;
  }
Done:
  ASLOG(debug, "found {} neighbors out of {} non-replacement nodes I know",
        neighbors.size(), this->NodesCount());
  return std::vector<Node>(neighbors.begin(), neighbors.end());
}

std::string RoutingTable::ToString() const {
  auto os = std::stringstream();
  for (auto &b : buckets_) {
    os << b.Size().first << " ";
  }
  return os.str();
}

void RoutingTable::DumpToLog() const {
  ASLOG(trace, "START----------------------------------------------------");
  for (auto &b : buckets_) {
    b.DumpBucketToLog();
  }
  ASLOG(trace, "END------------------------------------------------------");
}

std::ostream &operator<<(std::ostream &out, RoutingTable const &rt) {
  out << "[ ";
  for (auto &kb : rt) {
    out << kb << " ";
  }
  out << "]";
  return out;
}

}  // namespace kademlia
}  // namespace p2p
}  // namespace blocxxi


/*ckwg +29
 * Copyright 2013-2014 by Kitware, Inc.
 * 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 name of Kitware, Inc. nor the names of any 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 AUTHORS 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.
 */

/**
 * \file
 * \brief core match_features_homography algorithm implementation
 */

#include <maptk/core/algo/match_features_homography.h>
#include <maptk/core/exceptions/algorithm.h>
#include <maptk/core/match_set.h>

#include <boost/algorithm/string/join.hpp>
#include <boost/foreach.hpp>

#include <iostream>

namespace maptk
{

namespace algo
{

/// Private implementation class
class match_features_homography::priv
{
public:
  /// Constructor
  priv()
  : inlier_scale(10.0),
    min_required_inlier_count(0),
    min_required_inlier_percent(0.0)
  {
  }

  priv(const priv& other)
  : inlier_scale(other.inlier_scale),
    min_required_inlier_count(other.min_required_inlier_count),
    min_required_inlier_percent(other.min_required_inlier_percent)
  {
  }

  // the scale of inlier points
  double inlier_scale;

  // min inlier count required to make any matches
  int min_required_inlier_count;

  // min inlier percent required to make any matches
  double min_required_inlier_percent;
};

/// Constructor
match_features_homography
::match_features_homography()
: d_(new priv)
{
}

/// Copy Constructor
match_features_homography
::match_features_homography(const match_features_homography& other)
: d_(new priv(*other.d_))
{
}

/// Destructor
match_features_homography
::~match_features_homography()
{
}

/// Get this alg's \link maptk::config_block configuration block \endlink
config_block_sptr
match_features_homography
::get_configuration() const
{
  config_block_sptr config = algorithm::get_configuration();
  config->set_value("inlier_scale", d_->inlier_scale,
                    "The acceptable error distance (in pixels) between warped "
                    "and measured points to be considered an inlier match.");
  config->set_value("min_required_inlier_count", d_->min_required_inlier_count,
                    "The minimum required inlier point count. If there are less "
                    "than this many inliers, no matches will be output.");
  config->set_value("min_required_inlier_percent", d_->min_required_inlier_percent,
                    "The minimum required percentage of inlier points. If the "
                    "percentage of points considered inliers is less than this "
                    "amount, no matches will be output.");

  // nested algorithm configurations
  estimate_homography::get_nested_algo_configuration("homography_estimator",
                                                     config, h_estimator_);
  match_features::get_nested_algo_configuration("feature_matcher", config,
                                                matcher_);

  return config;
}

void
match_features_homography
::set_configuration(config_block_sptr in_config)
{
  // Starting with our generated config_block to ensure that assumed values are present
  // An alternative is to check for key presence before performing a get_value() call.
  config_block_sptr config = this->get_configuration();
  config->merge_config(in_config);

  // Set nested algorithm configurations
  estimate_homography::set_nested_algo_configuration("homography_estimator",
                                                     config, h_estimator_);
  match_features::set_nested_algo_configuration("feature_matcher", config,
                                                matcher_);

  // Other parameters
  d_->inlier_scale = config->get_value<double>("inlier_scale");
  d_->min_required_inlier_count = config->get_value<int>("min_required_inlier_count");
  d_->min_required_inlier_percent = config->get_value<double>("min_required_inlier_percent");
}

bool
match_features_homography
::check_configuration(config_block_sptr config) const
{
  return (
    estimate_homography::check_nested_algo_configuration("homography_estimator", config)
    &&
    match_features::check_nested_algo_configuration("feature_matcher", config)
  );
}

/// Match one set of features and corresponding descriptors to another
match_set_sptr
match_features_homography
::match(feature_set_sptr feat1, descriptor_set_sptr desc1,
        feature_set_sptr feat2, descriptor_set_sptr desc2) const
{
  if( !matcher_ || !h_estimator_ )
  {
    return match_set_sptr();
  }

  // compute the initial matches
  match_set_sptr init_matches = matcher_->match(feat1, desc1, feat2, desc2);

  // estimate a homography from the initial matches
  std::vector<bool> inliers;
  matrix_3x3d H = h_estimator_->estimate(feat1, feat2, init_matches,
                                         inliers, d_->inlier_scale);
  (void) H; // H not yet used, avoid compiler warning
  int inlier_count = static_cast<int>(std::count(inliers.begin(), inliers.end(), true));
  std::cout << "inlier ratio: " << inlier_count << "/" << inliers.size() << std::endl;

  // verify matching criteria are met
  if( !inlier_count || inlier_count < d_->min_required_inlier_count ||
      static_cast<double>(inlier_count)/inliers.size() < d_->min_required_inlier_percent )
  {
    return match_set_sptr(new simple_match_set());
  }

  // return correct matches (inliers)
  std::vector<maptk::match> m = init_matches->matches();
  std::vector<maptk::match> inlier_m;
  for( unsigned int i=0; i<inliers.size(); ++i )
  {
    if( inliers[i] )
    {
      inlier_m.push_back(m[i]);
    }
  }

  return match_set_sptr(new simple_match_set(inlier_m));
}

} // end namespace algo

} // end namespace maptk


/*
 * Software License Agreement (BSD License)
 *
 *  Point Cloud Library (PCL) - www.pointclouds.org
 *  Copyright (c) 2010-2012, Willow Garage, Inc.
 *
 *  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 copyright holder(s) 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 OWNER 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.
 *
 * $Id: random.hpp 6715 2012-08-06 18:15:54Z rusu $
 *
 */

#ifndef PCL_COMMON_RANDOM_HPP_
#define PCL_COMMON_RANDOM_HPP_

#include <boost/version.hpp>

/////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename T>
pcl::common::UniformGenerator<T>::UniformGenerator(T min, T max, uint32_t seed)
  : distribution_ (min, max)
  , generator_ (rng_, distribution_)
{
  parameters_ = Parameters (min, max, seed);
  if(parameters_.seed != -1)
    rng_.seed (seed);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename T>
pcl::common::UniformGenerator<T>::UniformGenerator(const Parameters& parameters)
  : parameters_ (parameters)
  , distribution_ (parameters_.min, parameters_.max)
  , generator_ (rng_, distribution_)
{
  if(parameters_.seed != -1)
    rng_.seed (parameters_.seed);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename T> void
pcl::common::UniformGenerator<T>::setSeed (uint32_t seed)
{
  if (seed != -1)
  {
    parameters_.seed = seed;
    rng_.seed(parameters_.seed);
  }
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename T> void
pcl::common::UniformGenerator<T>::setParameters (T min, T max, uint32_t seed)
{
  parameters_.min = min;
  parameters_.max = max;
  parameters_.seed = seed;
#if BOOST_VERSION >= 104700
  typename DistributionType::param_type params (parameters_.min, parameters_.max);
  distribution_.param (params);
#else
  distribution_ = DistributionType (parameters_.min, parameters_.max);
#endif
  distribution_.reset ();
  generator_.distribution () = distribution_;
  if (seed != -1)
  {
    parameters_.seed = seed;
    rng_.seed (parameters_.seed);
  }
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename T> void
pcl::common::UniformGenerator<T>::setParameters (const Parameters& parameters)
{
  parameters_ = parameters;
#if BOOST_VERSION >= 104700
  typename DistributionType::param_type params (parameters_.min, parameters_.max);
  distribution_.param (params);
#else
  distribution_ = DistributionType (parameters_.min, parameters_.max);
#endif
  distribution_.reset ();
  generator_.distribution () = distribution_;
  if (parameters_.seed != -1)
    rng_.seed (parameters_.seed);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename T>
pcl::common::NormalGenerator<T>::NormalGenerator(T mean, T sigma, uint32_t seed)
  : distribution_ (mean, sigma)
  , generator_ (rng_, distribution_)
{
  parameters_ = Parameters (mean, sigma, seed);
  if(parameters_.seed != -1)
    rng_.seed (seed);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename T>
pcl::common::NormalGenerator<T>::NormalGenerator(const Parameters& parameters)
  : parameters_ (parameters)
  , distribution_ (parameters_.mean, parameters_.sigma)
  , generator_ (rng_, distribution_)
{
  if(parameters_.seed != -1)
    rng_.seed (parameters_.seed);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename T> void
pcl::common::NormalGenerator<T>::setSeed (uint32_t seed)
{
  if (seed != -1)
  {
    parameters_.seed = seed;
    rng_.seed(seed);
  }
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename T> void
pcl::common::NormalGenerator<T>::setParameters (T mean, T sigma, uint32_t seed)
{
  parameters_.mean = mean;
  parameters_.sigma = sigma;
  parameters_.seed = seed;
#if BOOST_VERSION >= 104700
  typename DistributionType::param_type params (parameters_.mean, parameters_.sigma);
  distribution_.param (params);
#else
  distribution_ = DistributionType (parameters_.mean, parameters_.sigma);
#endif
  distribution_.reset ();
  generator_.distribution () = distribution_;
  if (seed != -1)
    rng_.seed (parameters_.seed);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename T> void
pcl::common::NormalGenerator<T>::setParameters (const Parameters& parameters)
{
  parameters_ = parameters;
#if BOOST_VERSION >= 104700
  typename DistributionType::param_type params (parameters_.mean, parameters_.sigma);
  distribution_.param (params);
#else
  distribution_ = DistributionType (parameters_.mean, parameters_.sigma);
#endif
  distribution_.reset ();
  generator_.distribution () = distribution_;
  if (parameters_.seed != -1)
    rng_.seed (parameters_.seed);
}

#endif


#include "AinurState.h"
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include "AinurDoubleOrFloat.h"

namespace PsimagLite {

struct MyProxyFor {

	static void convert(long unsigned int& t, std::string str)
	{
		t = PsimagLite::atoi(str);
	}

	static void convert(unsigned int& t, std::string str)
	{
		t = PsimagLite::atoi(str);
	}

	static void convert(long int& t, std::string str)
	{
		t = PsimagLite::atoi(str);
	}

	static void convert(int& t, std::string str)
	{
		t = PsimagLite::atoi(str);
	}

	static void convert(double& t, std::string str)
	{
		t = PsimagLite::atof(str);
	}

	static void convert(float& t, std::string str)
	{
		t = PsimagLite::atof(str);
	}

	template<typename T>
	static void convert(std::complex<T>& t, std::string str)
	{
		t = toComplex<T>(str);
	}

	static void convert(String& t, std::string str)
	{
		t = str;
	}

	template<typename T>
	static void convert(T& t, std::string str)
	{
		String msg("Unknown type ");
		throw RuntimeError("convert(): " + msg + typeid(t).name() + " for " + str + "\n");
	}

private:

	template<typename RealType>
	static std::complex<RealType> toComplex(std::string str)
	{
		typedef std::complex<RealType> ComplexType;
		String buffer;
		bool flag = false;
		const SizeType n = str.length();
		RealType real1 = 0;
		for (SizeType i = 0; i < n; ++i) {
			bool isSqrtMinus1 = (str[i] == 'i');
			if (isSqrtMinus1 && flag)
				throw RuntimeError("Error parsing number " + str + "\n");

			if (isSqrtMinus1) {
				flag = true;
				real1 = atof(buffer.c_str());
				buffer = "";
				continue;
			}

			buffer += str[i];
		}

		return (flag) ? ComplexType(real1, atof(buffer.c_str())) :
		                ComplexType(atof(buffer.c_str()), 0);
	}

};

//---------

boost::spirit::qi::rule<std::string::iterator,
std::vector<std::string>(),
boost::spirit::qi::space_type>
ruleRows()
{
	boost::spirit::qi::rule<std::string::iterator,
	        std::vector<std::string>(),
	        boost::spirit::qi::space_type> myrule =  "[" >> (+~boost::spirit::qi::char_(",[]"))
	                                                        % ',' >> "]";
	return myrule;
}

//---------

void AinurState::assign(String k, String v)
{
	int x = storageIndexByName(k);
	if (x < 0)
		err(errLabel(ERR_PARSE_UNDECLARED, k));

	assert(static_cast<SizeType>(x) < values_.size());

	//if (values_[x] != "")
	//	std::cerr<<"Overwriting label "<<k<<" with "<<v<<"\n";

	values_[x] = v;
}

//---------

template <typename T>
template <typename A, typename ContextType>
void AinurState::ActionMatrix<T>::operator()(A& attr,
                                             ContextType&,
                                             bool&) const
{
	SizeType rows = attr.size();
	if (rows == 0) return;
	SizeType cols = attr[0].size();
	t_.resize(rows, cols);
	for (SizeType i = 0; i < rows; ++i) {
		if (attr[i].size() != cols)
			err("Ainur: Problem reading matrix\n");
		for (SizeType j = 0; j < cols; ++j)
			MyProxyFor::convert(t_(i, j), attr[i][j]);
	}
}

//---------

template <typename T>
template <typename A, typename ContextType>
void AinurState::Action<T>::operator()(A& attr,
                                       ContextType&,
                                       bool&) const
{
	const SizeType n = attr.size();
	if (n == 2 && attr[1] == "...") {
		const SizeType m = t_.size();
		if (m == 0)
			err("Cannot use ellipsis for vector of unknown size\n");
		MyProxyFor::convert(t_[0], attr[0]);
		for (SizeType i = 1; i < m; ++i)
			t_[i] = t_[0];
		return;
	}

	if (n == 2 && attr[1].length() > 4 && attr[1].substr(0, 4) == "...x") {
		const SizeType m = t_.size();
		const SizeType l = attr[1].length();
		const SizeType mm = PsimagLite::atoi(attr[1].substr(4, l - 4));
		if (m != 0)
			std::cout<<"Resizing vector to "<<mm<<"\n";
		t_.resize(mm);
		MyProxyFor::convert(t_[0], attr[0]);
		for (SizeType i = 1; i < mm; ++i)
			t_[i] = t_[0];
		return;
	}

	t_.resize(n);
	for (SizeType i = 0; i < n; ++i)
		MyProxyFor::convert(t_[i], attr[i]);
}

//---------

template<typename T>
void AinurState::convertInternal(Matrix<T>& t,
                                 String value) const
{
	namespace qi = boost::spirit::qi;
	typedef std::string::iterator IteratorType;
	typedef std::vector<std::string> VectorStringType;
	typedef std::vector<VectorStringType> VectorVectorVectorType;

	IteratorType it = value.begin();
	qi::rule<IteratorType, VectorStringType(), qi::space_type> ruRows = ruleRows();

	qi::rule<IteratorType, VectorVectorVectorType(), qi::space_type> full =
	        "[" >> -(ruRows  % ",") >> "]";

	ActionMatrix<T> actionMatrix("matrix", t);
	bool r = qi::phrase_parse(it,
	                          value.end(),
	                          full [actionMatrix],
	                          qi::space);

	//check if we have a match
	if (!r) {
		err("matrix parsing failed near " +
		    stringContext(it, value.begin(), value.end())
		    + "\n");
	}

	if (it != value.end())
		std::cerr << "matrix parsing: unmatched part exists\n";
}

template<typename T>
void AinurState::convertInternal(std::vector<T>& t,
                                 String value,
                                 typename EnableIf<Loki::TypeTraits<T>::isArith ||
                                 IsComplexNumber<T>::True ||
                                 TypesEqual<T, String>::True,
                                 int>::Type) const
{
	namespace qi = boost::spirit::qi;
	typedef std::string::iterator IteratorType;
	typedef std::vector<std::string> VectorStringType;

	IteratorType it = value.begin();
	qi::rule<IteratorType, VectorStringType(), qi::space_type> ruRows = ruleRows();

	Action<T> actionRows("rows", t);

	bool r = qi::phrase_parse(it,
	                          value.end(),
	                          ruRows [actionRows],
	                          qi::space);

	//check if we have a match
	if (!r)
		err("vector parsing failed near " + stringContext(it, value.begin(), value.end()) + "\n");

	if (it != value.end()) {
		std::cerr << "vector parsing: unmatched part exists near ";
		std::cerr << stringContext(it, value.begin(), value.end())<<"\n";
	}
}

//---------

template void AinurState::convertInternal(Matrix<DoubleOrFloatType>&,String) const;

template void AinurState::convertInternal(Matrix<std::complex<DoubleOrFloatType> >&,
String) const;

template void AinurState::convertInternal(Matrix<String>&, String) const;

template void AinurState::convertInternal(std::vector<DoubleOrFloatType>&, String, int) const;

template void AinurState::convertInternal(std::vector<std::complex<DoubleOrFloatType> >&,
String,
int) const;

template void AinurState::convertInternal(std::vector<SizeType>&, String, int) const;

template void AinurState::convertInternal(std::vector<int>&, String, int) const;

template void AinurState::convertInternal(std::vector<String>&, String, int) const;
} // namespace PsimagLite


/*//////////////////////////////////////////////////////////////////////////////
    Copyright (c) 2014 Jamboree

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

#if defined(_MSC_VER)
#pragma once
#endif

#include <cstddef>
#include <boost/type_traits/integral_constant.hpp>

// This is a standard (c++1y) compatible integer_sequence implementation,
// it's needed for now, and it could be replaced with std::integer_sequence
// once the new standard is available everywhere.

namespace boost { namespace spirit { namespace x3
{
    template <typename T, T... Ns>
    struct integer_sequence
    {
        typedef T value_type;

        static constexpr std::size_t size() noexcept
        {
            return sizeof...(Ns);
        }
    };
}}}

namespace boost { namespace spirit { namespace x3 { namespace detail
{
    template <typename T, typename S1, typename S2, T N>
    struct accum_integer_sequence;

    template <typename T, T... N1, T... N2, T N>
    struct accum_integer_sequence<T, integer_sequence<T, N1...>, integer_sequence<T, N2...>, N>
    {
        typedef integer_sequence<T, N1..., (N + N2)...> type;
    };

    template <typename N>
    struct make_integer_sequence_impl
    {
        typedef typename N::value_type T;
        static T const n = N::value;
        static T const m = n / 2;
        typedef typename
            make_integer_sequence_impl<integral_constant<T, m>>::type
        part1;
        typedef typename
            make_integer_sequence_impl<integral_constant<T, n - m>>::type
        part2;
        typedef typename
            accum_integer_sequence<T, part1, part2, m>::type
        type;
    };

    template <typename T>
    struct make_integer_sequence_impl<integral_constant<T, 0>>
    {
        typedef integer_sequence<T> type;
    };

    template <typename T>
    struct make_integer_sequence_impl<integral_constant<T, 1>>
    {
        typedef integer_sequence<T, 0> type;
    };
}}}}

namespace boost { namespace spirit { namespace x3
{
    template <std::size_t... Ns>
    using index_sequence = integer_sequence<std::size_t, Ns...>;

    template <typename T, T N>
    using make_integer_sequence = typename detail::make_integer_sequence_impl<
        integral_constant<T, N>>::type;

    template <std::size_t N>
    using make_index_sequence = make_integer_sequence<std::size_t, N>;

    template <typename... T>
    using index_sequence_for = make_index_sequence<sizeof...(T)>;
}}}

#endif


#include <boost/ut.hpp>

#include <dot.hpp>
#include <pncounter.hpp>

auto main() -> int
{
    using namespace boost::ut;
    using namespace crdt;

    "basic by one"_test = [] {
        pncounter<std::string> a;
        expect(a.read() == 0);

        a.apply(a.inc("A"));
        expect(a.read() == 1);

        a.apply(a.inc("A"));
        expect(a.read() == 2);

        a.apply(a.dec("A"));
        expect(a.read() == 1);

        a.apply(a.inc("A"));
        expect(a.read() == 2);
    };

    "basic by many"_test = [] {
        pncounter<std::string> a;
        expect(a.read() == 0);

        std::uint32_t steps = 3;

        a.apply(a.inc("A", steps));
        expect(a.read() == steps);

        a.apply(a.inc("A", steps));
        expect(a.read() == (2 * steps));

        a.apply(a.dec("A", steps));
        expect(a.read() == steps);

        a.apply(a.inc("A", 1));
        expect(a.read() == (steps + 1));
    };
}


// ########################## OPEN3D ORIGINAL WORK ############################
// ----------------------------------------------------------------------------
// -                        Open3D: www.open3d.org                            -
// ----------------------------------------------------------------------------
// The MIT License (MIT)
//
// Copyright (c) 2018 www.open3d.org
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
// ----------------------------------------------------------------------------
//
//
//
// ########################## PROCARE MODIFIED WORK ############################
// Mofifications: block of codes specified by /*kimeguida*/
// -----------------------------------------------------------------------------
// <                                  ProCare                                  >
// -----------------------------------------------------------------------------
// The MIT License (MIT)
//
// Copyright (c) 2020 Merveille Eguida
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

#include "Feature.h"

#include <Eigen/Dense>
#include <Core/Utility/Console.h>
#include <Core/Geometry/PointCloud.h>
#include <Core/Geometry/KDTreeFlann.h>

namespace open3d {

namespace {

Eigen::Vector4d ComputePairFeatures(const Eigen::Vector3d &p1,
                                    const Eigen::Vector3d &n1,
                                    const Eigen::Vector3d &p2,
                                    const Eigen::Vector3d &n2) {
    Eigen::Vector4d result;
    Eigen::Vector3d dp2p1 = p2 - p1;
    result(3) = dp2p1.norm();
    if (result(3) == 0.0) {
        return Eigen::Vector4d::Zero();
    }
    auto n1_copy = n1;
    auto n2_copy = n2;
    double angle1 = n1_copy.dot(dp2p1) / result(3);
    double angle2 = n2_copy.dot(dp2p1) / result(3);
    if (acos(fabs(angle1)) > acos(fabs(angle2))) {
        n1_copy = n2;
        n2_copy = n1;
        dp2p1 *= -1.0;
        result(2) = -angle2;
    } else {
        result(2) = angle1;
    }
    auto v = dp2p1.cross(n1_copy);
    double v_norm = v.norm();
    if (v_norm == 0.0) {
        return Eigen::Vector4d::Zero();
    }
    v /= v_norm;
    auto w = n1_copy.cross(v);
    result(1) = v.dot(n2_copy);
    result(0) = atan2(w.dot(n2_copy), n1_copy.dot(n2_copy));
    return result;
}

std::shared_ptr<Feature> ComputeSPFHFeature(
        const PointCloud &input,
        const KDTreeFlann &kdtree,
        const KDTreeSearchParam &search_param) {
    auto feature = std::make_shared<Feature>();
    feature->Resize(33, (int)input.points_.size());
#ifdef _OPENMP
#pragma omp parallel for schedule(static)
#endif
    for (int i = 0; i < (int)input.points_.size(); i++) {
        const auto &point = input.points_[i];
        const auto &normal = input.normals_[i];
        std::vector<int> indices;
        std::vector<double> distance2;
        if (kdtree.Search(point, search_param, indices, distance2) > 1) {
            // only compute SPFH feature when a point has neighbors
            double hist_incr = 100.0 / (double)(indices.size() - 1);
            for (size_t k = 1; k < indices.size(); k++) {
                // skip the point itself, compute histogram
                auto pf = ComputePairFeatures(point, normal,
                                              input.points_[indices[k]],
                                              input.normals_[indices[k]]);
                int h_index = (int)(floor(11 * (pf(0) + M_PI) / (2.0 * M_PI)));
                if (h_index < 0) h_index = 0;
                if (h_index >= 11) h_index = 10;
                feature->data_(h_index, i) += hist_incr;
                h_index = (int)(floor(11 * (pf(1) + 1.0) * 0.5));
                if (h_index < 0) h_index = 0;
                if (h_index >= 11) h_index = 10;
                feature->data_(h_index + 11, i) += hist_incr;
                h_index = (int)(floor(11 * (pf(2) + 1.0) * 0.5));
                if (h_index < 0) h_index = 0;
                if (h_index >= 11) h_index = 10;
                feature->data_(h_index + 22, i) += hist_incr;
            }
        }
    }
    return feature;
}

/*kimeguida*/
std::shared_ptr<Feature> ComputeCSPFHFeature(
        const PointCloud &input,
        const KDTreeFlann &kdtree,
        const KDTreeSearchParam &search_param) {
    auto feature = std::make_shared<Feature>();
    feature->Resize(41, (int)input.points_.size());
#ifdef _OPENMP
#pragma omp parallel for schedule(static)
#endif
    for (int i = 0; i < (int)input.points_.size(); i++) {
        const auto &point = input.points_[i];
        const auto &normal = input.normals_[i];
        std::vector<int> indices;
        std::vector<double> distance2;
        if (kdtree.Search(point, search_param, indices, distance2) > 1) {
            // only compute CSPFH feature when a point has neighbors
            double hist_incr = 100.0 / (double)(indices.size() - 1);
            for (size_t k = 1; k < indices.size(); k++) {
                // skip the point itself, compute histogram
                auto pf = ComputePairFeatures(point, normal,
                                              input.points_[indices[k]],
                                              input.normals_[indices[k]]);
                int h_index = (int)(floor(11 * (pf(0) + M_PI) / (2.0 * M_PI)));
                if (h_index < 0) h_index = 0;
                if (h_index >= 11) h_index = 10;
                feature->data_(h_index, i) += hist_incr;
                h_index = (int)(floor(11 * (pf(1) + 1.0) * 0.5));
                if (h_index < 0) h_index = 0;
                if (h_index >= 11) h_index = 10;
                feature->data_(h_index + 11, i) += hist_incr;
                h_index = (int)(floor(11 * (pf(2) + 1.0) * 0.5));
                if (h_index < 0) h_index = 0;
                if (h_index >= 11) h_index = 10;
                feature->data_(h_index + 22, i) += hist_incr;
            }

            double c_hist_incr = 100.0 / (double)(indices.size());
            const char *CA_c = "16741671";
            const char *CZ_c = "4646984";
            const char *O_c = "15219528";
            const char *OD1_c = "0";
            const char *OG_c = "8204959";
            const char *N_c = "30894";
            const char *NZ_c = "15231913";
            const char *DU_c = "7566712";

            Eigen::Vector3d CA_rgb = ASCIIPCDColorToRGB(CA_c, 'F', 4);
            Eigen::Vector3d CZ_rgb = ASCIIPCDColorToRGB(CZ_c, 'F', 4);
            Eigen::Vector3d O_rgb = ASCIIPCDColorToRGB(O_c, 'F', 4);
            Eigen::Vector3d OD1_rgb = ASCIIPCDColorToRGB(OD1_c, 'F', 4);
            Eigen::Vector3d OG_rgb = ASCIIPCDColorToRGB(OG_c, 'F', 4);
            Eigen::Vector3d N_rgb = ASCIIPCDColorToRGB(N_c, 'F', 4);
            Eigen::Vector3d NZ_rgb = ASCIIPCDColorToRGB(NZ_c, 'F', 4);
            Eigen::Vector3d DU_rgb = ASCIIPCDColorToRGB(DU_c, 'F', 4);

            for (size_t k = 0; k < indices.size(); k++) {
                // include the point itself, compute histogram
                if (input.colors_[indices[k]] == CA_rgb) {
                    int c_h_index = 33;
                    feature->data_(c_h_index, i) += c_hist_incr;
                }
                if (input.colors_[indices[k]] == CZ_rgb) {
                    int c_h_index = 34;
                    feature->data_(c_h_index, i) += c_hist_incr;
                }
                if (input.colors_[indices[k]] == O_rgb) {
                    int c_h_index = 35;
                    feature->data_(c_h_index, i) += c_hist_incr;
                }
                if (input.colors_[indices[k]] == OD1_rgb) {
                    int c_h_index = 36;
                    feature->data_(c_h_index, i) += c_hist_incr;
                }
                if (input.colors_[indices[k]] == OG_rgb) {
                    int c_h_index = 37;
                    feature->data_(c_h_index, i) += c_hist_incr;
                }
                if (input.colors_[indices[k]] == N_rgb) {
                    int c_h_index = 38;
                    feature->data_(c_h_index, i) += c_hist_incr;
                }
                if (input.colors_[indices[k]] == NZ_rgb) {
                    int c_h_index = 39;
                    feature->data_(c_h_index, i) += c_hist_incr;
                }
                if (input.colors_[indices[k]] == DU_rgb) {
                    int c_h_index = 40;
                    feature->data_(c_h_index, i) += c_hist_incr;
                }
            }
        }
    }
    return feature;
}
/*kimeguida*/

}  // unnamed namespace

std::shared_ptr<Feature> ComputeFPFHFeature(
        const PointCloud &input,
        const KDTreeSearchParam &search_param /* = KDTreeSearchParamKNN()*/) {
    auto feature = std::make_shared<Feature>();
    feature->Resize(33, (int)input.points_.size());
    if (input.HasNormals() == false) {
        PrintDebug(
                "[ComputeFPFHFeature] Failed because input point cloud has no "
                "normal.\n");
        return feature;
    }
    KDTreeFlann kdtree(input);
    auto spfh = ComputeSPFHFeature(input, kdtree, search_param);
#ifdef _OPENMP
#pragma omp parallel for schedule(static)
#endif
    for (int i = 0; i < (int)input.points_.size(); i++) {
        const auto &point = input.points_[i];
        std::vector<int> indices;
        std::vector<double> distance2;
        if (kdtree.Search(point, search_param, indices, distance2) > 1) {
            double sum[3] = {0.0, 0.0, 0.0};
            for (size_t k = 1; k < indices.size(); k++) {
                // skip the point itself
                double dist = distance2[k];
                if (dist == 0.0) continue;
                for (int j = 0; j < 33; j++) {
                    double val = spfh->data_(j, indices[k]) / dist;
                    sum[j / 11] += val;
                    feature->data_(j, i) += val;
                }
            }
            for (int j = 0; j < 3; j++)
                if (sum[j] != 0.0) sum[j] = 100.0 / sum[j];
            for (int j = 0; j < 33; j++) {
                feature->data_(j, i) *= sum[j / 11];
                // The commented line is the fpfh function in the paper.
                // But according to PCL implementation, it is skipped.
                // Our initial test shows that the full fpfh function in the
                // paper seems to be better than PCL implementation. Further
                // test required.
                feature->data_(j, i) += spfh->data_(j, i);
            }
        }
    }
    return feature;
}

/*kimeguida*/
Eigen::Vector3d ASCIIPCDColorToRGB(const char *color_ptr,
                                   const char type,
                                   const int size) {
    if ((size == 4) && (type == 'F')) {
        std::uint8_t c_data[4] = {0, 0, 0, 0};
        char *end;
        std::float_t c_value = std::strtof(color_ptr, &end);
        memcpy(c_data, &c_value, 4);
        return Eigen::Vector3d((double)c_data[2] / 255.0,
                               (double)c_data[1] / 255.0,
                               (double)c_data[0] / 255.0);
    } else {
        return Eigen::Vector3d::Zero();
    }
}

std::shared_ptr<Feature> ComputeCFPFHFeature(
        const PointCloud &input,
        const KDTreeSearchParam
                &search_param /* = geometry::KDTreeSearchParamKNN()*/) {
    auto feature = std::make_shared<Feature>();
    feature->Resize(41, (int)input.points_.size());
    if (input.HasNormals() == false) {
        PrintDebug(
                "[ComputeCFPFHFeature] Failed because input point cloud has no "
                "normal.\n");
        return feature;
    }
    if (input.HasColors() == false) {
        PrintDebug(
                "[ComputeCFPFHFeature] Failed because input point cloud has no "
                "color.\n");
        return feature;
    }
    KDTreeFlann kdtree(input);
    auto cspfh = ComputeCSPFHFeature(input, kdtree, search_param);
#ifdef _OPENMP
#pragma omp parallel for schedule(static)
#endif
    for (int i = 0; i < (int)input.points_.size(); i++) {
        const auto &point = input.points_[i];
        std::vector<int> indices;
        std::vector<double> distance2;
        if (kdtree.Search(point, search_param, indices, distance2) > 1) {
            double sum[3] = {0.0, 0.0, 0.0};
            double c_sum = 0;
            for (size_t k = 1; k < indices.size(); k++) {
                // skip the point itself
                double dist = distance2[k];
                if (dist == 0.0) continue;
                for (int j = 0; j < 33; j++) {
                    double val = cspfh->data_(j, indices[k]) / dist;
                    sum[j / 11] += val;
                    feature->data_(j, i) += val;
                }
                for (int j = 33; j < 41; j++) {
                    double val = cspfh->data_(j, indices[k]) / dist;
                    c_sum += val;
                    feature->data_(j, i) += val;
                }
            }
            for (int j = 0; j < 3; j++)
                if (sum[j] != 0.0) sum[j] = 100.0 / sum[j];
            for (int j = 0; j < 33; j++) {
                feature->data_(j, i) *= sum[j / 11];
                // The commented line is the fpfh function in the paper.
                // But according to PCL implementation, it is skipped.
                // Our initial test shows that the full fpfh function in the
                // paper seems to be better than PCL implementation. Further
                // test required.
                feature->data_(j, i) += cspfh->data_(j, i);
            }

            if (c_sum != 0.0) c_sum = 100.0 / c_sum;

            for (int j = 33; j < 41; j++) {
                feature->data_(j, i) *= c_sum;
                feature->data_(j, i) += cspfh->data_(j, i);
            }
        }
    }
    return feature;
}
/*kimeguida*/
}  // namespace open3d


// Copyright András Vukics 2006–2020. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE.txt)
#include "BlitzArray.h"

#include "Random.h"

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

using namespace std;

typedef blitz::Array<dcomp,6> Array;

BOOST_AUTO_TEST_CASE( BlitzArrayTest )
{
  Array array(blitz::shape(2,4,3,2,5,4));

  randomutils::fill<std::uniform_real_distribution<double>,std::mt19937>(array,1001);

  array.transposeSelf(1,3,0,4,2,5);

  Array arrayv(array.copy()), arrayvv(array.shape()); arrayvv=array;

  cout<<array.ordering()<<arrayv.ordering()<<arrayvv.ordering()<<endl;

  dcomp* data=new dcomp[array.size()];

  memcpy(data,array.dataFirst(),array.size());

  BOOST_CHECK( all(arrayvv==array) );

}


#include "material.h"

#include <Eigen/Geometry>
#include <iostream>
#include <math.h>

void Material::loadTextureFromFile(const std::string& fileName)
{
    if (fileName.size()==0)
        std::cerr << "Material error : no texture file name provided" << std::endl;
    else
        m_texture = new Bitmap(fileName);
}

Diffuse::Diffuse(const PropertyList &propList)
{
    m_diffuseColor = propList.getColor("diffuse",Color3f(0.2));

    std::string texturePath = propList.getString("texture","");
    if(texturePath.size()>0){
        filesystem::path filepath = getFileResolver()->resolve(texturePath);
        loadTextureFromFile(filepath.str());
        setTextureScale(propList.getFloat("scale",1));
        setTextureMode(TextureMode(propList.getInteger("mode",0)));
    }
}

Color3f Diffuse::diffuseColor(const Vector2f& uv) const
{
    //if (texture() == nullptr)
    return m_diffuseColor;

}

REGISTER_CLASS(Diffuse, "diffuse")


#pragma once

// deal.II includes
#include <deal.II/base/exceptions.h>
#include <deal.II/base/parameter_handler.h>
#include <deal.II/dofs/dof_renumbering.h>
#include <deal.II/grid/grid_generator.h>
#include <deal.II/grid/grid_in.h>
#include <deal.II/grid/grid_tools.h>
#include <deal.II/grid/tria_boundary_lib.h>
#include <deal.II/lac/sparsity_pattern.h>
#include <deal.II/numerics/data_out.h>
#include <deal.II/numerics/vector_tools.h>
#include <yaml-cpp/yaml.h>
#include <cctype>
// own includes
#include "grid_handler_base.hpp"

namespace boltzmann {
// ----------------------------------------------------------------------

/**
 * @brief takes into account *all* ajdacent vertices
 *        when constructing the domain decomposition with METIS
 *        for *periodic* boundary conditions.
 *
 */
template <int dim>
class GridHandlerPeriodic : public GridHandlerBase<dim>
{
 private:
  typedef GridHandlerBase<dim> base_type;

 public:
  GridHandlerPeriodic();
  void init_square(int nref, int nprocs = 1);
  void init(dealii::ParameterHandler& param_handler, int nprocs = 1);
  void init(YAML::Node& config, int nprocs = 1);

 private:
  void make_graph();

 private:
  void load_msh(std::string fname);
  bool is_initialized;
  dealii::SparsityPattern graph;
  using base_type::triangulation_;
  using base_type::dofhandler_;
};

// ----------------------------------------------------------------------
template <int dim>
GridHandlerPeriodic<dim>::GridHandlerPeriodic()
    : GridHandlerBase<dim>()
    , is_initialized(false)
{
  static_assert(dim == 2, "dim must be 2, 3d code is not implemented");
}

// ----------------------------------------------------------------------
template <int dim>
void
GridHandlerPeriodic<dim>::init(dealii::ParameterHandler& param_handler, int nprocs)
{
  int n_refine = param_handler.get_integer("refine phys");
  std::string mesh = param_handler.get("mesh");
  if (!mesh.compare("")) {
    this->init_square(n_refine, nprocs);
  } else {
    throw std::runtime_error(
        "Grids for periodic boundary conditions should be generated and not loaded from file!");
    // load mesh grom file
    this->load_msh(mesh);
    this->dofhandler_.distribute_dofs(this->fe);
    if (nprocs > 1) {
      dealii::GridTools::partition_triangulation(nprocs, triangulation_);
      dealii::DoFRenumbering::subdomain_wise(dofhandler_);
    } else {
      dealii::DoFRenumbering::boost::Cuthill_McKee(dofhandler_);
    }
    is_initialized = true;
  }
}

// ----------------------------------------------------------------------
template <int dim>
void
GridHandlerPeriodic<dim>::init(YAML::Node& config, int nprocs)
{
  if (!config["Mesh"]["file"]) {
    int n_refine = config["Mesh"]["nref"].as<int>();
    this->init_square(n_refine, nprocs);
  } else {
    throw std::runtime_error(
        "Grids for periodic boundary conditions should be generated and not loaded from file!");
    std::string mesh = config["Mesh"]["file"].as<std::string>();
    // load mesh grom file
    this->load_msh(mesh);
    this->dofhandler_.distribute_dofs(this->fe);
    if (nprocs > 1) {
      dealii::GridTools::partition_triangulation(nprocs, triangulation_);
      dealii::DoFRenumbering::boost::Cuthill_McKee(dofhandler_);
      dealii::DoFRenumbering::subdomain_wise(dofhandler_);
    } else {
      dealii::DoFRenumbering::boost::Cuthill_McKee(dofhandler_);
    }
  }
  is_initialized = true;
}

// ----------------------------------------------------------------------
template <int dim>
void
GridHandlerPeriodic<dim>::init_square(int nref, int nprocs)
{
  AssertThrow(!is_initialized, dealii::ExcMessage("Attempt to initialize GridHandler twice"));
  bool colorize = true;
  dealii::GridGenerator::hyper_cube(triangulation_, 0, 1, colorize);
  triangulation_.refine_global(nref);
  dofhandler_.distribute_dofs(this->fe);
  if (nprocs > 1) {
    this->make_graph();
    dealii::GridTools::partition_triangulation(nprocs, graph, triangulation_);
    dealii::DoFRenumbering::boost::Cuthill_McKee(dofhandler_);
    dealii::DoFRenumbering::subdomain_wise(dofhandler_);
  } else {
    dealii::DoFRenumbering::boost::Cuthill_McKee(dofhandler_);
  }
  is_initialized = true;
}

// ----------------------------------------------------------------------
template <int dim>
void
GridHandlerPeriodic<dim>::make_graph()
{
  typedef long int lint;
  lint FUZZY = 1e6;
  typedef typename dealii::Triangulation<dim>::cell_iterator cell_t;
  //  typedef std::pair<cell_t, int> cell_face_pair_t;
  typedef std::map<lint, int> map_t;
  map_t hmap;
  map_t vmap;
  int nfaces = 4;
  dealii::DynamicSparsityPattern csp(triangulation_.n_active_cells());
  for (auto cell = triangulation_.begin_active(); cell != triangulation_.end(); ++cell) {
    for (int i = 0; i < nfaces; ++i) {
      auto neighbor = cell->neighbor(i);
      if (neighbor != triangulation_.end()) {
        csp.add(neighbor->index(), cell->index());
        csp.add(cell->index(), neighbor->index());
      }
    }
    if (cell->at_boundary()) {
      // vertical faces
      for (int f : {0, 1}) {
        if (cell->face(f)->at_boundary()) {
          auto center = cell->face(f)->center();
          // find horizontal neighbor
          auto it = vmap.find(lint(FUZZY * center[1]));
          if (it != vmap.end()) {
            // element found
            int index_other = it->second;
            csp.add(cell->index(), index_other);
            csp.add(index_other, cell->index());
            vmap.erase(it);
          } else {
            vmap.insert(it, std::make_pair(lint(center[1] * FUZZY), cell->index()));
          }
        }
      }
      // horizontal faces
      for (int f : {2, 3}) {
        if (cell->face(f)->at_boundary()) {
          auto center = cell->face(f)->center();
          // find horizontal neighbor
          auto it = hmap.find(lint(FUZZY * center[0]));
          if (it != hmap.end()) {
            // element found
            int index_other = it->second;
            csp.add(cell->index(), index_other);
            csp.add(index_other, cell->index());
            hmap.erase(it);
          } else {
            hmap.insert(it, std::make_pair(lint(center[0] * FUZZY), cell->index()));
          }
        }
      }
    }
  }
  // no single boundary faces
  AssertThrow(hmap.empty(), dealii::ExcMessage("unpaired horizontal face"));
  AssertThrow(vmap.empty(), dealii::ExcMessage("unpaired vertical face"));
  //  graph.reinit(triangulation_.n_active_cells());
  csp.compress();
  graph.copy_from(csp);
}

// ------------------------------------------------------------------------
template <int dim>
void
GridHandlerPeriodic<dim>::load_msh(std::string fname)
{
  dealii::GridIn<2> gridin;
  gridin.attach_triangulation(triangulation_);
  std::ifstream f(fname);
  gridin.read_msh(f);
  std::cout << "Number of active cells: " << triangulation_.n_active_cells() << std::endl;
  std::cout << "Total number of cells: " << triangulation_.n_cells() << std::endl;
}

}  // end namespace boltzmann


/*
 * Sampling.hpp
 *
 *  Created on: Aug 24, 2012
 *      Author: david
 */

#ifndef DASP_IMPL_SAMPLING_HPP_
#define DASP_IMPL_SAMPLING_HPP_

#include "../Point.hpp"
#include "../Seed.hpp"
#include <Eigen/Dense>
#include <vector>

namespace dasp
{
	Eigen::MatrixXf ComputeDepthDensity(const ImagePoints& points, const Parameters& opt);

	Eigen::MatrixXf ComputeSaliency(const ImagePoints& points, const Parameters& opt);

	void AdaptClusterRadiusBySaliency(ImagePoints& points, const Eigen::MatrixXf& saliency, const Parameters& opt);

}

#endif


#ifndef NETWORK_STRING_HPP
#define NETWORK_STRING_HPP

#include <string>
#include <dlib/dnn.h>

template <class Subnet>
struct NetworkStringTraits
{
};

template <class Subnet>
struct NetworkStringTraits<dlib::relu<Subnet>>
{
  static constexpr char const* STRING = "ReLU";
};

template <class Layer, class Subnet>
struct NetworkStringTraits<dlib::add_layer<Layer, Subnet>>
{
  static constexpr char const* STRING = NetworkStringTraits<Subnet>::STRING;
};

template <long n, class Subnet>
struct NetworkStringTraits<dlib::fc<n, Subnet>>
{
  static constexpr char const* STRING = "FullyConnectedLayer";
};

template <long nr, long nc, long sy, long sx, class Subnet>
struct NetworkStringTraits<dlib::max_pool<nr, nc, sy, sx, Subnet>>
{
  static constexpr char const* STRING = "MaxPooling";
};

template <long nf, long nr, long nc, int sy, int sx, typename Subnet>
struct NetworkStringTraits<dlib::con<nf, nr, nc, sy, sx, Subnet>>
{
  static constexpr char const* STRING = "Convolution";
};

#endif //!NETWORK_STRING_HPP


// Copyright 2018-2019 Henry Schreiner and Hans Dembinski
//
// Distributed under the 3-Clause BSD License.  See accompanying
// file LICENSE or https://github.com/scikit-hep/boost-histogram for details.

#include <bh_python/pybind11.hpp>

#include <bh_python/accumulators/mean.hpp>
#include <bh_python/accumulators/ostream.hpp>
#include <bh_python/accumulators/weighted_mean.hpp>
#include <bh_python/accumulators/weighted_sum.hpp>
#include <bh_python/kwargs.hpp>
#include <bh_python/register_accumulator.hpp>
#include <boost/histogram/accumulators/sum.hpp>
#include <pybind11/operators.h>

/// The mean fill can be implemented once. (sum fill varies slightly)
template <class T>
decltype(auto) make_mean_fill() {
    return [](T& self, py::object value, py::object weight) {
        if(weight.is_none()) {
            py::vectorize([](T& self, double val) { self(val); })(self, value);
        } else {
            py::vectorize([](T& self, double wei, double val) {
                self(bh::weight(wei), val);
            })(self, weight, value);
        }
        return self;
    };
}

/// The mean call can be implemented once. (sum uses +=)
template <class T>
decltype(auto) make_mean_call() {
    return [](T& self, double value, py::object weight) {
        if(weight.is_none())
            self(value);
        else
            self(bh::weight(py::cast<double>(weight)), value);

        return self;
    };
}

template <class T>
decltype(auto) make_buffer() {
    return [](T& self) -> py::buffer_info {
        return py::buffer_info(
            &self,                              // Pointer to buffer
            sizeof(T),                          // Size of one scalar
            py::format_descriptor<T>::format(), // Format registered with
                                                // PYBIND11_NUMPY_DTYPE
            0,                                  // Number of dimensions
            {},                                 // Buffer dimensions
            {}                                  // Stride
        );
    };
}

void register_accumulators(py::module& accumulators) {
    // Naming convention:
    // If a value is publicly available in Boost.Histogram accumulators
    // as a method, it has the same name in the numpy record array.
    // If it is not available except through a computation, it has
    // the same name as the private property without the trailing _.

    using weighted_sum = accumulators::weighted_sum<double>;

    PYBIND11_NUMPY_DTYPE(weighted_sum, value, variance);

    register_accumulator<weighted_sum>(
        accumulators, "WeightedSum", py::buffer_protocol())

        .def_buffer(make_buffer<weighted_sum>())

        .def(py::init<const double&>(), "value"_a)
        .def(py::init<const double&, const double&>(), "value"_a, "variance"_a)

        .def_readonly("value", &weighted_sum::value)
        .def_readonly("variance", &weighted_sum::variance)

        .def("__iadd__",
             [](weighted_sum& self, double value) {
                 self += bh::weight(value);
                 return self;
             })

        .def(
            "fill",
            [](weighted_sum& self, py::object value, py::object variance) {
                if(variance.is_none()) {
                    py::vectorize([](weighted_sum& self, double val) {
                        self += bh::weight(val);
                    })(self, value);
                } else {
                    py::vectorize([](weighted_sum& self, double val, double var) {
                        self += weighted_sum(val, var);
                    })(self, value, variance);
                }
                return self;
            },
            "value"_a,
            py::kw_only(),
            "variance"_a = py::none(),
            "Fill the accumulator with values. Optional variance parameter.")

        // This adapts existing memory to an accumulator
        .def_static("_make", py::vectorize([](const double& a, const double& b) {
                        return weighted_sum(a, b);
                    }))

        // This creates a array of accumulators using the normal constructor arguments
        .def_static("_array", py::vectorize([](const double& a, const double& b) {
                        return weighted_sum(a, b);
                    }))

        .def("__getitem__",
             [](const weighted_sum& self, py::str key) {
                 if(key.equal(py::str("value")))
                     return self.value;
                 else if(key.equal(py::str("variance")))
                     return self.variance;
                 else
                     throw py::key_error(
                         py::str("{0} not one of value, variance").format(key));
             })
        .def("__setitem__",
             [](weighted_sum& self, py::str key, double value) {
                 if(key.equal(py::str("value")))
                     self.value = value;
                 else if(key.equal(py::str("variance")))
                     self.variance = value;
                 else
                     throw py::key_error(
                         py::str("{0} not one of value, variance").format(key));
             })

        .def("_ipython_key_completions_",
             [](py::object /* self */) { return py::make_tuple("value", "variance"); })

        ;

    using sum = bh::accumulators::sum<double>;

    register_accumulator<sum>(accumulators, "Sum")
        .def(py::init<const double&>(), "value"_a)

        .def_property_readonly("value", &sum::value)

        .def(py::self += double())

        .def(
            "fill",
            [](sum& self, py::object value) {
                py::vectorize([](sum& self, double v) { self += v; })(self, value);
                return self;
            },
            "value"_a,
            "Run over an array with the accumulator")

        .def_property_readonly("_small", &sum::small_part)
        .def_property_readonly("_large", &sum::large_part)

        ;

    using weighted_mean = accumulators::weighted_mean<double>;
    PYBIND11_NUMPY_DTYPE(weighted_mean,
                         sum_of_weights,
                         sum_of_weights_squared,
                         value,
                         _sum_of_weighted_deltas_squared);

    register_accumulator<weighted_mean>(
        accumulators, "WeightedMean", py::buffer_protocol())

        .def_buffer(make_buffer<weighted_mean>())

        .def(py::init<const double&, const double&, const double&, const double&>(),
             "sum_of_weights"_a,
             "sum_of_weights_squared"_a,
             "value"_a,
             "variance"_a)

        .def_readonly("sum_of_weights", &weighted_mean::sum_of_weights)
        .def_readonly("sum_of_weights_squared", &weighted_mean::sum_of_weights_squared)
        .def_readonly("value", &weighted_mean::value)
        .def_readonly("_sum_of_weighted_deltas_squared",
                      &weighted_mean::_sum_of_weighted_deltas_squared)

        .def_property_readonly("variance", &weighted_mean::variance)

        .def("__call__",
             make_mean_call<weighted_mean>(),
             "value"_a,
             py::kw_only(),
             "weight"_a = py::none(),
             "Fill with value and optional keyword-only weight")

        .def("fill",
             make_mean_fill<weighted_mean>(),
             "value"_a,
             py::kw_only(),
             "weight"_a = py::none(),
             "Fill the accumulator with values. Optional weight parameter.")

        .def_static(
            "_make",
            py::vectorize(
                [](const double& a, const double& b, const double& c, const double& d) {
                    return weighted_mean(a, b, c, d, true);
                }))

        .def_static(
            "_array",
            py::vectorize(
                [](const double& a, const double& b, const double& c, const double& d) {
                    return weighted_mean(a, b, c, d);
                }))

        .def("__getitem__",
             [](const weighted_mean& self, py::str key) {
                 if(key.equal(py::str("value")))
                     return self.value;
                 else if(key.equal(py::str("sum_of_weights")))
                     return self.sum_of_weights;
                 else if(key.equal(py::str("sum_of_weights_squared")))
                     return self.sum_of_weights_squared;
                 else if(key.equal(py::str("_sum_of_weighted_deltas_squared")))
                     return self._sum_of_weighted_deltas_squared;
                 else
                     throw py::key_error(
                         py::str(
                             "{0} not one of value, sum_of_weights, "
                             "sum_of_weights_squared, _sum_of_weighted_deltas_squared")
                             .format(key));
             })
        .def("__setitem__",
             [](weighted_mean& self, py::str key, double value) {
                 if(key.equal(py::str("value")))
                     self.value = value;
                 else if(key.equal(py::str("sum_of_weights")))
                     self.sum_of_weights = value;
                 else if(key.equal(py::str("sum_of_weights_squared")))
                     self.sum_of_weights_squared = value;
                 else if(key.equal(py::str("_sum_of_weighted_deltas_squared")))
                     self._sum_of_weighted_deltas_squared = value;
                 else
                     throw py::key_error(
                         py::str(
                             "{0} not one of value, sum_of_weights, "
                             "sum_of_weights_squared, _sum_of_weighted_deltas_squared")
                             .format(key));
             })

        .def("_ipython_key_completions_",
             [](py::object /* self */) {
                 return py::make_tuple("value",
                                       "sum_of_weights",
                                       "sum_of_weights_squared",
                                       "_sum_of_weighted_deltas_squared");
             })

        ;

    using mean = accumulators::mean<double>;
    PYBIND11_NUMPY_DTYPE(mean, count, value, _sum_of_deltas_squared);

    register_accumulator<mean>(accumulators, "Mean", py::buffer_protocol())
        .def_buffer(make_buffer<mean>())

        .def(py::init<const double&, const double&, const double&>(),
             "count"_a,
             "value"_a,
             "variance"_a)

        .def_readonly("count", &mean::count)
        .def_readonly("value", &mean::value)
        .def_readonly("_sum_of_deltas_squared", &mean::_sum_of_deltas_squared)

        .def_property_readonly("variance", &mean::variance)

        .def("__call__",
             make_mean_call<mean>(),
             "value"_a,
             py::kw_only(),
             "weight"_a = py::none(),
             "Fill with value and optional keyword-only weight")

        .def("fill",
             make_mean_fill<mean>(),
             "value"_a,
             py::kw_only(),
             "weight"_a = py::none(),
             "Fill the accumulator with values. Optional weight parameter.")

        .def_static(
            "_make",
            py::vectorize([](const double& a, const double& b, const double& c) {
                return mean(a, b, c, true);
            }))

        .def_static(
            "_array",
            py::vectorize([](const double& a, const double& b, const double& c) {
                return mean(a, b, c);
            }))

        .def("__getitem__",
             [](const mean& self, py::str key) {
                 if(key.equal(py::str("count")))
                     return self.count;
                 else if(key.equal(py::str("value")))
                     return self.value;
                 else if(key.equal(py::str("_sum_of_deltas_squared")))
                     return self._sum_of_deltas_squared;
                 else
                     throw py::key_error(
                         py::str("{0} not one of count, value, _sum_of_deltas_squared")
                             .format(key));
             })
        .def("__setitem__",
             [](mean& self, py::str key, double value) {
                 if(key.equal(py::str("count")))
                     self.count = value;
                 else if(key.equal(py::str("value")))
                     self.value = value;
                 else if(key.equal(py::str("_sum_of_deltas_squared")))
                     self._sum_of_deltas_squared = value;
                 else
                     throw py::key_error(
                         py::str("{0} not one of count, value, _sum_of_deltas_squared")
                             .format(key));
             })

        .def("_ipython_key_completions_",
             [](py::object /* self */) {
                 return py::make_tuple("count", "value", "_sum_of_deltas_squared");
             })

        ;
}


// Copyright Louis Dionne 2013-2017
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)

#include <boost/hana/assert.hpp>
#include <boost/hana/equal.hpp>
#include <boost/hana/integral_constant.hpp>
#include <boost/hana/less.hpp>
#include <boost/hana/plus.hpp>
#include <boost/hana/while.hpp>

#include <vector>
namespace hana = boost::hana;
using namespace hana::literals;

int main() {
    // while_ with a Constant condition (loop is unrolled at compile-time)
    {
        std::vector<int> ints;
        auto final_state = hana::while_(hana::less.than(10_c), 0_c, [&](auto i) {
            ints.push_back(i);
            return i + 1_c;
        });

        // The state is known at compile-time
        BOOST_HANA_CONSTANT_CHECK(final_state == 10_c);

        BOOST_HANA_RUNTIME_CHECK(ints == std::vector<int>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
    }

    // while_ with a constexpr or runtime condition (loop is not unrolled)
    {
        std::vector<int> ints;
        int final_state = hana::while_(hana::less.than(10), 0, [&](int i) {
            ints.push_back(i);
            return i + 1;
        });

        // The state is known only at runtime, or at compile-time if constexpr
        BOOST_HANA_RUNTIME_CHECK(final_state == 10);

        BOOST_HANA_RUNTIME_CHECK(ints == std::vector<int>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
    }
}