#include <vector>
#include <map>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <unordered_map>
#include <chrono>
#include <iostream>
#include <sstream>
#include <random>
#include <array>
#include <unordered_map>
#include <boost/container/flat_map.hpp>

#if !defined KEY_SIZE
#define KEY_SIZE 1
#endif

using key_type = std::array<uint32_t, KEY_SIZE>;
using value_t = size_t;

struct hasher_t
{
    typedef std::size_t result_type;

    std::size_t operator()(key_type const &key) const noexcept
    {
        size_t r = 0;
        for (auto e : key)
        {
            r += r / 2 + r * 2 + e;
        }
        return r;
    }
};

template <typename key_t, typename mapped_t>
struct linemap
{
    using key_type = key_t;
    using mapped_type = mapped_t;
    using value_type = std::pair<key_type const, mapped_type>;

    std::vector<value_type> m_;

    using const_iterator = decltype(m_.cend());
    using iterator = decltype(m_.end());

    iterator end() { return m_.end(); }
    iterator begin() { return m_.begin(); }
    const_iterator end() const { return m_.cend(); }
    const_iterator begin() const { return m_.cbegin(); }
    const_iterator cend() const { return m_.cend(); }
    const_iterator cbegin() const { return m_.cbegin(); }

    const_iterator find(key_type const &k) const
    {
        return std::find_if(m_.cbegin(), m_.cend(), [&k](value_type const &v)
                            { return v.first == k; });
    }
    iterator find(key_type const &k)
    {
        return std::find_if(m_.begin(), m_.end(), [&k](value_type const &v)
                            { return v.first == k; });
    }
    mapped_type &operator[](key_type const &k)
    {
        auto it = find(k);
        if (it != end())
        {
            return it->second;
        }
        m_.push_back(value_type(k, mapped_type{}));
        return m_.back().second;
    }
    size_t size() const { return m_.size(); }
};

using linemap_t = linemap<key_type, value_t>;
using uomap_t = std::unordered_map<key_type, value_t, hasher_t>;
using map_t = std::unordered_map<key_type, value_t>;
using bfmap_t = boost::container::flat_map<key_type, value_t>;

key_type key_at(size_t ix)
{
    key_type k = {};
    *k.rbegin() = ix & ~0u;
    *(k.rbegin() + 1) = (ix >> 32) & ~0u;
    return k;
}

template <typename con_t, typename k_t, typename v_t>
struct runner
{
    int trial_count_ = 100;
    const size_t ecount_;
    std::string name_;
    std::mt19937 rng_{0};
    static constexpr size_t KEY_COUNT = 16;
    static constexpr size_t CON_COUNT = 16;
    std::array<k_t, KEY_COUNT> keys_;
    std::array<con_t, CON_COUNT> cons_;

    con_t create_con(size_t i)
    {
        std::mt19937 rng(i);
        std::uniform_int_distribution<size_t> vdist(0, 255u);
        std::vector<size_t> nums;
        for (size_t i = 0; i < ecount_; ++i)
        {
            nums.push_back(i);
        }
        std::shuffle(nums.begin(), nums.end(), rng);
        con_t c;
        for (auto n : nums)
        {
            auto k = key_at(n);
            c[k] = vdist(rng_);
        }
        return c;
    }

    runner(size_t csize, size_t ecount, char const *name)
        : trial_count_(csize),
          ecount_(ecount),
          name_(name)
    {
        for (size_t i = 0; i < cons_.size(); ++i)
        {
            cons_[i] = create_con(i);
        }
        for (size_t i = 0; i < keys_.size(); ++i)
        {
            keys_[i] = key_at(i);
        }
    }

    std::pair<uint64_t, double> run()
    {
        uint64_t sum = 0;
        using clock = std::chrono::high_resolution_clock;
        std::uniform_int_distribution<size_t> key_dist(0, keys_.size() - 1);
        std::uniform_int_distribution<size_t> con_dist(0, cons_.size() - 1);

        auto start = clock::now();
        for (int i = 0; i < trial_count_; ++i)
        {
            auto const &key = keys_[key_dist(rng_)];
            auto const &con = cons_[con_dist(rng_)];
            auto it = con.find(key);
            if (it != con.end())
            {
                sum += it->second;
            }
        }
        auto diff = clock::now() - start;
        double sec = std::chrono::duration_cast<std::chrono::nanoseconds>(diff).count() * 1e-9;
        return {sum, sec};
    }
};

int main(int argc, char const *argv[])
{
    size_t csize = argc < 2 ? 100 : std::atoi(argv[1]);
    size_t ecount = argc < 3 ? 10 : std::atoi(argv[2]);

    std::vector<int64_t> sums;

    // runner<std::vector<std::pair<key_type, value_t>>, key_type, value_t> mvec(csize, ecount, "vector");
    runner<std::map<key_type, value_t>, key_type, value_t> mmap(csize, ecount, "map");
    runner<uomap_t, key_type, value_t> muomap(csize, ecount, "uomap");
    runner<linemap_t, key_type, value_t> mlinemap(csize, ecount, "linemap");
    runner<bfmap_t,key_type, value_t> mbfmap(csize, ecount, "bfmap");
    std::vector<double> ticks;
    for (int i = 0; i < 3; ++i)
    {
        ticks.clear();
        {
            auto [s, t] = mmap.run();
            ticks.push_back(t);
            sums.push_back(s);
        }
        {
            auto [s, t] = muomap.run();
            ticks.push_back(t);
            sums.push_back(s);
        }
        {
            auto [s, t] = mlinemap.run();
            ticks.push_back(t);
            sums.push_back(s);
        }
        {
            auto [s, t] = mbfmap.run();
            ticks.push_back(t);
            sums.push_back(s);
        }
    }
    std::cout << csize << "," << ecount;
    for (auto t : ticks)
    {
        std::cout << "," << t;
    }
    std::cout << std::endl;
    for (auto e : sums)
    {
        std::cerr << e << " ";
    }
    std::cerr << std::endl;
    return 0;
}


#include "Version.h"
#include "Freenect2Camera.h"

#include <iostream>
#include <numeric>
#include <cmath>
#include <thread>
#include <memory>

#include <Eigen/Core>

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

#include <libfreenect2/packet_pipeline.h>
#include <libfreenect2/logger.h>

/** Freenect2 Depth Camera Backend **/
namespace ark {
Freenect2Camera::Freenect2Camera(const std::string &serial, bool use_kde,
                                 float scale, bool verbose)
    : _use_kde(use_kde), _serial(serial), _scale(scale), _verbose(verbose) {
    if (!verbose) {
        libfreenect2::setGlobalLogger(nullptr);
    }
    _freenect2 = std::make_unique<libfreenect2::Freenect2>();
    if (_freenect2->enumerateDevices() == 0) {
        std::cerr << "Fatal: No Freenect2 devices found" << std::endl;
        deviceOpenFlag = false;
        return;
    }

#ifdef LIBFREENECT2_WITH_CUDA_SUPPORT
    if (_use_kde)
        _pipeline = new libfreenect2::CudaPacketPipeline(-1);
    else
        _pipeline = new libfreenect2::CudaKdePacketPipeline(-1);
#elif defined(LIBFREENECT2_WITH_OPENCL_SUPPORT)
    if (_use_kde)
        _pipeline = new libfreenect2::OpenCLKdePacketPipeline(-1);
    else
        _pipeline = new libfreenect2::OpenCLPacketPipeline(-1);
#elif defined(LIBFREENECT2_WITH_OPENGL_SUPPORT)
    _pipeline = new libfreenect2::OpenGLPacketPipeline();
#else
    _pipeline = new libfreenect2::CPUPacketPipeline();
#endif

    if (_serial.empty()) _serial = _freenect2->getDefaultDeviceSerialNumber();

    if (!_pipeline) return;
    _dev = std::unique_ptr<libfreenect2::Freenect2Device>(
        _freenect2->openDevice(_serial, _pipeline));

    _listener = std::make_unique<libfreenect2::SyncMultiFrameListener>(
        libfreenect2::Frame::Color | libfreenect2::Frame::Depth);

    const int DEFAULT_W = 1920, DEFAULT_H = 1080;
    _scaled_width = _scale * DEFAULT_W;
    _scaled_height = _scale * DEFAULT_H;

    _dev->setColorFrameListener(_listener.get());
    _dev->setIrAndDepthFrameListener(_listener.get());
    if (verbose) {
        std::cout << "Freenect device serial: " << _dev->getSerialNumber()
                  << std::endl;
        std::cout << "Freenect device firmware: " << _dev->getFirmwareVersion()
                  << std::endl;
    }

    if (!_dev->start()) {
        _pipeline = nullptr;
    }

    libfreenect2::Freenect2Device::ColorCameraParams rgb_cam_params =
        _dev->getColorCameraParams();
    _registration = std::make_unique<libfreenect2::Registration>(
        _dev->getIrCameraParams(), rgb_cam_params);

    _xy_table_cache.resize(3, DEFAULT_H * DEFAULT_W);
    for (size_t i = 0; i < DEFAULT_H; ++i) {
        for (size_t j = 0; j < DEFAULT_W; ++j) {
            auto xyz = _xy_table_cache.col(i * DEFAULT_W + j);
            xyz.x() = (j - rgb_cam_params.cx) / rgb_cam_params.fx * 1e-3f;
            xyz.y() = (i - rgb_cam_params.cy) / rgb_cam_params.fy * 1e-3f;
            xyz.z() = 1e-3f;
        }
    }
    fx = rgb_cam_params.fx * _scale;
    cx = rgb_cam_params.cx * _scale;
    fy = rgb_cam_params.fy * _scale;
    cy = rgb_cam_params.cy * _scale;
}

Freenect2Camera::~Freenect2Camera() {
    if (_dev) {
        _dev->stop();
        _dev->close();
    }
}

const std::string Freenect2Camera::getModelName() const {
    return "Kinect V2 (Freenect2)";
}

int Freenect2Camera::getWidth() const { return _scaled_width; }
int Freenect2Camera::getHeight() const { return _scaled_height; }
bool Freenect2Camera::hasRGBMap() const { return true; }

void Freenect2Camera::update(cv::Mat &xyz_map, cv::Mat &rgb_map,
                             cv::Mat &ir_map, cv::Mat &amp_map,
                             cv::Mat &flag_map) {
    if (!_listener->waitForNewFrame(_frames, 10 * 1000)) {
        std::cout << "Freenect2 timeout!" << std::endl;
        badInputFlag = true;
        return;
    }
    badInputFlag = false;
    libfreenect2::Frame *rgb = _frames[libfreenect2::Frame::Color];
    libfreenect2::Frame *depth = _frames[libfreenect2::Frame::Depth];
    libfreenect2::Frame undistorted(depth->width, depth->height, 4);
    libfreenect2::Frame registered(depth->width, depth->height, 4);
    libfreenect2::Frame big_depth(rgb->width, rgb->height + 2, 4);
    std::vector<int> color_to_depth(depth->width * depth->height);
    _registration->apply(rgb, depth, &undistorted, &registered, true,
                         &big_depth, color_to_depth.data());

    const bool NEED_RESIZE = _scale != 1.0f;
    const size_t INPUT_SIZE = rgb->height * rgb->width;

    cv::Mat rgb_tmp, xyz_tmp;
    if (NEED_RESIZE) {
        rgb_tmp.create(rgb->height, rgb->width, CV_8UC3);
        xyz_tmp.create(rgb->height, rgb->width, CV_32FC3);
    }
    Eigen::Map<Eigen::Array<unsigned char, 3, Eigen::Dynamic>>(
        NEED_RESIZE ? rgb_tmp.data : rgb_map.data, 3, INPUT_SIZE) =
        Eigen::Map<Eigen::Array<unsigned char, 4, Eigen::Dynamic>>(rgb->data, 4,
                                                                   INPUT_SIZE)
            .topRows<3>();

    // Depth
    Eigen::Map<Eigen::Array<float, 3, Eigen::Dynamic>> xyz_out(
        reinterpret_cast<float *>(NEED_RESIZE ? xyz_tmp.data : xyz_map.data), 3,
        INPUT_SIZE);
    Eigen::Map<Eigen::Array<float, 1, Eigen::Dynamic>> big_depth_in(
        reinterpret_cast<float *>(big_depth.data) + rgb->width, 1, INPUT_SIZE);
    xyz_out = _xy_table_cache;
    xyz_out.rowwise() *= big_depth_in;
    for (int i = 0; i < INPUT_SIZE; ++i) {
        if (std::isinf(xyz_out(2, i))) {
            xyz_out.col(i).setZero();
        }
    }
    if (NEED_RESIZE) {
        cv::resize(rgb_tmp, rgb_map, cv::Size(_scaled_width, _scaled_height));
        cv::resize(xyz_tmp, xyz_map, cv::Size(_scaled_width, _scaled_height));
    }

    timestamp =
        static_cast<uint64_t>(std::max(rgb->timestamp, depth->timestamp)) *
        125000;

    _listener->release(_frames);
}  // namespace ark
}  // namespace ark


#ifndef PYTHONIC_NUMPY_ISPOSINF_HPP
#define PYTHONIC_NUMPY_ISPOSINF_HPP

#include "pythonic/include/numpy/isposinf.hpp"

#include "pythonic/utils/functor.hpp"
#include "pythonic/types/ndarray.hpp"
#include "pythonic/utils/numpy_traits.hpp"

#include <boost/simd/function/is_inf.hpp>
#include <boost/simd/function/is_positive.hpp>

PYTHONIC_NS_BEGIN

namespace numpy
{
  namespace wrapper
  {
    template <class T>
    bool isposinf(T const &t)
    {
      return boost::simd::is_inf(t) && boost::simd::is_positive(t);
    }
  }
#define NUMPY_NARY_FUNC_NAME isposinf
#define NUMPY_NARY_FUNC_SYM wrapper::isposinf
#include "pythonic/types/numpy_nary_expr.hpp"
}
PYTHONIC_NS_END

#endif


/**
 * @file      Mesh.cpp
 *
 * @brief     Implementation for Mesh type
 *
 * @author    Stefan Reinhold
 * @copyright Copyright (C) 2018 Stefan Reinhold  -- All Rights Reserved.
 *            You may use, distribute and modify this code under the terms of
 *            the AFL 3.0 license; see LICENSE for full license details.
 */

#include "Mesh.h"
#include "CheckExtension.h"
#include "MatrixIO.h"
#include "MeshAdaptors.h"
#include "MeshHelpers.h"
#include "SIMesh.h"

#include <Eigen/Sparse>
#include <gsl/gsl>
#include <igl/orient_outward.h>
#include <igl/orientable_patches.h>
#include <igl/ray_mesh_intersect.h>
#include <igl/readOFF.h>
#include <igl/read_triangle_mesh.h>
#include <igl/upsample.h>
#include <igl/writeOFF.h>
#include <igl/write_triangle_mesh.h>

#include <algorithm>
#include <cctype>
#include <fstream>
#include <functional>
#include <iterator>
#include <limits>

namespace CortidQCT {

using namespace Internal;

namespace {

template <class DerivedV, class DerivedF>
void orientOutwards(Eigen::MatrixBase<DerivedV> const &V,
                    Eigen::MatrixBase<DerivedF> &F) {
  Eigen::Matrix<typename DerivedF::Scalar, Eigen::Dynamic, Eigen::Dynamic> C;
  Eigen::Matrix<bool, Eigen::Dynamic, 1> I;

  Expects(V.rows() > 0 && V.cols() == 3);
  Expects(F.rows() > 0 && F.cols() == 3);
  igl::orientable_patches(F.derived(), C.derived());

  igl::orient_outward(V.derived(), F.derived(), C, F.derived(), I);
}

struct SequencialTransform {
  template <class I, class O, class F>
  void operator()(I b, I e, O o, F &&f) const {
    using std::transform;
    transform(b, e, o, std::forward<F>(f));
  }
};

struct ParallelTransform {
  template <class I, class O, class F>
  void operator()(I b, I e, O o, F &&f) const {
    using gsl::narrow_cast;
    using std::distance;
    auto const N = distance(b, e);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wsign-compare"

#pragma omp parallel for
    for (std::size_t i = 0; i < narrow_cast<std::size_t>(N); ++i) {
      o[i] = f(b[i]);
    }
  }

#pragma clang diagnostic pop
};

} // anonymous namespace

template <class T> void Mesh<T>::ensurePostconditions() const {
  using Eigen::Dynamic;
  using Eigen::Map;
  using Eigen::Matrix;

  Ensures(vertexData_.size() % 3 == 0);
  Ensures(indexData_.size() % 3 == 0);
  Ensures(labelData_.size() == vertexData_.size() / 3);
  Ensures(normalData_.size() % 3 == 0);
  Ensures(normalData_.size() == vertexData_.size());

  VertexMatrix<T> const V = vertexMatrix(*this);
  FacetMatrix const F = facetMatrix(*this);
}

template <class T>
Mesh<T> &Mesh<T>::loadFromFile(std::string const &meshFilename,
                               std::string const &labelFilename) {
  using Eigen::Dynamic;
  using Eigen::Map;
  using Eigen::Matrix;
  using Eigen::MatrixXd;
  using Eigen::MatrixXi;
  using gsl::narrow;

  // Check file format since igl does only print an error
  std::string const supportedFormats[] = {"obj", "off",  "stl",   "wrl",
                                          "ply", "mesh", "simesh"};

  if (!IO::checkExtensions(meshFilename, supportedFormats)) {
    throw std::invalid_argument("Unsupported file format '" +
                                IO::extension(meshFilename) + "'");
  }

  Size lVertexCount{0};

  bool meshLoaded = false;
  if (IO::extension(meshFilename, true) == "simesh") {
    *this = readFromSIMesh<T>(meshFilename, false);
    meshLoaded = true;

    Map<Matrix<T, 3, Dynamic>> vertices{vertexData_.data(), 3,
                                        narrow<Eigen::Index>(vertexCount())};
    Map<Matrix<Index, 3, Dynamic>> indices{
        indexData_.data(), 3, narrow<Eigen::Index>(triangleCount())};

    VertexMatrix<T> V = vertices.transpose();
    FacetMatrix F = indices.transpose();
    orientOutwards(V, F);

    vertices = V.transpose();
    indices = F.transpose();

    lVertexCount = vertexCount();
  }

  MatrixXd vertices;
  MatrixXi indices;

  constexpr auto magicLabel = std::numeric_limits<Label>::max();

  if (!meshLoaded) {
    // Ensure the file exists and is readable, otherwise
    // igl::read_triangle_mesh() might SEGFAULT.
    if (!std::ifstream{meshFilename} ||
        !igl::read_triangle_mesh(meshFilename, vertices, indices)) {
      throw std::invalid_argument("Failed to read mesh from file '" +
                                  meshFilename + "'");
    }
  }

  // load labels. Fill labels vector with prefined value to be able to check if
  // all labels are correctly initialized.
  Matrix<Label, Dynamic, 1> labels =
      magicLabel * Matrix<Label, Dynamic, 1>::Ones(vertices.rows());

  std::ifstream labelStream{labelFilename};
  if (!labelStream) {
    throw std::invalid_argument("Failed to read labels form file '" +
                                labelFilename + "'");
  }
  labelStream >> io(labels);

  // Check if labels were correctly initialized
  if ((labels.array() == magicLabel).any()) {
    throw std::invalid_argument(
        "Failed to read labels from file '" + labelFilename +
        "', maybe the files does not contain the right number of labels (" +
        std::to_string(vertices.rows()) + ")");
  }

  VertexData vertexData;
  IndexData indexData;
  if (!meshLoaded) {
    lVertexCount = narrow<Size>(vertices.rows());
    auto const lTriangleCount = narrow<Size>(indices.rows());

    orientOutwards(vertices, indices);

    // Reserve storage for vertex and index data
    vertexData = VertexData(3 * lVertexCount);
    indexData = IndexData(3 * lTriangleCount);

    // Copy data from eigen matrix into vectors
    Map<Matrix<Scalar, 3, Dynamic>>{vertexData.data(), 3, vertices.rows()} =
        vertices.cast<Scalar>().transpose();
    Map<Matrix<Index, 3, Dynamic>>{indexData.data(), 3, indices.rows()} =
        indices.cast<Index>().transpose();

    Ensures(vertexData.size() == 3 * lVertexCount);
    Ensures(indexData.size() == 3 * lTriangleCount);
  }

  auto labelData = LabelData(lVertexCount);
  Map<Matrix<Label, Dynamic, 1>>{labelData.data(), vertices.rows(), 1} = labels;
  Ensures(labelData.size() == lVertexCount);
  labelData_ = std::move(labelData);

  if (!meshLoaded) {
    vertexData_ = std::move(vertexData);
    indexData_ = std::move(indexData);
  }

  updatePerVertexNormals();

  ensurePostconditions();

  return *this;
}

template <class T>
Mesh<T> &Mesh<T>::loadFromFile(std::string const &meshFilename,
                               ColorToLabelMap<Label, double> const &colorMap) {
  using Eigen::Dynamic;
  using Eigen::Map;
  using Eigen::Matrix;
  using Eigen::MatrixXd;
  using Eigen::MatrixXi;
  using gsl::narrow;

  // Check file format since igl does only print an error
  std::string const supportedFormats[] = {"off", "coff", "simesh"};

  if (!IO::checkExtensions(meshFilename, supportedFormats)) {
    throw std::invalid_argument("Unsupported file format '" +
                                IO::extension(meshFilename) + "'");
  }

  if (IO::extension(meshFilename, true) == "simesh") {
    *this = readFromSIMesh<T>(meshFilename, true);
    Map<Matrix<T, 3, Dynamic>> vertices{vertexData_.data(), 3,
                                        narrow<Eigen::Index>(vertexCount())};
    Map<Matrix<Index, 3, Dynamic>> indices{
        indexData_.data(), 3, narrow<Eigen::Index>(triangleCount())};

    VertexMatrix<T> V = vertices.transpose();
    FacetMatrix F = indices.transpose();
    orientOutwards(V, F);

    vertices = V.transpose();
    indices = F.transpose();

    ensurePostconditions();
    return *this;
  }

  MatrixXd vertices, colors;
  MatrixXi indices;

  // Ensure the file exists and is readable, otherwise
  // igl::readOFF() might SEGFAULT.
  if (!std::ifstream{meshFilename} ||
      !igl::readOFF(meshFilename, vertices, indices, colors)) {
    throw std::invalid_argument("Failed to read mesh from file '" +
                                meshFilename + "'");
  }

  auto const lVertexCount = narrow<Size>(vertices.rows());
  auto const lTriangleCount = narrow<Size>(indices.rows());

  orientOutwards(vertices, indices);

  // Reserve storage for vertex and index data
  auto vertexData = VertexData(3 * lVertexCount);
  auto indexData = IndexData(3 * lTriangleCount);
  auto labelData = LabelData(lVertexCount);

  // Copy data from eigen matrix into vectors
  Map<Matrix<Scalar, 3, Dynamic>>{vertexData.data(), 3, vertices.rows()} =
      vertices.cast<Scalar>().transpose();
  Map<Matrix<Index, 3, Dynamic>>{indexData.data(), 3, indices.rows()} =
      indices.cast<Index>().transpose();

  // Convert colors to labels
  for (auto i = 0; i < colors.rows(); ++i) {
    labelData[gsl::narrow_cast<std::size_t>(i)] =
        colorMap(colors(i, 0), colors(i, 1), colors(i, 2));
  }

  Ensures(vertexData.size() == 3 * lVertexCount);
  Ensures(indexData.size() == 3 * lTriangleCount);
  Ensures(labelData.size() == lVertexCount);

  vertexData_ = std::move(vertexData);
  indexData_ = std::move(indexData);
  labelData_ = std::move(labelData);

  updatePerVertexNormals();

  ensurePostconditions();

  return *this;
}

template <class T>
void Mesh<T>::writeToFile(std::string const &meshFilename,
                          std::string const &labelFilename) const {

  using Eigen::Dynamic;
  using Eigen::Map;
  using Eigen::Matrix;
  using gsl::narrow;

  if (isEmpty()) { return; }

  // Check file format since igl does only print an error
  std::string const supportedFormats[] = {"obj", "off",  "stl",   "wrl",
                                          "ply", "mesh", "simesh"};

  if (!IO::checkExtensions(meshFilename, supportedFormats)) {
    throw std::invalid_argument("Unsupported file format '" +
                                IO::extension(meshFilename) + "'");
  }

  // check for SIMEsh format
  if (IO::extension(meshFilename, true) == "simesh") {
    constexpr auto magicLabel = std::numeric_limits<Label>::max();
    auto const includeLabels = labelVector(*this)[0] != magicLabel;
    writeToSIMesh(*this, meshFilename, includeLabels);
  } else {

    // Convert vertex and index data to a format igl understads
    Map<Matrix<Scalar, 3, Dynamic> const> const Vmap{
        vertexData_.data(), 3, narrow<Eigen::Index>(vertexCount())};
    Map<Matrix<Index, 3, Dynamic> const> const Fmap{
        indexData_.data(), 3, narrow<Eigen::Index>(triangleCount())};

    Eigen::Matrix<double, Dynamic, 3> const V =
        Vmap.template cast<double>().transpose();
    Eigen::Matrix<int, Dynamic, 3> const F =
        Fmap.template cast<int>().transpose();

    if (!igl::write_triangle_mesh(meshFilename, V, F)) {
      throw std::invalid_argument("Failed to write mesh to file '" +
                                  meshFilename + "'");
    }
  }

  // Write lables
  std::ofstream labelStream{labelFilename};

  if (!labelStream) {
    throw std::invalid_argument("Failed to write per-vertex labels to file '" +
                                labelFilename + "'");
  }

  Map<Matrix<Label, Dynamic, 1> const> const labelMap{
      labelData_.data(), narrow<Eigen::Index>(vertexCount())};
  labelStream << labelMap;
}

template <class T>
void Mesh<T>::writeToFile(
    std::string const &meshFilename,
    LabelToColorMap<double, Label> const &labelMap) const {

  using Eigen::Dynamic;
  using Eigen::Map;
  using Eigen::Matrix;
  using gsl::narrow;

  if (isEmpty()) { return; };

  // Check file format since igl does only print an error
  std::string const supportedFormats[] = {"off", "coff", "simesh"};

  if (!IO::checkExtensions(meshFilename, supportedFormats)) {
    throw std::invalid_argument("Unsupported file format '" +
                                IO::extension(meshFilename) + "'");
  }

  // check for SIMEsh format
  if (IO::extension(meshFilename, true) == "simesh") {
    writeToSIMesh(*this, meshFilename, true);
  } else {
    // Convert vertex and index data to a format igl understads
    Map<Matrix<Scalar, 3, Dynamic> const> const Vmap{
        vertexData_.data(), 3, narrow<Eigen::Index>(vertexCount())};
    Map<Matrix<Index, 3, Dynamic> const> const Fmap{
        indexData_.data(), 3, narrow<Eigen::Index>(triangleCount())};

    Matrix<double, Dynamic, 3> const V =
        Vmap.template cast<double>().transpose();
    Matrix<int, Dynamic, 3> const F = Fmap.template cast<int>().transpose();
    Matrix<double, Dynamic, 3> C(Vmap.cols(), 3);

    Ensures(V.rows() > 0 && F.rows() > 0);
    Ensures(C.rows() == V.rows());

    // convert labels to colors
    for (auto i = 0; i < C.rows(); ++i) {
      auto const label = labelData_[narrow<Size>(i)];
      auto const color = labelMap(label);

      C(i, 0) = color[0];
      C(i, 1) = color[1];
      C(i, 2) = color[2];
    }

    // clang-format off
  // This assertion is to silence clang-tidy linter, somehow it does not
  // recognize that C was allocated before and complains about null pointer
  // dereferenceing when igl::writeOFF() calls C.maxCoeff().
  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay, hicpp-no-array-decay)
  Ensures(C.data() != nullptr);
    // clang-format on

    if (!igl::writeOFF(meshFilename, V, F, C)) {
      throw std::invalid_argument("Failed to write mesh to file '" +
                                  meshFilename + "'");
    }
  }
}

template <class T>
std::array<T, 3> Mesh<T>::cartesianRepresentation(
    BarycentricPoint<T, Mesh<T>::Index> const &point) const {
  using gsl::narrow_cast;
  using Internal::Adaptor::indexMap;
  using Internal::Adaptor::vertexMap;

  if ((point.triangleIndex < 0) ||
      (narrow_cast<std::size_t>(point.triangleIndex) > triangleCount())) {
    throw std::range_error("Triangle index out of range");
  }

  auto const idx = point.triangleIndex;
  auto const idxMap = indexMap(*this);
  auto const vMap = vertexMap(*this);

  std::array<T, 3> cartPoint;
  Eigen::Map<Eigen::Matrix<T, 3, 1>> cartMap{cartPoint.data(), 3, 1};

  cartMap = point.uv[0] * vMap.col(idxMap(0, idx)) +
            point.uv[1] * vMap.col(idxMap(1, idx)) +
            (1 - point.uv[0] - point.uv[1]) * vMap.col(idxMap(2, idx));

  return cartPoint;
}

namespace Internal {

template <bool IsForward> struct BarycentricPointValidatorHelper_;

template <> struct BarycentricPointValidatorHelper_<true> {

  template <class T>
  static void validate(BarycentricPoint<T, typename Mesh<T>::Index> const &,
                       Mesh<T> const &) {}

  template <class T, class Iter>
  static void validate(Iter begin, Iter end, Mesh<T> const &mesh) {
    auto const idxUB = mesh.triangleCount();
    if (std::any_of(begin, end, [idxUB](auto const &p) {
          return (p.triangleIndex < 0) ||
                 (gsl::narrow_cast<std::size_t>(p.triangleIndex) > idxUB);
        })) {
      throw std::out_of_range("At least one triangle index is out of range");
    }
  }
};

template <> struct BarycentricPointValidatorHelper_<false> {

  template <class T>
  static void validate(BarycentricPoint<T, typename Mesh<T>::Index> const &pt,
                       Mesh<T> const &mesh) {
    if ((pt.triangleIndex < 0) ||
        (gsl::narrow_cast<std::size_t>(pt.triangleIndex) >
         mesh.triangleCount())) {
      throw std::out_of_range("triangle index is out of range");
    }
  }

  template <class T, class Iter>
  static void validate(Iter, Iter, Mesh<T> const &) {}
};

template <class Iter>
using BarycentricPointValidator_ =
    BarycentricPointValidatorHelper_<std::is_base_of<
        std::forward_iterator_tag,
        typename std::iterator_traits<Iter>::iterator_category>::value>;

} // namespace Internal

template <class T>
template <class InputIterator, class OutputIterator>
void Mesh<T>::cartesianRepresentation(InputIterator begin, InputIterator end,
                                      OutputIterator out) const {

  withUnsafeVertexPointer([begin, end, out, this](auto const *vPtr) {
    this->barycentricInterpolation(begin, end, vPtr, out, 3);
  });
}

template <class T>
template <class PtIter, class AttrIter, class OutputIterator>
void Mesh<T>::barycentricInterpolation(PtIter pointsBegin, PtIter pointsEnd,
                                       AttrIter attrBegin, OutputIterator out,
                                       std::size_t attributeDimensions) const {

  // Type assertions
  using std::is_base_of;
  using std::iterator_traits;
  using PtTraits = iterator_traits<PtIter>;
  using AttrTraits = iterator_traits<AttrIter>;
  // using OutTraits = iterator_traits<OutputIterator>;
  static_assert(is_base_of<std::input_iterator_tag,
                           typename PtTraits::iterator_category>::value,
                "PtIter must be an input iterator");
  static_assert(is_base_of<std::random_access_iterator_tag,
                           typename AttrTraits::iterator_category>::value,
                "AttrIterator must be a random access iterator");

  using Validator = Internal::BarycentricPointValidator_<PtIter>;

  using namespace Internal::Adaptor;

  // Validate input. Will only result in code if PtIter conforms to
  // ForwardIterator
  Validator::validate(pointsBegin, pointsEnd, *this);

  auto const iMap = indexMap(*this);

  auto const N = gsl::narrow<Index>(attributeDimensions);

  for (auto ptI = pointsBegin; ptI != pointsEnd; ++ptI) {
    for (auto i = 0; i < N; ++i) {
      // Validate input. Will only result in code if PtIter does not conform to
      // ForwardIterator
      Validator::validate(*ptI, *this);

      std::array<Index, 3> const attrIdx{{N * iMap(0, ptI->triangleIndex) + i,
                                          N * iMap(1, ptI->triangleIndex) + i,
                                          N * iMap(2, ptI->triangleIndex) + i}};

      // Interpolate values
      *out++ = ptI->uv[0] * attrBegin[attrIdx[0]] +
               ptI->uv[1] * attrBegin[attrIdx[1]] +
               (1 - ptI->uv[0] - ptI->uv[1]) * attrBegin[attrIdx[2]];
    }
  }
}

template <class T>
RayMeshIntersection<T> Mesh<T>::rayIntersection(Ray<T> const &ray) const {
  using std::addressof;
  RayMeshIntersection<T> intersection;

  rayIntersections(addressof(ray), addressof(ray) + 1, addressof(intersection));

  return intersection;
}

template <class T>
template <class InputIterator, class OutputIterator>
void Mesh<T>::rayIntersections(InputIterator raysBegin, InputIterator raysEnd,
                               OutputIterator intersectionsOut) const {
  using namespace Internal::Adaptor;
  using Eigen::Map;
  using Eigen::Matrix;
  using gsl::narrow_cast;
  using std::abs;

  // Type validation
  using InputTraits = std::iterator_traits<InputIterator>;

  static_assert(
      std::is_convertible<typename InputTraits::value_type, Ray<T>>::value,
      "value_type of InputIterator must be convertible to Ray");

  // Get iterator categories for input and output iterator and check if both
  // are random access.
  using ITag = typename std::iterator_traits<InputIterator>::iterator_category;
  using OTag = typename std::iterator_traits<OutputIterator>::iterator_category;
  constexpr bool isRandomAccessI =
      std::is_base_of<std::random_access_iterator_tag, ITag>::value;
  constexpr bool isRandomAccessO =
      std::is_base_of<std::random_access_iterator_tag, OTag>::value;
  // If both iterator types are random access, parallel execution can be used.
  // If not use sequential code.
  using Transform = std::conditional_t<isRandomAccessI && isRandomAccessO,
                                       ParallelTransform, SequencialTransform>;

  auto const vMap = vertexMap(*this);
  auto const iMap = indexMap(*this);

  // Either transverse the data sequencially or parallel, depending on the
  // iterator types
  Transform{}(
      raysBegin, raysEnd, intersectionsOut, [&vMap, &iMap](auto const &ray) {
        // map ray elements to eigen vectors
        Map<Matrix<T, 3, 1> const> const sourceMap{ray.origin.data(), 3, 1};
        Map<Matrix<T, 3, 1> const> const dirMap{ray.direction.data(), 3, 1};
        igl::Hit hit;
        // Note: intersection.signedDistance is initialized to infinity
        RayMeshIntersection<T> intersection;

        if (igl::ray_mesh_intersect(sourceMap, dirMap, vMap.transpose(),
                                    iMap.transpose(), hit)) {
          intersection.position.triangleIndex = hit.id;
          intersection.position.uv[0] = narrow_cast<T>(hit.u);
          intersection.position.uv[1] = narrow_cast<T>(hit.v);
          intersection.signedDistance = narrow_cast<T>(hit.t);
        }

        // Search in reversed direction
        if (igl::ray_mesh_intersect(sourceMap, -dirMap, vMap.transpose(),
                                    iMap.transpose(), hit)) {
          // If we found a closer hit, update intersection
          if (abs(intersection.signedDistance) > abs(narrow_cast<T>(hit.t))) {
            intersection.position.triangleIndex = hit.id;
            intersection.position.uv[0] = narrow_cast<T>(hit.u);
            intersection.position.uv[1] = narrow_cast<T>(hit.v);
            intersection.signedDistance = narrow_cast<T>(-hit.t);
          }
        }

        return intersection;
      });
}

template <class T> Mesh<T> &Mesh<T>::upsample(std::size_t nTimes) {

  using gsl::narrow_cast;
  using VMat = Eigen::Matrix<T, Eigen::Dynamic, 3>;
  using FMat = Eigen::Matrix<int, Eigen::Dynamic, 3>;
  using LMat = Eigen::Matrix<Label, Eigen::Dynamic, 1>;

  if (nTimes == 0) return *this;

  // Copy vertces and indices into matrices so that igl can handle them
  VMat const V = Adaptor::vertexMap(*this).transpose();
  FMat const F = Adaptor::indexMap(*this).transpose().template cast<int>();
  VMat const N = Adaptor::vertexNormalMap(*this).transpose();
  LMat const L = Adaptor::labelMap(*this);

  VMat Vnew = V;
  FMat Fnew = F;
  VMat Nnew = N;
  LMat Lnew = L;

  for (auto i = 0u; i < nTimes; ++i) {
    FMat tmpF = Fnew;
    Eigen::SparseMatrix<T> S;

    igl::upsample(narrow_cast<int>(Vnew.rows()), tmpF, S, Fnew);

    // find best matching labels
    LMat tmpL = LMat(S.rows(), 1);

    Eigen::Matrix<Label, Eigen::Dynamic, 3> LL =
        Eigen::Matrix<Label, Eigen::Dynamic, 3>::Zero(S.rows(), 3);
    Eigen::Matrix<Eigen::Index, Eigen::Dynamic, 3> Lcnt =
        Eigen::Matrix<Eigen::Index, Eigen::Dynamic, 3>::Zero(S.rows(), 3);

    for (auto j = 0; j < S.outerSize(); ++j) {
      for (typename Eigen::SparseMatrix<T>::InnerIterator it(S, j); it; ++it) {
        auto const lj = Lnew(it.col());
        auto const row = it.row();

        for (auto k = 0; k < 3; ++k) {
          if (lj == LL(row, k)) {
            ++Lcnt(row, k);
            break;
          } else if (LL(row, k) == 0) {
            LL(row, k) = lj;
            Lcnt(row, k) = 1;
            break;
          }
        }
      }
    }
    // Select label with most votes
    for (auto j = 0; j < Lcnt.rows(); ++j) {
      Eigen::Matrix<Index, 3, 1> const row = Lcnt.row(j).transpose();
      auto const maxEl = std::max_element(row.data(), row.data() + row.size());
      tmpL(j) = LL(j, std::distance(row.data(), maxEl));
    }

    Lnew = tmpL;
    Vnew = (S * Vnew).eval();
    Nnew = (S * Nnew).eval();
    // normalize
    Nnew.array().colwise() /= Nnew.array().square().rowwise().sum().sqrt();
  }

  vertexData_.resize(3 * narrow_cast<std::size_t>(Vnew.rows()));
  normalData_.resize(3 * narrow_cast<std::size_t>(Vnew.rows()));
  indexData_.resize(3 * narrow_cast<std::size_t>(Fnew.rows()));
  labelData_.resize(narrow_cast<std::size_t>(Vnew.rows()), Label{0});

  // Assign copy new vertices and indices
  Adaptor::vertexMap(*this) = Vnew.transpose();
  Adaptor::indexMap(*this) = Fnew.transpose().template cast<Index>();
  Adaptor::vertexNormalMap(*this) = Nnew.transpose();
  Adaptor::labelMap(*this) = Lnew;

  return *this;
}

template <class T> void Mesh<T>::updatePerVertexNormals() {
  if (normalData_.size() != vertexData_.size()) {
    normalData_.resize(vertexData_.size());
  }

  Adaptor::vertexNormalMap(*this) =
      Internal::perVertexNormalMatrix(*this).transpose();
}

/*************************************
 * Explicit template instanciations
 */

template class Mesh<float>;
template class Mesh<double>;

template void Mesh<float>::barycentricInterpolation(
    BarycentricPoint<float, std::ptrdiff_t> const *,
    BarycentricPoint<float, std::ptrdiff_t> const *, float const *, float *,
    std::size_t) const;
template void Mesh<double>::barycentricInterpolation(
    BarycentricPoint<double, std::ptrdiff_t> const *,
    BarycentricPoint<double, std::ptrdiff_t> const *, double const *, double *,
    std::size_t) const;

template void Mesh<float>::cartesianRepresentation(
    BarycentricPoint<float, std::ptrdiff_t> const *,
    BarycentricPoint<float, std::ptrdiff_t> const *, float *) const;
template void Mesh<double>::cartesianRepresentation(
    BarycentricPoint<double, std::ptrdiff_t> const *,
    BarycentricPoint<double, std::ptrdiff_t> const *, double *) const;
template void Mesh<float>::cartesianRepresentation(
    std::vector<BarycentricPoint<float, std::ptrdiff_t>>::const_iterator,
    std::vector<BarycentricPoint<float, std::ptrdiff_t>>::const_iterator,
    float *) const;
template void Mesh<double>::cartesianRepresentation(
    std::vector<BarycentricPoint<double, std::ptrdiff_t>>::const_iterator,
    std::vector<BarycentricPoint<double, std::ptrdiff_t>>::const_iterator,
    double *) const;

template void Mesh<float>::rayIntersections(Ray<float> const *,
                                            Ray<float> const *,
                                            RayMeshIntersection<float> *) const;
template void Mesh<float>::rayIntersections(
    Ray<float> const *, Ray<float> const *,
    std::back_insert_iterator<std::vector<RayMeshIntersection<float>>>) const;
template void
Mesh<double>::rayIntersections(Ray<double> const *, Ray<double> const *,
                               RayMeshIntersection<double> *) const;
template void Mesh<double>::rayIntersections(
    Ray<double> const *, Ray<double> const *,
    std::back_insert_iterator<std::vector<RayMeshIntersection<double>>>) const;

// namespace CortidQCT
} // namespace CortidQCT


#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#pragma GCC push_options
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx")
#include<bits/stdc++.h>
#include <xmmintrin.h>
#include <immintrin.h>
using namespace::std;
__attribute__((constructor))void init(){cin.tie(0);ios::sync_with_stdio(false);cout<<fixed<<setprecision(15);}
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/priority_queue.hpp>
#include<ext/pb_ds/tag_and_trait.hpp>
// #include <boost/multiprecision/cpp_dec_float.hpp>
// #include <boost/multiprecision/cpp_int.hpp>
// namespace mp = boost::multiprecision;
// typedef mp::number<mp::cpp_dec_float<0>> cdouble;
// typedef mp::cpp_int cint;
template<typename T>using pbds=__gnu_pbds::tree<T,__gnu_pbds::null_type,less<T>,__gnu_pbds::rb_tree_tag,__gnu_pbds::tree_order_statistics_node_update>;
template<typename T>using pbds_map=__gnu_pbds::tree<T,T,less<T>,__gnu_pbds::rb_tree_tag,__gnu_pbds::tree_order_statistics_node_update>;
template<typename T,typename E>using hash_map=__gnu_pbds::gp_hash_table<T,E>;
template<typename T>using pqueue =__gnu_pbds::priority_queue<T, greater<T>,__gnu_pbds::rc_binomial_heap_tag>;
typedef long long lint;
#define INF (1LL<<60)
#define IINF (1<<30)
#define LINF (9223372036854775807LL)
#define EPS (1e-10)
#define endl ('\n')
#define MOD 1000000007LL
//#define MOD 998244353LL
//#define MOD 18446744069414584321ULL
typedef vector<lint> vec;
typedef vector<vector<lint>> mat;
typedef vector<vector<vector<lint>>> mat3;
typedef vector<string> svec;
typedef vector<vector<string>> smat;
template<typename T>inline void numout(T t){bool f=0;for(auto i:t){cout<<(f?" ":"")<<i<INF/2?i:"INF";f=1;}cout<<endl;}
template<typename T>inline void numout2(T t){for(auto i:t)numout(i);}
template<typename T>inline void output(T t){bool f=0;for(auto i:t){cout<<(f?" ":"")<<i;f=1;}cout<<endl;}
template<typename T>inline void output2(T t){for(auto i:t)output(i);}
template<typename T>inline void _output(T t){bool f=0;for(lint i=0;i<t.size();i++){cout<<f?"":" "<<t[i];f=1;}cout<<endl;}
template<typename T>inline void _output2(T t){for(lint i=0;i<t.size();i++)output(t[i]);}
#define rep(i,...) for(auto i:range(__VA_ARGS__))
#define rrep(i,...) for(auto i:reversed(range(__VA_ARGS__)))
#define repi(i,a,b) for(lint i=lint(a);i<(lint)(b);++i)
#define rrepi(i,a,b) for(lint i=lint(b)-1;i>=lint(a);--i)
#define irep(i) for(lint i=0;;++i)
inline vector<int64_t> range(int64_t n){vector<int64_t>v(n);iota(v.begin(),v.end(),0LL);return v;}
inline vector<int64_t> range(int64_t a,int64_t b){vector<int64_t>v(b-a);iota(v.begin(),v.end(),a);return v;}
inline vector<int64_t> range(int64_t a,int64_t b,int64_t c){vector<int64_t>v((b-a+c-1)/c);for(int i=0;i<(int)v.size();++i)v[i]=i?v[i-1]+c:a;return v;}
inline auto reversed(auto v){reverse(v.begin(),v.end());return v;}
#define all(n) begin(n),end(n)
#define dist(a,b,c,d) sqrt(pow(a-c,2)+pow(b-d,2))
//inline lint gcd(lint A,lint B){return B?gcd(B,A%B):A;}
//inline lint lcm(lint A,lint B){return A/gcd(A,B)*B;}
// inline cint cgcd(cint A,cint B){return B?cgcd(B,A%B):A;}
// inline cint clcm(cint A,cint B){return A/cgcd(A,B)*B;}
bool chmin(auto& s,const auto& t){bool res=s>t;s=min(s,t);return res;}
bool chmax(auto& s,const auto& t){bool res=s<t;s=max(s,t);return res;}
const vector<lint> dx={1,0,-1,0,1,1,-1,-1};
const vector<lint> dy={0,1,0,-1,1,-1,1,-1};
#define SUM(v) accumulate(all(v),0LL)
auto call=[](auto f,auto... args){return f(f,args...);};
template<typename T,typename ...Args>auto make_vector(T x,int arg,Args ...args){if constexpr(sizeof...(args)==0)return vector<T>(arg,x);else return vector(arg,make_vector<T>(x,args...));}

int main(){
	mat v(10'000'001);
	lint t;
	cin>>t;
	assert(1<=t&&t<=100'000);
	while(t--){
		lint x,y;
		cin>>x>>y;
		lint ans=0;
		assert(1<=x&&x<=10'000'000);
		assert(1<=y&&y<=10'000'000);
        if(x==1&&y==2){
            cout<<0<<endl;
            continue;
        }
		if(x>y-x)x=y-x;
		{
			for(lint i=1;i*i<=y;i++){
				if(y%i)continue;
				{
					__int128_t a=i;
					if(a%2){
						lint b=(a*a-1)*y/(2*a*x);
						if((a*a-1)*y%(2*a*x)==0&&1<=b&&b<=100'000'000&&a<b){
							ans++;
						}
					}
				}
				if(y==i*i)continue;
				{
					__int128_t a=y/i;
					if(a%2){
						lint b=(a*a-1)*y/(2*a*x);
						if((a*a-1)*y%(2*a*x)==0&&1<=b&&b<=100'000'000&&a<b){
							ans++;
						}
					}
				}
			}
			ans+=100'000'000/y;
		}
		cout<<ans<<endl;
	}
}


#include <numpy_eigen/boost_python_headers.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
#include <bsplines/BSpline.hpp>
#include <aslam/splines/BSplineDesignVariable.hpp>
#include <aslam/splines/BSplinePoseDesignVariable.hpp>
#include <aslam/splines/EuclideanBSplineDesignVariable.hpp>
#include <sstream>
#include <aslam/cameras.hpp>

using namespace bsplines;
using namespace boost::python;
using namespace aslam::splines;
using namespace aslam::backend;
using namespace aslam::cameras;

void exportBSplineMotionError();
void exportSimpleSplineError();
void exportOptBSplines();
void exportAddQuadraticIntegralExpressionErrorTerms();

template<int D>
void exportBsd()
{
    std::stringstream str;
    str << "BSpline" << D << "DesignVariable";

    class_< BSplineDesignVariable<D>, boost::shared_ptr< BSplineDesignVariable<D> > >( str.str().c_str(), init<const BSpline &>() )
        .def("spline", &BSplineDesignVariable<D>::spline, return_value_policy<copy_const_reference>())
        .def("toExpression", &BSplineDesignVariable<D>::toExpression)
        .def("numDesignVariables", &BSplineDesignVariable<D>::numDesignVariables )
        .def("designVariable", &BSplineDesignVariable<D>::designVariable, return_internal_reference<>())
        ;

}

BOOST_PYTHON_MODULE(libaslam_splines_python)
{
    exportBsd<1>();
    exportBsd<2>();
    exportBsd<3>();
    exportBsd<4>();
    exportBsd<5>();
    exportBsd<6>();
    exportBsd<7>();
    exportBsd<8>();
    exportBsd<9>();
    exportBsd<10>();

    aslam::backend::TransformationExpression (BSplinePoseDesignVariable::*transformationAtTime1)(const aslam::backend::ScalarExpression &) = &BSplinePoseDesignVariable::transformationAtTime;
    aslam::backend::TransformationExpression (BSplinePoseDesignVariable::*transformationAtTime2)(const aslam::backend::ScalarExpression &, double, double) = &BSplinePoseDesignVariable::transformationAtTime;

    class_< BSplinePoseDesignVariable, boost::shared_ptr< BSplinePoseDesignVariable > >("BSplinePoseDesignVariable", init<const BSplinePose &>() )
        .def("spline", &BSplinePoseDesignVariable::spline, return_value_policy<copy_const_reference>())
        .def("numDesignVariables", &BSplinePoseDesignVariable::numDesignVariables )
        .def("designVariable", &BSplinePoseDesignVariable::designVariable, return_internal_reference<>())
        .def("transformation", &BSplinePoseDesignVariable::transformation)
        .def("angularVelocityBodyFrame", &BSplinePoseDesignVariable::angularVelocityBodyFrame)
        .def("linearVelocity", &BSplinePoseDesignVariable::linearVelocity)
        .def("linearAcceleration", &BSplinePoseDesignVariable::linearAcceleration)
        .def("position", &BSplinePoseDesignVariable::position)
        .def("orientation", &BSplinePoseDesignVariable::orientation)
        .def("transformationAtTime", transformationAtTime1)
        .def("transformationAtTime", transformationAtTime2);
        ;

    class_<EuclideanBSplineDesignVariable, boost::shared_ptr< EuclideanBSplineDesignVariable>, bases<BSplineDesignVariable<3> > >("EuclideanBSplineDesignVariable", init<const BSpline &>())
        .def("toEuclideanExpression", &EuclideanBSplineDesignVariable::toEuclideanExpression)
        .def("toEuclidean", &EuclideanBSplineDesignVariable::toEuclidean)
        ;

    exportBSplineMotionError();
    exportSimpleSplineError();

}


/**
 * eigen.cc
 *
 * Copyright 2018. All Rights Reserved.
 *
 * Created: September 8, 2018
 * Authors: Toki Migimatsu
 */

#include <pybind11/eigen.h>
#include <pybind11/operators.h>
#include <pybind11/pybind11.h>

#include <Eigen/Eigen>
#include <memory>  // std::unique_ptr
#include <string>  // std::string, std:;to_string

namespace py = pybind11;
using namespace ::pybind11::literals;
using namespace ::Eigen;

template <int Mode>
void DeclareTransform(pybind11::module& m, const char* class_name) {
  using Transform3d = Transform<double, 3, Mode>;
  py::class_<Transform3d> c(m, class_name);
  c.def(py::init<const Transform3d&>())
      .def(py::init<Ref<const Matrix4d>>())
      .def(py::init<Ref<const Matrix3d>>())
      .def(py::init<const Quaterniond&>())
      .def(py::init<const AngleAxisd&>())
      .def_property(
          "matrix",
          (const Matrix4d& (Transform3d::*)(void) const) & Transform3d::matrix,
          [](Transform3d& T, Ref<const Matrix4d> matrix) {
            T.matrix() = matrix;
          })
      .def_property(
          "linear",
          (const Block<const Matrix4d, 3, 3> (Transform3d::*)(void) const) &
              Transform3d::linear,
          [](Transform3d& T, Ref<const Matrix3d> linear) {
            T.linear() = linear;
          })
      .def_property(
          "affine",
          (const Block<const Matrix4d, 3, 4> (Transform3d::*)(void) const) &
              Transform3d::affine,
          [](Transform3d& T, Ref<const Matrix<double, 3, 4>> affine) {
            T.affine() = affine;
          })
      .def_property(
          "translation",
          (const Block<const Matrix4d, 3, 1, true> (Transform3d::*)(void)
               const) &
              Transform3d::translation,
          [](Transform3d& T, Ref<const Matrix<double, 3, 1>> translation) {
            T.translation() = translation;
          })
      .def("__mul__", [](const Transform3d& T,
                         const Translation3d& other) { return T * other; })
      .def("__mul__", [](const Transform3d& T,
                         const Quaterniond& other) { return T * other; })
      .def("__mul__", [](const Transform3d& T,
                         const AngleAxisd& other) { return T * other; })
      .def("__mul__", [](const Transform3d& T,
                         const Isometry3d& other) { return T * other; })
      .def("__mul__", [](const Transform3d& T,
                         const Affine3d& other) { return T * other; })
      .def("__mul__", [](const Transform3d& T,
                         const Projective3d& other) { return T * other; })
      .def("__mul__", [](const Transform3d& T,
                         Ref<const Vector4d> other) { return T * other; })
      .def("__mul__", [](const Transform3d& T,
                         Ref<const Matrix4d> other) { return T * other; })
      .def("set",
           [](Transform3d& T, const Translation3d& other) { return T = other; })
      .def("set",
           [](Transform3d& T, const Quaterniond& other) { return T = other; })
      .def("set",
           [](Transform3d& T, const AngleAxisd& other) { return T = other; })
      .def("set",
           [](Transform3d& T, const Transform3d& other) { return T = other; })
      .def("set",
           [](Transform3d& T, const Matrix4d& other) { return T = other; })
      .def("set_identity", &Transform3d::setIdentity)
      .def_static("identity", &Transform3d::Identity)
      // .def("scale",
      //      (Transform3d & (Transform3d::*)(const double&)) &
      //      Transform3d::scale)
      // .def("prescale", (Transform3d & (Transform3d::*)(const double&)) &
      //                      Transform3d::prescale)
      // .def("scale", [](Transform3d& T,
      //                  Ref<const Vector3d> scale) { return T.scale(scale); })
      // .def("prescale",
      //      [](Transform3d& T, Ref<const Vector3d> prescale) {
      //        return T.prescale(prescale);
      //      })
      .def("rotate", &Transform3d::template rotate<Quaterniond>)
      .def("rotate", &Transform3d::template rotate<AngleAxisd>)
      .def("prerotate", &Transform3d::template prerotate<Quaterniond>)
      .def("prerotate", &Transform3d::template prerotate<AngleAxisd>)
      // .def("shear", &Transform3d::shear)
      // .def("preshear", &Transform3d::preshear)
      .def("rotation", &Transform3d::rotation,
           Mode == Isometry ? py::return_value_policy::reference_internal
                            : py::return_value_policy::automatic)
      .def("inverse", &Transform3d::inverse)
      .def("is_approx", &Transform3d::isApprox, py::arg(),
           "prec"_a = NumTraits<double>::dummy_precision())
      .def("make_affine", &Transform3d::makeAffine)
      .def("__repr__", [class_name](const Transform3d& T) {
        return "<eigen." + std::string(class_name) + "\n [[" +
               std::to_string(T.matrix()(0, 0)) + ", " +
               std::to_string(T.matrix()(0, 1)) + ", " +
               std::to_string(T.matrix()(0, 2)) + ", " +
               std::to_string(T.matrix()(0, 3)) + "]\n  [" +
               std::to_string(T.matrix()(1, 0)) + ", " +
               std::to_string(T.matrix()(1, 1)) + ", " +
               std::to_string(T.matrix()(1, 2)) + ", " +
               std::to_string(T.matrix()(1, 3)) + "]\n  [" +
               std::to_string(T.matrix()(2, 0)) + ", " +
               std::to_string(T.matrix()(2, 1)) + ", " +
               std::to_string(T.matrix()(2, 2)) + ", " +
               std::to_string(T.matrix()(2, 3)) + "]\n  [" +
               std::to_string(T.matrix()(3, 0)) + ", " +
               std::to_string(T.matrix()(3, 1)) + ", " +
               std::to_string(T.matrix()(3, 2)) + ", " +
               std::to_string(T.matrix()(3, 3)) + "]]>";
      });

  if constexpr (Mode != Projective) {
    c.def("__mul__", [](const Transform3d& T, Ref<const Vector3d> other) {
       return T * other;
     }).def("__mul__", [](const Transform3d& T, Ref<const Matrix3d> other) {
      return T * other;
    });
  }
}

PYBIND11_MODULE(ctrlutils_eigen, m) {
  DeclareTransform<Isometry>(m, "Isometry3d");
  DeclareTransform<Affine>(m, "Affine3d");
  DeclareTransform<Projective>(m, "Projective3d");

  // Translation3d
  py::class_<Translation3d>(m, "Translation3d")
      .def(py::init<>())
      .def(py::init<const Translation3d&>())
      .def(py::init<const double&, const double&, const double&>())
      .def(py::init<Ref<const Vector3d>>())
      .def_property("x",
                    (double(Translation3d::*)(void) const) & Translation3d::x,
                    [](Translation3d& T, double x) { T.x() = x; })
      .def_property("y",
                    (double(Translation3d::*)(void) const) & Translation3d::y,
                    [](Translation3d& T, double y) { T.y() = y; })
      .def_property("z",
                    (double(Translation3d::*)(void) const) & Translation3d::z,
                    [](Translation3d& T, double z) { T.z() = z; })
      .def_property("vector",
                    (const Vector3d& (Translation3d::*)(void) const) &
                        Translation3d::vector,
                    [](Translation3d& T, Ref<const Vector3d> vector) {
                      T.vector() = vector;
                    })
      .def_property("translation",
                    (const Vector3d& (Translation3d::*)(void) const) &
                        Translation3d::translation,
                    [](Translation3d& T, Ref<const Vector3d> translation) {
                      T.translation() = translation;
                    })
      .def("__mul__", [](const Translation3d& T,
                         const Translation3d& other) { return T * other; })
      .def("__mul__", [](const Translation3d& T,
                         const Quaterniond& other) { return T * other; })
      .def("__mul__", [](const Translation3d& T,
                         const AngleAxisd& other) { return T * other; })
      .def("__mul__", [](const Translation3d& T,
                         const Isometry3d& other) { return T * other; })
      .def("__mul__", [](const Translation3d& T,
                         const Affine3d& other) { return T * other; })
      .def("__mul__", [](const Translation3d& T,
                         const Projective3d& other) { return T * other; })
      .def("__mul__", [](const Translation3d& T,
                         const Vector3d& other) { return T * other; })
      .def("__mul__", [](const Translation3d& T,
                         Ref<const Matrix3d> other) { return T * other; })
      .def("set", [](Translation3d& T,
                     const Translation3d& other) { return T = other; })
      .def("inverse", &Translation3d::inverse)
      .def_static("identity", &Translation3d::Identity)
      .def("is_approx", &Translation3d::isApprox, py::arg(),
           "prec"_a = NumTraits<double>::dummy_precision())
      .def("__repr__", [](const Translation3d& T) {
        return "<eigen.Translation3d [" + std::to_string(T.translation()[0]) +
               ", " + std::to_string(T.translation()[1]) + ", " +
               std::to_string(T.translation()[2]) + "]>";
      });

  // Quaterniond
  py::class_<QuaternionBase<Quaterniond>,
             std::unique_ptr<QuaternionBase<Quaterniond>, py::nodelete>>(
      m, "_QuaternionBase_Quaterniond");

  py::class_<Quaterniond, QuaternionBase<Quaterniond>>(m, "Quaterniond")
      .def(py::init<>())
      .def(py::init<const AngleAxisd&>())
      .def(py::init<Ref<const Matrix3d>>())
      .def(py::init<Ref<const Vector4d>>())
      .def(py::init<const Quaterniond&>())
      .def(py::init<const double&, const double&, const double&,
                    const double&>(),
           "w"_a, "x"_a, "y"_a, "z"_a)
      .def_property(
          "x", (const double& (Quaterniond::*)(void) const) & Quaterniond::x,
          [](Quaterniond& quat, double x) { quat.x() = x; })
      .def_property(
          "y", (const double& (Quaterniond::*)(void) const) & Quaterniond::y,
          [](Quaterniond& quat, double y) { quat.y() = y; })
      .def_property(
          "z", (const double& (Quaterniond::*)(void) const) & Quaterniond::z,
          [](Quaterniond& quat, double z) { quat.z() = z; })
      .def_property(
          "w", (const double& (Quaterniond::*)(void) const) & Quaterniond::w,
          [](Quaterniond& quat, double w) { quat.w() = w; })
      .def_property(
          "vec",
          (const VectorBlock<const Vector4d, 3> (Quaterniond::*)(void) const) &
              Quaterniond::vec,
          [](Quaterniond& quat, Ref<Vector3d> vec) { quat.vec() = vec; })
      .def_property(
          "coeffs",
          (const Vector4d& (Quaterniond::*)(void) const) & Quaterniond::coeffs,
          [](Quaterniond& quat, Ref<const Vector4d> coeffs) {
            quat.coeffs() = coeffs;
          })
      .def("set", [](Quaterniond& quat,
                     const Quaterniond& other) { return quat = other; })
      .def("set", [](Quaterniond& quat,
                     const AngleAxisd& other) { return quat = other; })
      .def("set", [](Quaterniond& quat,
                     Ref<const Matrix3d> other) { return quat = other; })
      .def("set",
           [](Quaterniond& quat, Ref<Vector4d> other) { return quat = other; })
      .def_static("unit_random", &Quaterniond::UnitRandom)
      .def_static("from_two_vectors",
                  [](Ref<const Vector3d> a, Ref<const Vector3d> b) {
                    return Quaterniond::FromTwoVectors(a, b);
                  })
      .def("set_from_two_vectors",
           [](Quaterniond& quat, Ref<const Vector3d> a, Ref<const Vector3d> b) {
             return quat.setFromTwoVectors(a, b);
           })
      .def_static("identity", &Quaterniond::Identity)
      .def("set_identity", &Quaterniond::setIdentity)
      .def("squared_norm", &Quaterniond::squaredNorm)
      .def("norm", &Quaterniond::norm)
      .def("normalize", &Quaterniond::normalize)
      .def("normalized", &Quaterniond::normalized)
      .def("dot", &Quaterniond::dot<Quaterniond>)
      .def("angular_distance", &Quaterniond::angularDistance<Quaterniond>)
      .def("matrix", &Quaterniond::matrix)
      .def("__mul__", &Quaterniond::operator*<Quaterniond>)
      .def("__mul__", [](const Quaterniond& quat,
                         Ref<const Vector3d> other) { return quat * other; })
      .def("__mul__", [](const Quaterniond& quat,
                         Ref<const Matrix3d> other) { return quat * other; })
      .def("__mul__", [](const Quaterniond& quat,
                         const Translation3d& other) { return quat * other; })
      .def("__mul__", [](const Quaterniond& quat,
                         const Isometry3d& other) { return quat * other; })
      .def("__mul__", [](const Quaterniond& quat,
                         const Affine3d& other) { return quat * other; })
      .def("inverse", &Quaterniond::inverse)
      .def("conjugate", &Quaterniond::conjugate)
      .def("slerp", &Quaterniond::slerp<Quaterniond>)
      .def("is_approx", &Quaterniond::isApprox<Quaterniond>, py::arg(),
           "prec"_a = NumTraits<double>::dummy_precision())
      .def("__repr__", [](const Quaterniond& quat) {
        return "<eigen.Quaterniond (x=" + std::to_string(quat.x()) +
               ", y=" + std::to_string(quat.y()) +
               ", z=" + std::to_string(quat.z()) +
               ", w=" + std::to_string(quat.w()) + ")>";
      });

  // AngleAxisd
  py::class_<AngleAxisd>(m, "AngleAxisd")
      .def(py::init<>())
      .def(py::init<const AngleAxisd&>())
      .def(py::init<Ref<const Matrix3d>>())
      .def(py::init<const Quaterniond&>())
      .def(py::init<double, Ref<const Vector3d>>(), "angle"_a, "axis"_a)
      .def_property("angle",
                    (double(AngleAxisd::*)(void) const) & AngleAxisd::angle,
                    [](AngleAxisd& aa, double angle) { aa.angle() = angle; })
      .def_property(
          "axis",
          (const Vector3d& (AngleAxisd::*)(void) const) & AngleAxisd::axis,
          [](AngleAxisd& aa, Ref<const Vector3d> axis) { aa.axis() = axis; })
      .def("__mul__", (Quaterniond(AngleAxisd::*)(const AngleAxisd&) const) &
                          AngleAxisd::operator*)
      .def("__mul__", (Quaterniond(AngleAxisd::*)(const Quaterniond&) const) &
                          AngleAxisd::operator*)
      .def("__mul__", [](const AngleAxisd& aa,
                         Ref<const Vector3d> other) { return aa * other; })
      .def("__mul__", [](const AngleAxisd& aa,
                         Ref<const Matrix3d> other) { return aa * other; })
      .def("__mul__", [](const AngleAxisd& aa,
                         const Translation3d& other) { return aa * other; })
      .def("__mul__", [](const AngleAxisd& aa,
                         const Isometry3d& other) { return aa * other; })
      .def("__mul__", [](const AngleAxisd& aa,
                         const Affine3d& other) { return aa * other; })
      .def("set",
           [](AngleAxisd& aa, const AngleAxisd& other) { return aa = other; })
      .def("set",
           [](AngleAxisd& aa, const Quaterniond& other) { return aa = other; })
      .def("set",
           [](AngleAxisd& aa, Ref<const Matrix3d> other) { return aa = other; })
      .def("matrix", &AngleAxisd::matrix)
      .def_static("identity", &AngleAxisd::Identity)
      .def("inverse", &AngleAxisd::inverse)
      .def("is_approx", &AngleAxisd::isApprox, py::arg(),
           "prec"_a = NumTraits<double>::dummy_precision())
      .def("__repr__", [](const Eigen::AngleAxisd& aa) {
        return "<eigen.AngleAxisd (angle=" + std::to_string(aa.angle()) +
               ", axis=[" + std::to_string(aa.axis()[0]) + ", " +
               std::to_string(aa.axis()[1]) + ", " +
               std::to_string(aa.axis()[2]) + "])>";
      });

  // LDLT<Eigen::MatrixXd>
  py::class_<LDLT<MatrixXd>>(m, "LdltMatrixXd")
      .def("solve", [](const LDLT<MatrixXd>& ldlt, Ref<const MatrixXd> x) {
        return ldlt.solve(x);
      });
}


#ifndef STORM_TYPES_HPP
#define STORM_TYPES_HPP

#include <Pythia8/Pythia.h>
#include <boost/histogram.hpp>
#include <boost/histogram/axis/regular.hpp>
#include <boost/histogram/weight.hpp>

namespace storm {

using Pythia8::Vec4;

using MomentaList = std::vector<Pythia8::Vec4>;
using Spectum = std::pair<std::vector<double>, std::vector<double>>;

using SquaredMatrixElement =
    std::function<double(const std::vector<Pythia8::Vec4> &)>;

namespace bp = boost::histogram;

using LogAxis = bp::axis::regular<double, bp::axis::transform::log>;
using Histogram = bp::histogram<std::tuple<LogAxis>>;

enum Generation : size_t {
  first = 0,
  second = 1,
  third = 2,
};

} // namespace storm

#endif // STORM_TYPES_HPP


#include "opt.hh"
#include "io.hh"
#include "key.hh"
#include "random.hh"

#include <boost/numeric/odeint.hpp>

namespace boost {
  //
  namespace numeric {
    //
    namespace odeint {
      //
      template<>
      struct is_resizeable<Coord::Cartesian> {
	//
	typedef boost::true_type type;

	static const bool value = type::value;
      };
    }
  }
}

/**********************************************************************************************************
 *************************************** HARDING POTENTIAL WRAPPER ****************************************
 **********************************************************************************************************/

double Opt::HardPot::evaluate (const Cartesian& x) const
{
  const char funame [] = "Opt::HardPot::evaluate: ";

  if(atom_size() != x.atom_size()) {
    //
    std::cerr << funame << "dimensions mismatch: " << atom_size() << " vs. " << x.atom_size() << "\n";

    throw Error::Logic();
  }

  return potential(x);
}

/**********************************************************************************************************
 ***************************** CONSTRAINED OPTIMIZATION IN CARTESIAN SPACE ********************************
 **********************************************************************************************************/

int Opt::CcOpt::_grad_count;

void Opt::CcOpt::execute (Coord::Cartesian& x) const
{
  const char funame [] = "Opt::CcOpt::execute: ";

  namespace odeint = boost::numeric::odeint;

  double dtemp;
  int    itemp;

  IO::Marker fumarker(funame);

  _grad_count = 0;

  Cartesian grad(x.size());

  (*this)(x, grad);

  dtemp = vlength(grad);

  IO::log << IO::log_offset << "initial gradient projection length = " << dtemp << "\n";

  if(dtemp <= _grad_tol) {
    //
    IO::log << IO::log_offset << "gradient projection is small enough: no optimization necessary\n";

    return;
  }

  if(_constrain.size() && use_constrain) {
    //
    IO::log << IO::log_offset << "initial constrain values:";

    for(_con_t::const_iterator cit = _constrain.begin(); cit != _constrain.end(); ++cit) {
      //
      dtemp = (*cit)->evaluate(x);

      switch((*cit)->type()) {
	//
      case Coord::DISTANCE:
	//
	dtemp /= Phys_const::angstrom;

	break;

      case Coord::ANGLE:
	//
	dtemp *= 180. / M_PI;

	break;

      case Coord::DIHEDRAL:
	//
	dtemp *= 180. / M_PI;

	break;
      }

      IO::log << "   " << dtemp;
    }

    IO::log << std::endl;
  }

  try {
    //
    double time = 0.;

    while(1) {
      //
      IO::log << IO::log_offset
	      << std::setw(13) << "time"
	      << std::setw(13) << "gradient"
	      << std::setw(13) << "target"
	      << "\n";

      double tmax = time + _time_length;

      itemp = odeint::integrate(*this, x, time, tmax, _time_step);

      time = tmax;

      IO::log << IO::log_offset << "steps # = " << itemp << std::endl;
    }
  } catch(_Fin) {
    //
    (*this)(x, grad);

    dtemp = vlength(grad);

    IO::log << IO::log_offset << "final gradient projection length  = " << std::setw(13) << dtemp  << "\n";

    IO::log << IO::log_offset << "gradient calls #       = " << std::setw(13) << _grad_count << "\n";

    if(_constrain.size() && use_constrain) {
      //
      IO::log << IO::log_offset << "final constrain values:";

      for(_con_t::const_iterator cit = _constrain.begin(); cit != _constrain.end(); ++cit) {
	//
	dtemp = (*cit)->evaluate(x);

	switch((*cit)->type()) {
	  //
	case Coord::DISTANCE:
	  //
	  dtemp /= Phys_const::angstrom;

	  break;

	case Coord::ANGLE:
	  //
	  dtemp *= 180. / M_PI;

	  break;

	case Coord::DIHEDRAL:
	  //
	  dtemp *= 180. / M_PI;

	  break;
	}

	IO::log << "   " << dtemp;
      }

      IO::log << std::endl;
    }
  }
}

void Opt::CcOpt::operator() (const Cartesian& x, Cartesian& dx, double time) const
{
  const char funame [] = "Opt::CcOpt::operator(): ";

  int    itemp;
  double dtemp;

  ++_grad_count;

  if(!x.size()) {
    //
    std::cerr << funame << "zero configuration space vector dimension\n";

    throw Error::Init();
  }

  dx.resize(x.size());

  for(int i = 0; i < dx.size(); ++i)
    //
    dx[i] = -pot()->grad(x, i);

  if(use_constrain && _constrain.size()) {
    //
    // constrain subspace
    //
    std::vector<Cartesian> cspace(_constrain.size(), x);

    int cc = 0;

    for(_con_t::const_iterator cit = _constrain.begin(); cit != _constrain.end(); ++cit, ++cc) {
      //
      for(int i = 0; i < x.size(); ++i)
	//
	cspace[cc][i] = (*cit)->grad(x, i);

      dtemp = normalize(cspace[cc]);

      if(dtemp == 0.) {
	//
	std::cerr << funame << "zero constrain vector\n";

	throw Error::Range();
      }

      for(int d = 0; d < cc; ++d)
	//
	orthogonalize(cspace[cc], cspace[d]);

      if(cc) {
	//
	dtemp = normalize(cspace[cc]);

	if(dtemp < _ci_tol) {
	  //
	  std::cerr << funame << "constrains are linearly dependent: " << dtemp << " vs " << _ci_tol << "\n";

	  throw Error::Run();
	}
      }
    }

    // potential gradient component, orthogonal to constrain subspace
    //
    for(cc = 0; cc < cspace.size(); ++cc)
      //
      orthogonalize(dx, cspace[cc]);
  }

  if(time != 0.) {
    //
    dtemp = vlength(dx);

    IO::log << IO::log_offset
	    << std::setw(13) << time
	    << std::setw(13) << dtemp
	    << std::setw(13) << _grad_tol
	    << std::endl;

    if(dtemp < _grad_tol)
      //
      throw _Fin();
  }
}

void Opt::CcOpt::set (std::istream& from)
{
  const char funame [] = "Opt::CcOpt::set:";

    int    itemp;
    double dtemp;

  IO::Marker funame_marker(funame);

  KeyGroup CcOptGroup;

  Key  tol_key("GradientTolerance[a.u.]"       );
  Key  cit_key("ConstrainIndependenceTolerance");
  Key step_key("IntegrationStep"               );
  Key time_key("IntegrationTime"               );

  std::string token, comment, stemp;

  while(from >> token) {
    //
    // end of input
    //
    if(token == IO::end_key()) {
      //
      std::getline(from, comment);
      //
      break;
    }
    // gradient tolerance
    //
    else if(token == tol_key) {
      //
      if(!(from >> _grad_tol)) {
	//
	std::cerr << funame << token << ": corrupted\n";

	throw Error::Input();
      }

      if(_grad_tol <= 0.) {
	//
	std::cerr << funame << token << ": out of range: " << _grad_tol << "\n";

	throw Error::Range();
      }
    }
    // constrain (local) independence tolerance
    //
    else if(token == cit_key) {
      //
      if(!(from >> _ci_tol)) {
	//
	std::cerr << funame << token << ": corrupted\n";

	throw Error::Input();
      }

      if(_ci_tol <= 0.) {
	//
	std::cerr << funame << token << ": out of range: " << _ci_tol << "\n";

	throw Error::Range();
      }
    }
    // initial time step
    //
    else if(token == step_key) {
      //
      if(!(from >> _time_step)) {
	//
	std::cerr << funame << token << ": corrupted\n";

	throw Error::Input();
      }

      if(_time_step <= 0.) {
	//
	std::cerr << funame << token << ": out of range: " << _time_step << "\n";

	throw Error::Range();
      }
    }
    // integration time
    //
    else if(token == time_key) {
      //
      if(!(from >> _time_length)) {
	//
	std::cerr << funame << token << ": corrupted\n";

	throw Error::Input();
      }

      if(_time_length <= 0.) {
	//
	std::cerr << funame << token << ": out of range: " << _time_length << "\n";

	throw Error::Range();
      }
    }
    // unknown keyword
    //
    else if(IO::skip_comment(token, from)) {
      //
      std::cerr << funame << "unknown keyword " << token << "\n";

      Key::show_all(std::cerr);

      std::cerr << "\n";

      throw Error::Init();
    }
  }

  if(!from) {
    //
    std::cerr << funame << "corrupted\n";

    throw Error::Input();
  }
}

/********************************************************************************************************
 *********************** OPTIMIZATION AND IMPORTANCE SAMPLING IN Z-MATRIX COORDINATES********************
 ********************************************************************************************************/

// gradient tolerance
//
double Opt::ZOpt::grad_tol = 1.e-5;

// maximal optimization step
//
double Opt::ZOpt::max_opt_step = 0.1;

// maximal number of optimization iterations
//
int Opt::ZOpt::max_opt_count = 100;

// differentiation step
//
double Opt::ZOpt::diff_step = .001;

// potential
//
ConstSharedPointer<Coord::CartFun> Opt::ZOpt::pot;

double Opt::ZOpt::_con_pot (const std::vector<double>& cpos) const
{
  const char funame [] = "Opt::ZOpt::_con_pot: ";

  int    itemp;
  double dtemp;

  if(!pot) {
    //
    std::cerr << funame << "potential not initialized\n";

    throw Error::Init();
  }

  if(cpos.size() != _con_modes.size()) {
    //
    std::cerr << funame << "number of conserved modes mismatch: " << cpos.size() << " vs " << _con_modes.size() << "\n";

    throw Error::Logic();
  }

  ZData zpos = _zmin;

  itemp = 0;

  for(mode_t::const_iterator cit = _con_modes.begin(); cit != _con_modes.end(); ++cit, ++itemp)
    //
    zpos[cit->first] = cpos[itemp];

  return pot->evaluate((Cartesian)zpos);
}

Lapack::Vector Opt::ZOpt::_con_grad () const
{
  int    itemp;
  double dtemp;

  std::vector<double> cpos(_con_modes.size());

  itemp = 0;

  for(mode_t::const_iterator cit = _con_modes.begin(); cit != _con_modes.end(); ++cit, ++itemp)
    //
    cpos[itemp] = _zmin[cit->first];

  Lapack::Vector res(_con_modes.size());

  for(int i  = 0; i < _con_modes.size(); ++i) {
    //
    cpos[i] += diff_step;

    res[i] = _con_pot(cpos);

    cpos[i] -= 2. * diff_step;

    res[i] -= _con_pot(cpos);

    cpos[i] += diff_step;

    res[i] /= 2. * diff_step;
  }

  return res;
}

Lapack::SymmetricMatrix Opt::ZOpt::_con_hess () const
{
  int    itemp;
  double dtemp;

  std::vector<double> cpos(_con_modes.size());

  itemp = 0;

  for(mode_t::const_iterator cit = _con_modes.begin(); cit != _con_modes.end(); ++cit, ++itemp)
    //
    cpos[itemp] = _zmin[cit->first];

  const double e0 = _con_pot(cpos);

  Lapack::SymmetricMatrix res(_con_modes.size());

  for(int i  = 0; i < _con_modes.size(); ++i)
    //
    for(int j  = i; j < _con_modes.size(); ++j)
      //
      if(i != j) {
	//
	cpos[i] += diff_step;

	cpos[j] += diff_step;

	res(i, j) = _con_pot(cpos);

	cpos[i] -= 2. * diff_step;

	res(i, j) -= _con_pot(cpos);

	cpos[j] -= 2. * diff_step;

	res(i, j) += _con_pot(cpos);

	cpos[i] += 2. * diff_step;

	res(i, j) -= _con_pot(cpos);

	cpos[i] -= diff_step;

	cpos[j] += diff_step;

	res(i, j) /= 4. * diff_step * diff_step;
      }
      else {
	//
	cpos[i] += diff_step;

	res(i, i) = _con_pot(cpos) - 2. * e0;

	cpos[i] -= 2. * diff_step;

	res(i, i) += _con_pot(cpos);

	cpos[i] += diff_step;

	res(i, i) /= diff_step * diff_step;
      }

  return res;
}

Opt::ZOpt::ZOpt (const ZData& zinit, const mode_t& cm) : _zmin(zinit), _con_modes(cm)
{
  const char funame [] = "Opt::ZOpt::ZOpt: ";

  IO::Marker fumark(funame);

  double dtemp;
  int    itemp;

  int count = 0;

  IO::log << IO::log_offset << std::setw(5) << "#" << std::setw(13) << "Grad[au]" << std::setw(13) << "Target" << std::endl;

  while(count++ < max_opt_count) {
    //
    _fc_eval_sqrt = _con_hess().eigenvalues(&_fc_evec);

    Lapack::Vector dx = _con_grad();

    dtemp = vlength(dx);

    IO::log << IO::log_offset << std::setw(5) << count << std::setw(13) << dtemp << std::setw(13) << grad_tol << std::endl;

    if(dtemp < grad_tol)
      //
      break;

    dx = dx * _fc_evec;

    for(int i = 0; i < dx.size(); ++i) {
      //
      if(_fc_eval_sqrt[i] <= 0.) {
	//
	IO::log << IO::log_offset << i + 1 << "-th force constant eigenvalue is negative" << std::endl;

	if(dx[i] < 0.) {
	  //
	  dx[i] = -max_opt_step;
	}
	else if(dx[i] > 0.) {
	  //
	  dx[i] = max_opt_step;
	}
      }
      else {
	//
	dtemp = dx[i] / _fc_eval_sqrt[i];

	if(dtemp < -max_opt_step) {
	  //
	  dx[i] = -max_opt_step;
	}
	else if(dtemp > max_opt_step) {
	  //
	  dx[i] = max_opt_step;
	}
	else
	  //
	  dx[i] = dtemp;
      }
    }

    dx = _fc_evec * dx;

    itemp = 0;

    for(mode_t::const_iterator cit = _con_modes.begin(); cit != _con_modes.end(); ++cit, ++itemp) {
      //
      dtemp = _zmin[cit->first] - dx[itemp];

      if(dtemp < cit->second.first) {
	//
	IO::log << IO::log_offset << cit->first << " proposed value is below lower limit, " << dtemp << ": clip it" << std::endl;

	_zmin[cit->first] = cit->second.first;
      }
      else if(dtemp > cit->second.second) {
	//
	IO::log << IO::log_offset << cit->first << " proposed value is above upper limit, " << dtemp << ": clip it" << std::endl;

	_zmin[cit->first] = cit->second.second;
      }
      else
	//
	_zmin[cit->first] = dtemp;
    }
  }

  if(count > max_opt_count) {
    //
    IO::log << IO::log_offset << "maximal number of iterations reached: no convergence" << std::endl;

    std::cerr << funame << "maximal number of iterations reached: no convergence\n";

    throw NoConv();
  }

  for(int i = 0; i < _con_modes.size(); ++i) {
    //
    dtemp = _fc_eval_sqrt[i];

    if(dtemp <= 0.) {
      //
      std::cerr << funame << "force constant matrix at minimum is not positively defined\n";

      throw Error::Run();
    }

    _fc_eval_sqrt[i] = std::sqrt(dtemp);
  }

  _mass_factor = std::sqrt(product(_zmin.inertia_moments())) / Lapack::Cholesky(_zmin.mobility_matrix()).det_sqrt();

  _ener_min = pot->evaluate((Cartesian)_zmin);
}

double Opt::ZOpt::anharmonic_correction (double temperature, int count_max, double* rerr, int* fail) const
{
  const char funame [] = "Opt::ZOpt::anharmonic_correction: ";

  static const double exp_pow_max = 100.;

  double dtemp;
  int    itemp;
  bool   btemp;

  const double tsqrt = std::sqrt(temperature);

  Lapack::Vector vtemp(_con_modes.size());

  double res = 0., var =  0.;

  int skip = 0;

  // sampling cycle
  //
  for(int count = 0; count < count_max; ++count) {
    //
    double eref = 0.;

    for(int i = 0; i < _con_modes.size(); ++i) {
      //
      dtemp = Random::norm();

      eref += dtemp * dtemp;

      vtemp[i] = dtemp / _fc_eval_sqrt[i] * tsqrt;
    }

    eref *= temperature / 2.;

    vtemp = _fc_evec * vtemp;

    ZData ztemp = _zmin;

    itemp = 0;

    btemp = false;

    for(mode_t::const_iterator cit = _con_modes.begin(); cit != _con_modes.end(); ++cit, ++itemp) {
      //
      dtemp = ztemp[cit->first] + vtemp[itemp];

      // check that the non-fluxional coordinate is in the allowed window
      //
      if(dtemp < cit->second.first || dtemp > cit->second.second) {
	//
	switch(ztemp.type(cit->first)) {
	  //
	case DISTANCE:
	  //
	  dtemp /= Phys_const::angstrom;

	  break;

	default:
	  //
	  dtemp *= 180. / M_PI;
	}

	IO::log << funame << "WARNING: T = " << temperature / Phys_const::kelv
		<< "K, z-matrix variable " << cit->first << " out of limits: " << dtemp << "\n";

	++skip;

	btemp = true;

	break;
      }

      ztemp[cit->first] = dtemp;
    }

    if(btemp)
      //
      continue;

    dtemp = (pot->evaluate((Cartesian)ztemp) - eref - _ener_min) / temperature;

    if(dtemp > exp_pow_max)
      //
      continue;

    if(dtemp < -exp_pow_max) {
      //
      IO::log << funame << "WARNING: anharmonic correction too negative at T = " << temperature / Phys_const::kelv << "K: "
	      << dtemp * temperature / Phys_const::kcal << " kcal/mol: skipping the point" << std::endl;

      ++skip;

      continue;
    }

    dtemp = std::exp(-dtemp) / Lapack::Cholesky(ztemp.mobility_matrix()).det_sqrt()
      * std::sqrt(product(ztemp.inertia_moments())) / mass_factor();

    res += dtemp;

    var += dtemp * dtemp;
    //
  }// sampling cycle
  //

  res /= (double)count_max;

  var /= (double)count_max;

  // relative error
  //
  if(rerr)
    //
    *rerr = std::sqrt(var - res * res) / res / std::sqrt((double)count_max);

  // failed points #
  //
  if(fail)
    //
    *fail = skip;

  return res;
}


//==============================================================================
//         Copyright 2003 - 2011 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef BOOST_SIMD_TOOLBOX_IEEE_FUNCTIONS_SIMD_COMMON_EPS_HPP_INCLUDED
#define BOOST_SIMD_TOOLBOX_IEEE_FUNCTIONS_SIMD_COMMON_EPS_HPP_INCLUDED
#include <boost/simd/toolbox/ieee/functions/eps.hpp>
#include <boost/simd/include/constants/one.hpp>
#include <boost/dispatch/meta/as_integer.hpp>
#include <boost/simd/include/constants/smallestposval.hpp>
#include <boost/simd/include/constants/mindenormal.hpp>
#include <boost/simd/include/constants/nbmantissabits.hpp>
#include <boost/simd/include/constants/nan.hpp>
#include <boost/simd/include/functions/simd/ldexp.hpp>
#include <boost/simd/include/functions/simd/seladd.hpp>
#include <boost/simd/include/functions/simd/if_else.hpp>
#include <boost/simd/include/functions/simd/is_less.hpp>
#include <boost/simd/include/functions/simd/is_invalid.hpp>
#include <boost/simd/include/functions/simd/exponent.hpp>
#include <boost/simd/include/functions/simd/abs.hpp>

namespace boost { namespace simd { namespace ext
{
  BOOST_SIMD_FUNCTOR_IMPLEMENTATION( boost::simd::tag::eps_, tag::cpu_
                            , (A0)(X)
                            , ((simd_<arithmetic_<A0>,X>))
                            )
  {
    typedef A0 result_type;
    inline result_type operator()(const A0&)const
      {
        return One<A0>();
      }
  };

  BOOST_SIMD_FUNCTOR_IMPLEMENTATION( boost::simd::tag::eps_, tag::cpu_
                            , (A0)(X)
                            , ((simd_<floating_<A0>,X>))
                            )
  {
    typedef A0 result_type;
    BOOST_SIMD_FUNCTOR_CALL(1)
    {
      typedef typename dispatch::meta::as_integer<A0, signed>::type        int_type;
      const A0 a = boost::simd::abs(a0);
      return seladd(is_invalid(a),
                select(boost::simd::is_less(a, Smallestposval<A0>()),
                     Mindenormal<A0>(),
                     ldexp(One<A0>(), exponent(a)-Nbmantissabits<A0>())
                     ),
                Nan<A0>());
      }
  };
} } }

#endif


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

  @copyright 2016 NumScale SAS
  @copyright 2016 J.T. Lapreste

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

#include <boost/simd/function/scalar/fnms.hpp>
#include <boost/simd/arch/common/generic/function/autodispatcher.hpp>
#include <boost/simd/arch/common/generic/function/fnms.hpp>

#if defined(BOOST_HW_SIMD_X86)

#  if BOOST_HW_SIMD_X86 >= BOOST_HW_SIMD_X86_FMA3_VERSION
// #    include <boost/simd/arch/x86/fma3/simd/function/fnms.hpp>
#  endif
#  if BOOST_HW_SIMD_X86 >= BOOST_HW_SIMD_X86_FMA4_VERSION
// #    include <boost/simd/arch/x86/fma4/simd/function/fnms.hpp>
#  endif

#endif

#if defined(BOOST_HW_SIMD_PPC)

#  if BOOST_HW_SIMD_PPC >= BOOST_HW_SIMD_PPC_VMX_VERSION
// #    include <boost/simd/arch/power/vmx/simd/function/fnms.hpp>
#  endif

#endif

#endif


#include <fstream>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <fmt/format.h>
#include <string>
#include <iomanip>
#include <boost/variant.hpp>
#include <unordered_map>
#include <range/v3/all.hpp>
#include "combinations_algo.h"

struct Item
{
  int cost_ = 0;
  int damage_ = 0;
  int armor_ = 0;
  Item &operator+=(const Item &other)
  {
    cost_ += other.cost_;
    damage_ += other.damage_;
    armor_ += other.armor_;
    return *this;
  }
  friend Item operator+(const Item &l, const Item &r)
  {
    Item tmp(l);

    return tmp += r;
  }
};
struct Role
{
private:
  int beheated(const Role &other) const
  {
    int diff = 1;
    if (other.damage_ > armor_) {
      diff = other.damage_ - armor_;
    }
    return diff;
  }

public:
  int points_ = 100;
  int damage_ = 0;
  int armor_ = 0;
  int rounds(const Role &other) const
  {
    return points_ / beheated(other);
  }
  auto operator<=>(const Role &other) const
  {
    return rounds(other) <=> other.rounds(*this);
  }
  void assign(const Item &other)
  {
    damage_ = other.damage_;
    armor_ = other.armor_;
  }
};

struct Game
{
  Role boss_{ 100, 8, 2 };
  Role player_{ 100 };

  std::array<Item, 5> weapons_{
    Item{ 8, 4 },
    Item{ 10, 5 },
    Item{ 25, 6 },
    Item{ 40, 7 },
    Item{ 74, 8 }
  };
  std::array<Item, 6> armor_{
    Item{},
    Item{ 13, 0, 1 },
    Item{ 31, 0, 2 },
    Item{ 53, 0, 3 },
    Item{ 75, 0, 4 },
    Item{ 102, 0, 5 }
  };
  int minCost() const
  {
    std::array<Item, 6> rings_{
      Item{ 25, 1 },
      Item{ 50, 2 },
      Item{ 100, 3 },
      Item{ 20, 0, 1 },
      Item{ 40, 0, 2 },
      Item{ 80, 0, 3 }
    };

    int maxcost = std::numeric_limits<int>::max();

    for (auto w : weapons_) {
      for (auto a : armor_) {
        Role newplayer = this->player_;
        Item sum = w + a;
        newplayer.assign(sum);
        if (boss_ < newplayer) {
          maxcost = std::min(sum.cost_, maxcost);
        }

        for (auto mid = rings_.begin(); mid != rings_.begin() + 3; ++mid) {
          auto f = [this, w, a, &maxcost](auto b, auto e) {
            Role newplayer = this->player_;
            Item sum = w + std::accumulate(b, e, a);
            newplayer.assign(sum);
            if (boss_ <= newplayer) {
              maxcost = std::min(sum.cost_, maxcost);
            }
            return false;
          };
          for_each_combination(rings_.begin(), mid, rings_.end(), f);
        }
      }
    }
    return maxcost;
  }
};

int main(int argc, char **argv)
{
  Game g;
  fmt::print("minCost:{}\n", g.minCost());
}


#include <NTL/ZZ_p.h>
#include <NTL/FFT.h>

#include <NTL/new.h>

NTL_START_IMPL

ZZ_pInfoT::ZZ_pInfoT(const ZZ& NewP)
{
   if (NewP <= 1) Error("ZZ_pContext: p must be > 1");

   ref_count = 1;
   p = NewP;
   size = p.size();

   ExtendedModulusSize = 2*size +
                 (NTL_BITS_PER_LONG + NTL_ZZ_NBITS - 1)/NTL_ZZ_NBITS;

   initialized = 0;
   x = 0;
   u = 0;
   tbl = 0;
   tbl1 = 0;

   long i;
   for (i = 0; i < MAX_ZZ_p_TEMPS; i++)
      temps[i] = 0;

   temps_top = 0;
}

void ZZ_pInfoT::init()
{
   ZZ B, M, M1, M2, M3;
   long n, i;
   long q, t;

   initialized = 1;

   sqr(B, p);

   LeftShift(B, B, NTL_FFTMaxRoot+NTL_FFTFudge);

   set(M);
   n = 0;
   while (M <= B) {
      UseFFTPrime(n);
      q = FFTPrime[n];
      n++;
      mul(M, M, q);
   }

   NumPrimes = n;
   MaxRoot = CalcMaxRoot(q);

   double fn = double(n);

   if (8.0*fn*(fn+32) > NTL_FDOUBLE_PRECISION)
      Error("modulus too big");

   if (8.0*fn*(fn+32) > NTL_FDOUBLE_PRECISION/double(NTL_SP_BOUND))
      QuickCRT = 0;
   else
      QuickCRT = 1;

   if (!(x = (double *) NTL_MALLOC(n, sizeof(double), 0)))
      Error("out of space");

   if (!(u = (long *) NTL_MALLOC(n,  sizeof(long), 0)))
      Error("out of space");

   ZZ_p_rem_struct_init(&rem_struct, n, p, FFTPrime);

   ZZ_p_crt_struct_init(&crt_struct, n, p, FFTPrime);

   if (ZZ_p_crt_struct_special(crt_struct)) return;

   ZZ qq, rr;

   DivRem(qq, rr, M, p);

   NegateMod(MinusMModP, rr, p);

   for (i = 0; i < n; i++) {
      q = FFTPrime[i];

      long tt = rem(qq, q);

      mul(M2, p, tt);
      add(M2, M2, rr);
      div(M2, M2, q);  // = (M/q) rem p

      div(M1, M, q);
      t = rem(M1, q);
      t = InvMod(t, q);

      mul(M3, M2, t);
      rem(M3, M3, p);

      ZZ_p_crt_struct_insert(crt_struct, i, M3);

      x[i] = ((double) t)/((double) q);
      u[i] = t;
   }
}

ZZ_pInfoT::~ZZ_pInfoT()
{
   long i;

   for (i = 0; i < MAX_ZZ_p_TEMPS; i++)
      if (temps[i]) delete temps[i];

   if (initialized) {
      ZZ_p_rem_struct_free(rem_struct);
      ZZ_p_crt_struct_free(crt_struct);

      free(x);
      free(u);
   }
}

ZZ_pInfoT *ZZ_pInfo = 0;

typedef ZZ_pInfoT *ZZ_pInfoPtr;

static
void CopyPointer(ZZ_pInfoPtr& dst, ZZ_pInfoPtr src)
{
   if (src == dst) return;

   if (dst) {
      dst->ref_count--;

      if (dst->ref_count < 0)
         Error("internal error: negative ZZ_pContext ref_count");

      if (dst->ref_count == 0) delete dst;
   }

   if (src) {
      if (src->ref_count == NTL_MAX_LONG)
         Error("internal error: ZZ_pContext ref_count overflow");

      src->ref_count++;
   }

   dst = src;
}

void ZZ_p::init(const ZZ& p)
{
   ZZ_pContext c(p);
   c.restore();
}

ZZ_pContext::ZZ_pContext(const ZZ& p)
{
   ptr = NTL_NEW_OP ZZ_pInfoT(p);
}

ZZ_pContext::ZZ_pContext(const ZZ_pContext& a)
{
   ptr = 0;
   CopyPointer(ptr, a.ptr);
}

ZZ_pContext& ZZ_pContext::operator=(const ZZ_pContext& a)
{
   CopyPointer(ptr, a.ptr);
   return *this;
}

ZZ_pContext::~ZZ_pContext()
{
   CopyPointer(ptr, 0);
}

void ZZ_pContext::save()
{
   CopyPointer(ptr, ZZ_pInfo);
}

void ZZ_pContext::restore() const
{
   CopyPointer(ZZ_pInfo, ptr);
}

ZZ_pBak::~ZZ_pBak()
{
   if (MustRestore)
      CopyPointer(ZZ_pInfo, ptr);

   CopyPointer(ptr, 0);
}

void ZZ_pBak::save()
{
   MustRestore = 1;
   CopyPointer(ptr, ZZ_pInfo);
}

void ZZ_pBak::restore()
{
   MustRestore = 0;
   CopyPointer(ZZ_pInfo, ptr);
}

ZZ_pTemp::ZZ_pTemp()
{
   if (ZZ_pInfo->temps_top == MAX_ZZ_p_TEMPS)
      Error("ZZ_p temporary: out of temps");

   pos = ZZ_pInfo->temps_top;
   ZZ_pInfo->temps_top++;
}

ZZ_pTemp::~ZZ_pTemp()
{
   ZZ_pInfo->temps_top--;
}

ZZ_p& ZZ_pTemp::val() const
{
   if (!ZZ_pInfo->temps[pos])
      ZZ_pInfo->temps[pos] = NTL_NEW_OP ZZ_p;

   return *(ZZ_pInfo->temps[pos]);
}

const ZZ_p& ZZ_p::zero()
{
   static ZZ_p z(ZZ_p_NoAlloc);
   return z;
}

ZZ_p::DivHandlerPtr ZZ_p::DivHandler = 0;

ZZ_p::ZZ_p()
{
   _ZZ_p__rep.SetSize(ModulusSize());
}

ZZ_p::ZZ_p(INIT_VAL_TYPE, const ZZ& a)
{
   _ZZ_p__rep.SetSize(ModulusSize());
   conv(*this, a);
}

ZZ_p::ZZ_p(INIT_VAL_TYPE, long a)
{
   _ZZ_p__rep.SetSize(ModulusSize());
   conv(*this, a);
}

void conv(ZZ_p& x, long a)
{
   if (a == 0)
      clear(x);
   else if (a == 1)
      set(x);
   else {
      static ZZ y;

      conv(y, a);
      conv(x, y);
   }
}

istream& operator>>(istream& s, ZZ_p& x)
{
   static ZZ y;

   s >> y;
   conv(x, y);

   return s;
}

void div(ZZ_p& x, const ZZ_p& a, const ZZ_p& b)
{
   ZZ_pTemp TT; ZZ_p& T = TT.val();

   inv(T, b);
   mul(x, a, T);
}

void inv(ZZ_p& x, const ZZ_p& a)
{
   if (InvModStatus(x._ZZ_p__rep, a._ZZ_p__rep, ZZ_p::modulus())) {
      if (IsZero(a._ZZ_p__rep))
         Error("ZZ_p: division by zero");
      else if (ZZ_p::DivHandler)
         (*ZZ_p::DivHandler)(a);
      else
         Error("ZZ_p: division by non-invertible element");
   }
}

long operator==(const ZZ_p& a, long b)
{
   if (b == 0)
      return IsZero(a);

   if (b == 1)
      return IsOne(a);

   ZZ_pTemp TT; ZZ_p& T = TT.val();
   conv(T, b);
   return a == T;
}

void add(ZZ_p& x, const ZZ_p& a, long b)
{
   ZZ_pTemp TT; ZZ_p& T = TT.val();
   conv(T, b);
   add(x, a, T);
}

void sub(ZZ_p& x, const ZZ_p& a, long b)
{
   ZZ_pTemp TT; ZZ_p& T = TT.val();
   conv(T, b);
   sub(x, a, T);
}

void sub(ZZ_p& x, long a, const ZZ_p& b)
{
   ZZ_pTemp TT; ZZ_p& T = TT.val();
   conv(T, a);
   sub(x, T, b);
}

void mul(ZZ_p& x, const ZZ_p& a, long b)
{
   ZZ_pTemp TT; ZZ_p& T = TT.val();
   conv(T, b);
   mul(x, a, T);
}

void div(ZZ_p& x, const ZZ_p& a, long b)
{
   ZZ_pTemp TT; ZZ_p& T = TT.val();
   conv(T, b);
   div(x, a, T);
}

void div(ZZ_p& x, long a, const ZZ_p& b)
{
   if (a == 1) {
      inv(x, b);
   }
   else {
      ZZ_pTemp TT; ZZ_p& T = TT.val();
      conv(T, a);
      div(x, T, b);
   }
}

NTL_END_IMPL


#include "NumberParserImpl.hpp"
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
//#include <boost/spirit/home/phoenix/container.hpp>
//#include <boost/spirit/home/phoenix/statement/sequence.hpp>
#include <climits>
#include "../../parsing/EmptyNode.hpp"
#include "../../parsing/NumberTerminal.hpp"
#include "string_traits.hpp"

namespace bs = boost::spirit;
namespace asli = axis::services::language::iterators;
namespace aslp = axis::services::language::parsing;
namespace aslppi = axis::services::language::primitives::impl;

aslppi::NumberParserImpl::NumberParserImpl( void )
{
	InitGrammar();
}

void aslppi::NumberParserImpl::InitGrammar( void )
{
	_signal =
    bs::qi::lexeme[_T("+")][bs::qi::_val = _T("+")] | bs::qi::lexeme[_T("-")][bs::qi::_val = _T("-")];
	_digit_sequence %= +bs::qi::digit;

	_integer = (_signal >> _digit_sequence)[bs::qi::_val = bs::qi::_1 + bs::qi::_2] |
			   (_digit_sequence)[bs::qi::_val = bs::qi::_1];
	_decimal_part =
    (_digit_sequence >> bs::qi::lexeme[_T(".")] >> _digit_sequence)[bs::qi::_val = bs::qi::_1 + _T(".") + bs::qi::_2] |
		(_digit_sequence >> bs::qi::lexeme[_T(".")])[bs::qi::_val = bs::qi::_1 + _T(".")] |
		(_digit_sequence)[bs::qi::_val = bs::qi::_1] |
		(bs::qi::lexeme[_T(".")] >> _digit_sequence)[bs::qi::_val = String(_T(".")) + bs::qi::_1];
 	_scientific_not = (bs::qi::lexeme[_T("E")] >> _integer)[bs::qi::_val = String(_T("E")) + bs::qi::_1] |
 		(bs::qi::lexeme[_T("e")] >> _integer)[bs::qi::_val =  String(_T("e")) + bs::qi::_1];
	_num_rule =
    (_signal >> _decimal_part >> _scientific_not)[bs::qi::_val = bs::qi::_1 + bs::qi::_2 + bs::qi::_3] |
		(_signal >> _decimal_part)[bs::qi::_val = bs::qi::_1 + bs::qi::_2] |
		(_decimal_part >> _scientific_not)[bs::qi::_val = bs::qi::_1 + bs::qi::_2] |
		_decimal_part[bs::qi::_val = bs::qi::_1];
}

aslppi::NumberParserImpl::~NumberParserImpl( void )
{
	/* nothing to do here */
}

aslp::ParseResult aslppi::NumberParserImpl::DoParse( const asli::InputIterator& begin,
                                                     const asli::InputIterator& end,
                                                     bool trimSpaces /*= true*/ ) const
{
	bool isInteger;		// flag indicating that we have an integer number
	size_t intNumSize = 0, doubleNumSize = 0;	// auxiliary variables for measurement purposes

	/* Parse result data */
	aslp::ParseTreeNode *rootNode;
	asli::InputIterator it;
	aslp::ParseResult::Result result;
	String value;
	bool ret;

	// check which parse rule better fits in this input stream
	it = begin;
	if(trimSpaces) bs::qi::parse(it, end, *bs::qi::blank); // try to remove whitespaces before, if asked
	ret = bs::qi::parse(it, end, _integer, value);	/* parse as int */
	if (ret) intNumSize = value.size();

	it = begin;
	if(trimSpaces) bs::qi::parse(it, end, *bs::qi::blank); // try to remove whitespaces before, if asked
	ret = bs::qi::parse(it, end, _num_rule, value);	/* parse as double */
	if (ret) doubleNumSize = value.size();

	if (ret)
	{	// since we have a number, parse its value from the string
		isInteger = (intNumSize >= doubleNumSize);	// actually it cannot be greater than, but anyways...

		// remove whitespaces at the end, if asked
		if(trimSpaces) bs::qi::parse(it, end, *bs::qi::blank);

		axis::String::iterator testIt;
		if (isInteger)
		{	// parse as an integer
			long intVal;
			if(bs::qi::parse(testIt = value.begin(), value.end(), bs::qi::long_, intVal) &&
        testIt == value.end())
			{
				rootNode = new aslp::NumberTerminal(intVal, value);
				result = aslp::ParseResult::MatchOk;
			}
			else
			{	// indeed it is an integer, but even so we couldn't parse; this mean we detected an overflow!
				result = aslp::ParseResult::FailedMatch;
				it = begin;		// move iterator to the beginning
				rootNode = new aslp::EmptyNode();
			}
		}
		else
		{	// parse as a double
			double doubleVal;
			if (bs::qi::parse(testIt = value.begin(), value.end(), bs::qi::double_, doubleVal)
        && testIt == value.end() && /* must parse successfully and ...*/
				doubleVal != std::numeric_limits<double>::infinity() &&	 /* should not be equal to positive infinity and ...*/
				doubleVal != -std::numeric_limits<double>::infinity() && /* neither negative infinity ... */
				doubleVal != std::numeric_limits<double>::quiet_NaN()		 /* nor a NaN (not a number) */
				)
			{
				rootNode = new aslp::NumberTerminal(doubleVal, value);
				result = aslp::ParseResult::MatchOk;
			}
			else
			{	// indeed it is a double, but even so we couldn't parse (or one of the other conditions was
				// not met -- see above); this mean we detected an overflow/underflow!
				result = aslp::ParseResult::FailedMatch;
				it = begin;		// move iterator to the beginning
				rootNode = new aslp::EmptyNode();
			}
		}
	}
	else
	{	// this is not a number!
		rootNode = new aslp::EmptyNode();
		result = aslp::ParseResult::FailedMatch;
	}
	return aslp::ParseResult(result, *rootNode, it);
}


// Copyright (C) 2008-2018 Lorenzo Caminiti
// Distributed under the Boost Software License, Version 1.0 (see accompanying
// file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt).
// See: http://www.boost.org/doc/libs/release/libs/contract/doc/html/index.html

#include <boost/contract.hpp>
#include <limits>
#include <cassert>

//[private_protected
class counter {
protected: // Protected functions use `function()` (like non-members).
    void set(int n) {
        boost::contract::check c = boost::contract::function()
            .precondition([&] {
                BOOST_CONTRACT_ASSERT(n <= 0);
            })
            .postcondition([&] {
                BOOST_CONTRACT_ASSERT(get() == n);
            })
        ;

        n_ = n;
    }

private: // Private functions use `function()` (like non-members).
    void dec() {
        boost::contract::old_ptr<int> old_get = BOOST_CONTRACT_OLDOF(get());
        boost::contract::check c = boost::contract::function()
            .precondition([&] {
                BOOST_CONTRACT_ASSERT(
                        get() + 1 >= std::numeric_limits<int>::min());
            })
            .postcondition([&] {
                BOOST_CONTRACT_ASSERT(get() == *old_get - 1);
            })
        ;

        set(get() - 1);
    }

    int n_;

    /* ... */
//]

public:
    int get() const {
        int result;
        boost::contract::check c = boost::contract::public_function(this)
            .postcondition([&] {
                BOOST_CONTRACT_ASSERT(result <= 0);
                BOOST_CONTRACT_ASSERT(result == n_);
            })
        ;

        return result = n_;
    }

    counter() : n_(0) {} // Should contract constructor and destructor too.

    void invariant() const {
        BOOST_CONTRACT_ASSERT(get() <= 0);
    }

    friend int main();
};

int main() {
    counter cnt;
    assert(cnt.get() == 0);
    cnt.dec();
    assert(cnt.get() == -1);
    return 0;
}


///////////////////////////////////////////////////////////////////////////////
// statistics::model::random::include.hpp                                    //
//                                                                           //
//  Copyright 2009 Erwann Rogard. Distributed under the Boost                //
//  Software License, Version 1.0. (See accompanying file                    //
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)         //
///////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_MODEL_RANDOM_INCLUDE_HPP_ER_2009
#define BOOST_STATISTICS_MODEL_RANDOM_INCLUDE_HPP_ER_2009

#include <boost/statistics/model/random/importance_sampler.hpp>

#endif