//-------------------------------------------------------------------
// The code was written by Vikas C. Raykar
// and is copyrighted under the Lessr GPL:
//
// Copyright (C) 2006 Vikas C. Raykar
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; version 2.1 or later.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston,
// MA 02111-1307, USA.
//
// The author may be contacted via email at: vikas(at)cs(.)umd(.)edu
//-------------------------------------------------------------------

//-------------------------------------------------------------
// File    : FastUnivariateDensityDerivative.cpp
// Purpose : Implementation for for FastUnivariateDensityDerivative
// Author  : Vikas C. Raykar (vikas@cs.umd.edu)
// Date    : September 17, 2005
//-------------------------------------------------------------

#include <math.h>
#include <boost/python.hpp>
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define max(a, b) (((a) > (b)) ? (a) : (b))
#define P_UL 500
#define R 1.0
namespace python = boost::python;
//-------------------------------------------------------------------
// Constructor.
//
// PURPOSE
// -------
// Initialize the class.
// Read the parameters.
// Choose the parameter for the algorithm.
// Space subdivision.
// Compute the constant a.
// Compute B or all the clusters.
//
// PARAMETERS
// ----------
// NSources		      --> number of sources, N.
// MTargets		      --> number of targets, M.
// pSources		      --> pointer to sources, px(N).
// pTargets           --> pointer to the targets, py(M).
// Bandwidth		  --> the source bandwidth, h.
// Order              --> order of the derivative, r.
// epsilon            --> desired error, eps.
// pDensityDerivative --> pointer the the evaluated Density
//-------------------------------------------------------------------

class FastUnivariateDensityDerivative {

public:
    //constructor
    FastUnivariateDensityDerivative(
        int     NSources,
        int     MTargets,
        python::list pSources,
        python::list pTargets,
        double  Bandwidth,
        int     Order,
        double  epsilon);

    python::list pDensityDerivative;

    //destructor
    ~FastUnivariateDensityDerivative();

    //function to evaluate the Density Derivative
    void evaluate();

    //function to evaluate the Hermite polynomial.
    double hermite(double x, int r);

private:
    int N; //number of sources.
    int M; //number of targets.
    double* px; //pointer to sources, (N).
    double* py; //pointer to the targets, (M).
    double h; //the source bandwidth.
    int r; //the rth density derivative.
    double eps; //the desired error
    double* pD; //pointer to the evaluated Density Derivative, (M).

    double rx;
    double rr;
    double ry;
    int K;
    int p;
    double h_square;
    double two_h_square;

    double* pClusterCenter;
    int* pClusterIndex;

    int num_of_a_terms;
    double* a_terms;

    int num_of_b_terms;
    double* b_terms;

    double pi;
    double q;

    void* operator new[](size_t s) { return malloc(s); }
    void operator delete[](void* mem) { free(mem); }

    int factorial(int n);
    double* converter_double(python::list lis);
    python::list converter_list(double* lis);
    void choose_parameters();
    void space_sub_division();
    void compute_a();
    void compute_b();
};

FastUnivariateDensityDerivative::FastUnivariateDensityDerivative(
    int NSources,
    int MTargets,
    python::list pSources,
    python::list pTargets,
    double Bandwidth,
    int Order,
    double epsilon)
{
    // Read the arguments.
    N = NSources;
    M = MTargets;
    px = converter_double(pSources);
    h = Bandwidth;
    r = Order;
    py = converter_double(pTargets);
    pD = new double[N];
    eps = epsilon;

    h_square = h * h;
    two_h_square = 2 * h_square;

    pi = 3.14159265358979;
    q = (pow(-1, r)) / (sqrt(2 * pi) * N * (pow(h, (r + 1))));

    // Choose the parameters for the algorithm.
    choose_parameters();

    // Space sub-division
    space_sub_division();

    // Compute the constant a
    compute_a();

    // Compute the constant B
    compute_b();
}

double* FastUnivariateDensityDerivative::converter_double(python::list lis)
{
    int length = len(lis);
    double* temp = new double[length];
    for (int i = 0; i < length; i++)
    {
        temp[i] = python::extract<double>(lis[i]);
    }
    return temp;
}

python::list FastUnivariateDensityDerivative::converter_list(double* lis)
{
    python::list temp;
    for (int i = 0; i < N; i++)
    {
        temp.append(lis[i]);
    }
    return temp;
}
//-------------------------------------------------------------------
// Destructor.
//-------------------------------------------------------------------
FastUnivariateDensityDerivative::~FastUnivariateDensityDerivative()
{
    delete[] a_terms;
    delete[] b_terms;
}

//-------------------------------------------------------------------
// Compute the factorial.
//-------------------------------------------------------------------
int FastUnivariateDensityDerivative::factorial(int n)
{
    int fact = 1;
    for (int i = 1; i <= n; i++) {
        fact = fact * i;
    }
    return fact;
}

//-------------------------------------------------------------------
// Choose the parameters
// 1. rx --> interval length.
// 2. K  --> number of intervals.
// 3. rr --> cutoff radius.
// 4. ry --> cluster cutoff radius.
// 5. p  --> truncation number.
//-------------------------------------------------------------------

void FastUnivariateDensityDerivative::choose_parameters()
{
    // 1. rx --> interval length.
    rx = h / 2;

    // 2. K  --> number of intervals.
    K = (int)ceil(1.0 / rx);
    rx = 1.0 / K;
    double rx_square = rx * rx;

    // 3. rr --> cutoff radius.
    double r_term = sqrt((double)factorial(r));
    rr = min(R, 2 * h * sqrt(log(r_term / eps)));

    // 4. ry --> cluster cutoff radius.
    ry = rx + rr;

    // 5. p  --> truncation number.
    p = 0;
    double error = 1;
    double temp = 1;
    double comp_eps = eps / r_term;
    while ((error > comp_eps) & (p <= P_UL)) {
        p++;
        double b = min(((rx + sqrt((rx_square) + (8 * p * h_square))) / 2), ry);
        double c = rx - b;
        temp = temp * (((rx * b) / h_square) / p);
        error = temp * (exp(-(c * c) / 2 * two_h_square));
    }
    p = p + 1;
}

//-------------------------------------------------------------------
// Space subdivision
//-------------------------------------------------------------------
void FastUnivariateDensityDerivative::space_sub_division()
{

    // 1. Cluster Centers
    pClusterCenter = new double[K];
    for (int i = 0; i < K; i++) {
        pClusterCenter[i] = (i * rx) + (rx / 2);
    }

    //2. Allocate each source to the corresponding interval
    pClusterIndex = new int[N];
    for (int i = 0; i < N; i++) {
        pClusterIndex[i] = min((int)floor(px[i] / rx), K - 1);
    }
}

//-------------------------------------------------------------------
// Compute the contant term a_{lm}.
// l=0...floor(r/2)
// m=0...r-2l
//-------------------------------------------------------------------
void FastUnivariateDensityDerivative::compute_a()
{
    double r_factorial = (double)factorial(r);
    double* l_constant;
    l_constant = new double[((int)floor((double)r / 2)) + 1];
    l_constant[0] = 1;
    for (int l = 1; l <= (int)floor((double)r / 2); l++) {
        l_constant[l] = l_constant[l - 1] * (-1.0 / (2 * l));
    }
    double* m_constant;
    m_constant = new double[r + 1];
    m_constant[0] = 1;
    for (int m = 1; m <= r; m++) {
        m_constant[m] = m_constant[m - 1] * (-1.0 / m);
    }
    num_of_a_terms = 0;
    for (int l = 0; l <= (int)floor((double)r / 2); l++) {
        for (int m = 0; m <= r - (2 * l); m++) {
            num_of_a_terms++;
        }
    }
    a_terms = new double[num_of_a_terms];
    int k = 0;
    for (int l = 0; l <= (int)floor((double)r / 2); l++) {
        for (int m = 0; m <= r - (2 * l); m++) {
            a_terms[k] = (l_constant[l] * m_constant[m] * r_factorial) / ((double)factorial(r - (2 * l) - m));
            k++;
        }
    }

    delete[] l_constant;
    delete[] m_constant;
}

//-------------------------------------------------------------------
// Compute the contant term B^{n}_{km} for all the clusters.
// n=0...K-1
// k=0...p-1
// m=0...r
//-------------------------------------------------------------------
void FastUnivariateDensityDerivative::compute_b()
{
    num_of_b_terms = K * p * (r + 1);
    b_terms = new double[num_of_b_terms];
    double* k_factorial;
    k_factorial = new double[p];
    k_factorial[0] = 1;
    for (int i = 1; i < p; i++) {
        k_factorial[i] = k_factorial[i - 1] / i;
    }
    double* temp3;
    temp3 = new double[p + r];
    for (int n = 0; n < K; n++) {
        for (int k = 0; k < p; k++) {
            for (int m = 0; m < r + 1; m++) {
                b_terms[(n * p * (r + 1)) + ((r + 1) * k) + m] = 0.0;
            }
        }
    }
    for (int i = 0; i < N; i++) {
        int cluster_number = pClusterIndex[i];
        double temp1 = (px[i] - pClusterCenter[cluster_number]) / h;
        double temp2 = exp(-temp1 * temp1 / 2);
        temp3[0] = 1;
        for (int k = 1; k < p + r; k++) {
            temp3[k] = temp3[k - 1] * temp1;
        }
        for (int k = 0; k < p; k++) {
            for (int m = 0; m < r + 1; m++) {
                b_terms[(cluster_number * p * (r + 1)) + ((r + 1) * k) + m] += (temp2 * temp3[k + m]);
            }
        }
    }
    for (int n = 0; n < K; n++) {
        for (int k = 0; k < p; k++) {
            for (int m = 0; m < r + 1; m++) {
                b_terms[(n * p * (r + 1)) + ((r + 1) * k) + m] *= (k_factorial[k] * q);
            }
        }
    }

    delete[] k_factorial;
    delete[] temp3;
}

//-------------------------------------------------------------------
// Actual function to evaluate the Univariate Density Derivative.
//-------------------------------------------------------------------
void FastUnivariateDensityDerivative::evaluate()
{
    double* temp3 = new double[p + r];
    for (int j = 0; j < M; j++) {
        pD[j] = 0.0;
        int target_cluster_number = min((int)floor(py[j] / rx), K - 1);
        double temp1 = py[j] - pClusterCenter[target_cluster_number];
        double dist = abs(temp1);
        while (dist <= ry && target_cluster_number < K && target_cluster_number >= 0) {
            double temp2 = exp(-temp1 * temp1 / two_h_square);
            double temp1h = temp1 / h;
            temp3[0] = 1;
            for (int i = 1; i < p + r; i++) {
                temp3[i] = temp3[i - 1] * temp1h;
            }
            for (int k = 0; k <= p - 1; k++) {
                int dummy = 0;
                for (int l = 0; l <= (int)floor((double)r / 2); l++) {
                    for (int m = 0; m <= r - (2 * l); m++) {
                        pD[j] = pD[j] + (a_terms[dummy] * b_terms[(target_cluster_number * p * (r + 1)) + ((r + 1) * k) + m] * temp2 * temp3[k + r - (2 * l) - m]);
                        dummy++;
                    }
                }
            }
            target_cluster_number++;
            temp1 = py[j] - pClusterCenter[target_cluster_number];
            dist = abs(temp1);
        }
        target_cluster_number = min((int)floor(py[j] / rx), K - 1) - 1;
        if (target_cluster_number >= 0) {
            double temp1 = py[j] - pClusterCenter[target_cluster_number];
            double dist = abs(temp1);
            while (dist <= ry && target_cluster_number < K && target_cluster_number >= 0) {
                double temp2 = exp(-temp1 * temp1 / two_h_square);
                double temp1h = temp1 / h;
                temp3[0] = 1;
                for (int i = 1; i < p + r; i++) {
                    temp3[i] = temp3[i - 1] * temp1h;
                }
                for (int k = 0; k <= p - 1; k++) {
                    int dummy = 0;
                    for (int l = 0; l <= (int)floor((double)r / 2); l++) {
                        for (int m = 0; m <= r - (2 * l); m++) {
                            pD[j] = pD[j] + (a_terms[dummy] * b_terms[(target_cluster_number * p * (r + 1)) + ((r + 1) * k) + m] * temp2 * temp3[k + r - (2 * l) - m]);
                            dummy++;
                        }
                    }
                }
                target_cluster_number--;
                temp1 = py[j] - pClusterCenter[target_cluster_number];
                dist = abs(temp1);
            }
        }
    }
    pDensityDerivative = converter_list(pD);
    delete[] temp3;
}

BOOST_PYTHON_MODULE(fast_deriv)
{
    namespace python = boost::python;
    python::class_<FastUnivariateDensityDerivative>("FastUnivariateDensityDerivative",
        python::init<int,
                     int,
                     python::list,
                     python::list,
                     double,
                     int,
                     double
                    >())
        .def("evaluate", &FastUnivariateDensityDerivative::evaluate)
        .def_readonly("pD", &FastUnivariateDensityDerivative::pDensityDerivative)
    ;

}


// Software License for MTL
//
// Copyright (c) 2007 The Trustees of Indiana University.
//               2008 Dresden University of Technology and the Trustees of Indiana University.
//               2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also license.mtl.txt in the distribution.

#include <iostream>
#include <vector>
#include <boost/numeric/mtl/mtl.hpp>

using namespace std;

template <typename Vector>
void test(const Vector&, const char* name)
{
    mtl::multi_vector<Vector> A(5, 5), B(2, 3);

    A[2][3]= 7.0;
    cout << "A[2][3] = " << A[2][3] << "\n";

    A= 3.0;
    cout << name << ":\n A after initialization is \n" << A;

    MTL_THROW_IF(A[1][1] != 3.0, mtl::runtime_error("Wrong value in diagonal"));
    MTL_THROW_IF(A[1][2] != 0.0, mtl::runtime_error("Wrong value off diagonal"));

    cout << "Dimension of B is " << num_rows(B) << " x " << num_cols(B) << "\n\n";
}

int main(int, char**)
{
    using namespace mtl;

    dense_vector<double>    v;
    std::vector<double>     w;

    test(v, "dense_vector<double>");
    test(w, "std::vector<double>");

    return 0;
}


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   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
//==============================================================================
//////////////////////////////////////////////////////////////////////////////
// cover test behavior of arithmetic components in scalar mode
//////////////////////////////////////////////////////////////////////////////

#include <nt2/arithmetic/include/functions/correct_fma.hpp>
#include <boost/simd/sdk/simd/io.hpp>
#include <vector>
#include <nt2/include/constants/valmin.hpp>
#include <nt2/include/constants/valmax.hpp>
#include <nt2/include/constants/sqrtvalmax.hpp>
#include <nt2/include/constants/mone.hpp>
#include <nt2/include/constants/one.hpp>
#include <nt2/include/functions/scalar/multiplies.hpp>

#include <nt2/sdk/unit/tests/cover.hpp>
#include <nt2/sdk/unit/module.hpp>
#include <nt2/sdk/unit/tests/type_expr.hpp>

NT2_TEST_CASE_TPL ( correct_fma_real__1_0_1,  (float))
{

  using nt2::correct_fma;
  using nt2::tag::correct_fma_;
  typedef typename nt2::meta::call<correct_fma_(T, T, T)>::type r_t;

  nt2::uint32_t NR = NT2_NB_RANDOM_TEST;
  std::vector<T> in1(NR), in2(NR), in3(NR);
  std::vector<r_t> ref(NR);
  nt2::roll(in1, nt2::Mone<T>()/2, nt2::One<T>()/2);
  nt2::roll(in2, nt2::Mone<T>()/2, nt2::One<T>()/2);
  nt2::roll(in3, nt2::Mone<T>()/2, nt2::One<T>()/2);
  for(nt2::uint32_t i=0; i < NR ; ++i)
  {
    ref[i] = double(in1[i])*double(in2[i])+double(in3[i]);
  }

 NT2_COVER_ULP_EQUAL(correct_fma_, ((T, in1))((T, in2))((T, in3)), ref, 0);

}

NT2_TEST_CASE_TPL ( correct_fma_real2,  NT2_REAL_TYPES)
{

  using nt2::correct_fma;
  using nt2::tag::correct_fma_;
  typedef typename nt2::meta::call<correct_fma_(T, T, T)>::type r_t;

  nt2::uint32_t NR = NT2_NB_RANDOM_TEST;
  std::vector<T> in1(NR), in2(NR), in3(NR);
  std::vector<r_t> ref(NR);
  nt2::roll(in1, nt2::Valmin<T>()/2, nt2::Valmax<T>()/2);
  for(uint32_t i=0; i < NR ; i++)
  {
    in2[i] = 3;
    in3[i] = -in1[i];
    ref[i] = 2*in1[i];
  }

 NT2_COVER_ULP_EQUAL(correct_fma_, ((T, in1))((T, in2))((T, in3)), ref, 0);

}

NT2_TEST_CASE_TPL ( correct_fma_ui,  NT2_INTEGRAL_UNSIGNED_TYPES)
{

  using nt2::correct_fma;
  using nt2::tag::correct_fma_;
  typedef typename nt2::meta::call<correct_fma_(T, T, T)>::type r_t;

  static const nt2::uint32_t NR = NT2_NB_RANDOM_TEST;
  std::vector<T> in1(NR), in2(NR), in3(NR);
  std::vector<r_t> ref(NR);
  nt2::roll(in1, nt2::Valmin<T>(), nt2::Valmax<T>());
  nt2::roll(in2, nt2::Valmin<T>(), nt2::Valmax<T>());
  nt2::roll(in3, nt2::Valmin<T>(), nt2::Valmax<T>());
  for(nt2::uint32_t i=0; i < NR ; ++i)
  {
    std::cout << "i " << i << std::endl;
    ref[i] = T(nt2::multiplies(in1[i], in2[i]))+T(in3[i]);
  }
 NT2_COVER_ULP_EQUAL(correct_fma_, ((T, in1))((T, in2))((T, in3)), ref, 0);

}

NT2_TEST_CASE_TPL ( correct_fma_si,  NT2_INTEGRAL_SIGNED_TYPES)
{

  using nt2::correct_fma;
  using nt2::tag::correct_fma_;
  typedef typename nt2::meta::call<correct_fma_(T, T, T)>::type r_t;

  static const nt2::uint32_t NR = NT2_NB_RANDOM_TEST;
  std::vector<T> in1(NR), in2(NR), in3(NR);
  std::vector<r_t> ref(NR);
  nt2::roll(in1, -nt2::Sqrtvalmax<T>()/2, nt2::Sqrtvalmax<T>()/2);
  nt2::roll(in2, -nt2::Sqrtvalmax<T>()/2, nt2::Sqrtvalmax<T>()/2);
  nt2::roll(in3, -nt2::Sqrtvalmax<T>()/2, nt2::Sqrtvalmax<T>()/2);
  for(nt2::uint32_t i=0; i < NR ; ++i)
  {
    ref[i] = T(nt2::multiplies(in1[i], in2[i]))+T(in3[i]);
  }
 NT2_COVER_ULP_EQUAL(correct_fma_, ((T, in1))((T, in2))((T, in3)), ref, 0);

}


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   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
//==============================================================================
#define NT2_UNIT_MODULE "nt2 trigonometric toolbox - rem_pio2_cephes/simd Mode"

//////////////////////////////////////////////////////////////////////////////
// unit test behavior of trigonometric components in simd mode
//////////////////////////////////////////////////////////////////////////////
/// created  by jt the 11/02/2011
///
#include <nt2/trigonometric/include/functions/rem_pio2_cephes.hpp>
#include <boost/simd/sdk/simd/native.hpp>
#include <boost/simd/sdk/simd/io.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <nt2/include/constants/zero.hpp>
#include <nt2/sdk/meta/as_integer.hpp>

#include <nt2/sdk/unit/module.hpp>
#include <nt2/sdk/unit/tests/relation.hpp>
#include <nt2/sdk/unit/tests/ulp.hpp>
#include <nt2/sdk/unit/tests/type_expr.hpp>

NT2_TEST_CASE_TPL ( rem_pio2_cephes_real__1_0,  NT2_SIMD_REAL_TYPES)
{
  using nt2::rem_pio2_cephes;
  using nt2::tag::rem_pio2_cephes_;
  using boost::simd::native;
  typedef typename nt2::meta::as_integer<T>::type           iT;
  typedef native<T,BOOST_SIMD_DEFAULT_EXTENSION>            vT;
  typedef native<iT,BOOST_SIMD_DEFAULT_EXTENSION>          viT;

  {
    vT r1;
    NT2_TEST_EQUAL( rem_pio2_cephes(nt2::Zero<vT>(), r1), nt2::Zero<viT>());
    NT2_TEST_ULP_EQUAL( r1, nt2::Zero<vT>(), 0.5);
  }

}


//==================================================================================================
/*!

  Copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#include <boost/simd/function/scalar/rem_2pi.hpp>
#include <scalar_test.hpp>
#include <boost/simd/constant/inf.hpp>
#include <boost/simd/constant/minf.hpp>
#include <boost/simd/constant/nan.hpp>
#include <boost/simd/constant/one.hpp>
#include <boost/simd/constant/mone.hpp>
#include <boost/simd/constant/zero.hpp>
#include <boost/simd/constant/mzero.hpp>
#include <boost/simd/constant/eps.hpp>
#include <boost/simd/constant/pi.hpp>
#include <boost/simd/constant/pio_2.hpp>
#include <boost/simd/constant/twopi.hpp>
#include <boost/simd/constant/ten.hpp>

STF_CASE_TPL (" rem_2pi",  STF_IEEE_TYPES)
{
  namespace bs = boost::simd;
  namespace bd = boost::dispatch;
  using bs::rem_2pi;

  {
    T res = rem_2pi(bs::Zero<T>());
    STF_ULP_EQUAL( res, bs::Zero<T>(), 1.5);
    res = rem_2pi(bs::Pi<T>()-bs::Ten<T>()*bs::Eps<T>());
    STF_ULP_EQUAL( res, bs::Pi<T>()-bs::Ten<T>()*bs::Eps<T>(), 1.5);
    res = rem_2pi(bs::Pi<T>()+bs::Ten<T>()*bs::Eps<T>());
    STF_ULP_EQUAL( res, bs::Ten<T>()*bs::Eps<T>()-bs::Pi<T>(), 1.5);
    res = rem_2pi(bs::Twopi<T>());
    STF_ULP_EQUAL( res, bs::Zero<T>(), 1.5);
    res = rem_2pi(bs::Pio_2<T>());
    STF_ULP_EQUAL( res, bs::Pio_2<T>(), 1.5);
  }
} // end of test for floating_


// from http://perso.ens-lyon.fr/philippe.theveny/cise.pdf

#include <iostream>
#include <utility>
#include <cmath> // _Decimal64?

#include <boost/tr1/cmath.hpp>
#include <boost/math/special_functions.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/multiprecision/cpp_bin_float.hpp>

typedef boost::multiprecision::number<
            boost::multiprecision::backends::cpp_bin_float<
                106,
                boost::multiprecision::backends::digit_base_2,
                void,
                boost::int16_t, -1022, 1023>,
            boost::multiprecision::et_off>
        cpp_bin_float_double_double;

typedef boost::multiprecision::number<
            boost::multiprecision::backends::cpp_bin_float<
                1000,
                boost::multiprecision::backends::digit_base_2,
                void,
                boost::int16_t, -1022, 1023>,
            boost::multiprecision::et_off>
        cpp_bin_float_1000b;

using boost::multiprecision::cpp_dec_float;
using boost::multiprecision::cpp_bin_float_single;
using boost::multiprecision::cpp_bin_float_double;
using boost::multiprecision::cpp_bin_float_double_extended;
using boost::multiprecision::cpp_bin_float_quad;

typedef boost::multiprecision::number<cpp_dec_float<64> > mp_type;

// This is busted: https://svn.boost.org/trac/boost/ticket/11764
//typedef boost::multiprecision::cpp_bin_float_single mp_type;

//typedef boost::multiprecision::cpp_bin_float_double mp_type;
//typedef boost::multiprecision::cpp_bin_float_double_extended mp_type;
//typedef boost::multiprecision::cpp_bin_float_quad mp_type;
//typedef cpp_bin_float_double_double mp_type;
//typedef cpp_bin_float_1000b mp_type;

//typedef _Decimal64 mp_type;

int main (void)
{
    mp_type a = sin((mp_type)1e22);
    mp_type b = log((mp_type)17.1);
    mp_type c = exp((mp_type)0.42);
    mp_type d = 173746*a + 94228*b - 78487*c;
    std::cout << std::setprecision(std::numeric_limits<mp_type>::max_digits10) << d << std::endl;
    return 0;
}


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

#include <Eigen/Dense>
#include <graphlab.hpp>

typedef Eigen::VectorXd vec_type;
typedef Eigen::MatrixXd mat_type;

#include <graphlab/util/stl_util.hpp>
#include <graphlab/macros_def.hpp>

int main(int argc, char** argv) {
  global_logger().set_log_level(LOG_INFO);
  global_logger().set_log_to_console(true);

  // Parse command line options -----------------------------------------------
  const std::string description =
    "Creates a folder with synthetic training data";
  graphlab::command_line_options clopts(description, false);
  std::string output_folder = "synthetic_data";
  size_t nfiles            = 5;
  size_t D                 = 20;
  size_t nusers            = 1000;
  size_t nmovies           = 10000;
  size_t nvalidate       = 2;
  size_t npredict          = 1;
  double noise             = 0.1;
  double stdev             = 2;
  double alpha             = 1.8;

  clopts.attach_option("dir", output_folder,
                       "Location to create the data files");
  clopts.attach_option("nfiles", nfiles,
                       "The number of files to generate.");
  clopts.attach_option("D", D, "Number of latent dimensions.");
  clopts.attach_option("nusers", nusers,
                       "The number of users.");
  clopts.attach_option("nmovies", nmovies,
                       "The number of movies.");
  clopts.attach_option("alpha", alpha,
                       "The power-law constant.");
  clopts.attach_option("nvalidate", nvalidate,
                       "The validate ratings pers user");
  clopts.attach_option("npredict", npredict,
                       "The predict ratings pers user");

  clopts.attach_option("noise", noise,
                       "The standard deviation noise parameter");
  clopts.attach_option("stdev", stdev,
                       "The standard deviation in latent factor values");

  if(!clopts.parse(argc, argv)) {
    std::cout << "Error in parsing command line arguments." << std::endl;
    return EXIT_FAILURE;
  }

  std::cout << "Creating data directory: " << output_folder << std::endl;
  boost::filesystem::path directory(output_folder);
  if(!boost::filesystem::create_directory(output_folder)) {
    logstream(LOG_ERROR)
      << "Error creating directory: " << directory << std::endl;
    return EXIT_FAILURE;
  }

  std::cout << "Opening files:" << std::endl;
  std::vector< std::ofstream* > train_files(nfiles);
  std::vector< std::ofstream* > validate_files(nfiles);
  std::vector< std::ofstream* > predict_files(nfiles);
  for(size_t i = 0; i < nfiles; ++i) {
    const std::string train_fname =
      output_folder + "/graph_" + graphlab::tostr(i) + ".tsv";
    train_files[i] = new std::ofstream(train_fname.c_str());
    if(!train_files[i]->good()) {
      logstream(LOG_ERROR)
        << "Error creating file: " << train_fname;
    }

    const std::string validate_fname =
      output_folder + "/graph_" + graphlab::tostr(i) + ".tsv.validate";
    validate_files[i] = new std::ofstream(validate_fname.c_str());
    if(!validate_files[i]->good()){
      logstream(LOG_ERROR)
        << "Error creating file: " << train_fname;
    }

    const std::string predict_fname =
      output_folder + "/graph_" + graphlab::tostr(i) + ".tsv.predict";
    predict_files[i] = new std::ofstream(predict_fname.c_str());
    if(!predict_files[i]->good()){
      logstream(LOG_ERROR)
        << "Error creating file: " << train_fname;
    }
  }

  // Make synthetic latent factors
  std::vector< vec_type > user_factors(nusers);
  std::vector< vec_type > movie_factors(nmovies);
  // Create a shared random number generator
  graphlab::random::generator gen; gen.seed(31413);

  std::cout << "Constructing latent user factors" << std::endl;
  foreach(vec_type& factor, user_factors) {
    factor.resize(D);
    // Randomize the factor
    for(size_t d = 0; d < D; ++d)
      factor(d) = gen.gaussian(0, stdev);
  }

  std::cout << "Constructing latent movie factors" << std::endl;
  foreach(vec_type& factor, movie_factors) {
    factor.resize(D);
    // Randomize the factor
    for(size_t d = 0; d < D; ++d)
      factor(d) = gen.gaussian(0, stdev);
  }

  size_t nedges_train = 0;
  ASSERT_GT(nusers, nvalidate + npredict);
  // Make power-law probability vector
  std::vector<double> prob(nusers - nvalidate - npredict);
  for(size_t i = 0; i < prob.size(); ++i)
    prob[i] = std::pow(double(i+1), -alpha);
  graphlab::random::pdf2cdf(prob);
  for(size_t movie_id = 0, user_id = 0; movie_id < nmovies; ++movie_id) {
    // Add power-law out degree ratings
    const size_t out_degree = gen.multinomial_cdf(prob) + 1;
    for(size_t i = 0; i < out_degree; ++i) {
      user_id = (user_id + 2654435761)  % nusers;
      const size_t file_id = user_id % nfiles;
      const double rating =
        user_factors[user_id].dot(movie_factors[movie_id]);
      *(train_files[file_id])
        << user_id << '\t' << (movie_id + nusers) << '\t' << rating << '\n';
      nedges_train++;
    }
    // Add a few extra validate ratings
    for(size_t i = 0; i < nvalidate; ++i) {
      user_id = (user_id + 2654435761)  % nusers;
      const size_t file_id = user_id % nfiles;
      const double rating =
        user_factors[user_id].dot(movie_factors[movie_id]);
      *(validate_files[file_id])
        << user_id << '\t' << (movie_id + nusers) << '\t' << rating << '\n';
    }
    // Add a few extra predict ratings
    for(size_t i = 0; i < npredict; ++i) {
      user_id = (user_id + 2654435761)  % nusers;
      const size_t file_id = user_id % nfiles;
      *(predict_files[file_id])
        << user_id << '\t' << (movie_id + nusers) << '\n';
    }
  } // end of loop over movies
  for(size_t i = 0; i < nfiles; ++i) {
    train_files[i]->close();
    delete train_files[i]; train_files[i] = NULL;
    validate_files[i]->close();
    delete validate_files[i]; validate_files[i] = NULL;
    predict_files[i]->close();
    delete predict_files[i]; predict_files[i] = NULL;
  }
  std::cout << "Created " << nedges_train
            << " training edges." << std::endl;

} // end of main


/**
 * Could really represent any coordinate that has X/Y/Z axises.
 *
 * Copyright 2013 Bruce Ide
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

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

#ifndef _HPP_XYZ_COORDINATE
#define _HPP_XYZ_COORDINATE

namespace fr {

  namespace coordinates {

    class xyz_coordinate {
    protected:
      double x,y,z;

      inline double limit(double val, double lower, double upper)
      {
	assert(upper > lower);
	double retval = val;
	if (val > upper) {
	  retval = upper;
	} else if (val < lower) {
	  retval = lower;
	}
	return retval;
      }

      inline Eigen::Vector3d unit_vec(const Eigen::Vector3d &vec)
      {
	Eigen::Vector3d retval;
	double nrm = vec.norm();
	retval = vec / nrm;
	return retval;
      }

      // Generic interpolate between two points

      Eigen::Vector3d interpolate_3x3(const Eigen::Vector3d &this_vec, const double &time_now, const Eigen::Vector3d &next_vec, const double &time_other, const double &time_between)
      {
	Eigen::Quaternion<double> quat(1.0, 0.0, 0.0, 0.0);
	double this_norm = this_vec.norm();
	double next_norm = next_vec.norm();
	Eigen::Vector3d this_uv = unit_vec(this_vec);
	Eigen::Vector3d next_uv = unit_vec(next_vec);
	double x = this_uv.dot(next_uv);
	double y = limit(x,-1.0, 1.0);
	double theta = acos(y);
	double adjust = (time_between - time_now) / (time_other - time_now);
	double factor = ((next_norm - this_norm) * adjust + this_norm) / this_norm;
	theta = theta * adjust;
	double sto2 = sin(theta / 2.0);
	Eigen::Vector3d ax = next_vec.cross(this_vec);
	Eigen::Vector3d ax_uv = unit_vec(ax);
	double qx, qy, qz, qw;
	qx = ax_uv(0) * sto2;
	qy = ax_uv(1) * sto2;
	qz = ax_uv(2) * sto2;
	qw = cos(theta/2.0);
	quat = Eigen::Quaternion<double>(qw,qx,qy,qz);
	Eigen::Vector3d z = this_vec * factor;
	Eigen::Quaternion<double> z_q(0.0, z(0), z(1), z(2));
	Eigen::Quaternion<double> q1 = z_q * quat;
	Eigen::Quaternion<double> retaq = quat.inverse();
	Eigen::Quaternion<double> q2 = retaq * q1;
	return q2.vec();
      }

    public:
      xyz_coordinate(const double &x, const double &y, const double &z) : x(x), y(y), z(z)
      {
      }

      xyz_coordinate(const xyz_coordinate &copy) : x(copy.get_x()), y(copy.get_y()), z(copy.get_z())
      {
      }

      // Ah ah, ah ha! Yep, I'm planning children classes!
      virtual ~xyz_coordinate()
      {
      }

      virtual double get_x() const { return x; }
      virtual double get_y() const { return y; }
      virtual double get_z() const { return z; }

      virtual Eigen::Vector3d get_xyz() const
      {
	Eigen::Vector3d retval;
	retval << x,y,z;
	return retval;
      }

    };

  }
}

#endif


//          Copyright Carl Philipp Reh 2006 - 2019.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#include <sge/renderer/pixel_rect.hpp>
#include <sge/renderer/screen_size.hpp>
#include <sge/renderer/target/viewport.hpp>
#include <sge/viewport/fractional_aspect.hpp>
#include <sge/viewport/impl/center.hpp>
#include <sge/viewport/impl/maintain_aspect_function.hpp>
#include <sge/window/dim.hpp>
#include <awl/window/event/resize.hpp>
#include <fcppt/cast/size_fun.hpp>
#include <fcppt/cast/to_signed_fun.hpp>
#include <fcppt/math/dim/output.hpp>
#include <fcppt/math/dim/structure_cast.hpp>
#include <fcppt/math/vector/null.hpp>
#include <fcppt/config/external_begin.hpp>
#include <boost/rational.hpp>
#include <fcppt/config/external_end.hpp>

namespace
{

using rational = boost::rational<sge::viewport::fractional_aspect::value_type>;

rational::int_type truncate_rational(rational const &_rational)
{
  return _rational.numerator() / _rational.denominator();
}

}

sge::renderer::target::viewport sge::viewport::impl::maintain_aspect_function(
    awl::window::event::resize const &_event, sge::viewport::fractional_aspect const _aspect)
{
  rational const desired(_aspect.num(), _aspect.denom());

  rational::int_type const width(_event.dim().w());

  rational::int_type const height(_event.dim().h());

  rational const actual(width, height);

  if (actual == desired)
  {
    return sge::renderer::target::viewport(sge::renderer::pixel_rect(
        fcppt::math::vector::null<sge::renderer::pixel_rect::vector>(),
        fcppt::math::dim::
            structure_cast<sge::renderer::pixel_rect::dim, fcppt::cast::to_signed_fun>(
                fcppt::math::dim::structure_cast<sge::renderer::screen_size, fcppt::cast::size_fun>(
                    _event.dim()))));
  }

  bool const horizontal(actual > desired);

  rational const difference(horizontal ? desired / actual : actual / desired);

  return sge::viewport::impl::center(
      horizontal ? sge::window::dim(truncate_rational(rational(width) * difference), height)
                 : sge::window::dim(width, truncate_rational(rational(height) * difference)),
      fcppt::math::dim::structure_cast<sge::window::dim, fcppt::cast::size_fun>(_event.dim()));
}


#include <boost/graph/page_rank.hpp>


#include <CGAL/Simple_cartesian.h>
#include <CGAL/Arr_segment_traits_2.h>
#include <CGAL/Arrangement_2.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Triangulation_2.h>
#include <CGAL/Delaunay_triangulation_3.h>
#include <CGAL/Linear_cell_complex.h>
#include <CGAL/boost/graph/graph_traits_Arrangement_2.h>
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
#include <CGAL/boost/graph/graph_traits_Triangulation_2.h>
#include <map>
#include <boost/unordered_map.hpp>

typedef CGAL::Simple_cartesian<double> Kernel;
typedef CGAL::Arr_segment_traits_2<Kernel> Arrangement_traits_2;
typedef CGAL::Arrangement_2<Arrangement_traits_2> Arrangement_2;

typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
typedef CGAL::Surface_mesh<Kernel::Point_3> Surface_mesh;
typedef CGAL::Triangulation_2<Kernel> Triangulation_2;
#ifdef CGAL_LINKED_WITH_TBB
typedef CGAL::Triangulation_data_structure_3<
    CGAL::Triangulation_vertex_base_3<Kernel>,
    CGAL::Triangulation_cell_base_3<Kernel>,
    CGAL::Parallel_tag>                          Tds;
typedef CGAL::Delaunay_triangulation_3<Kernel, Tds> Triangulation_3;
#endif

typedef CGAL::Linear_cell_complex<3, 3> Linear_cell_complex_3;

template <typename P>
void
fct(const P& )
{
  typedef typename boost::graph_traits<P>::vertex_descriptor vertex_descriptor;

  std::map<vertex_descriptor,int> M;
  vertex_descriptor vd;
  M.find(vd);

  boost::unordered_map<vertex_descriptor, int> U;
  U[vd] = 12;
}

void fct2()
{
  typedef Linear_cell_complex_3::Dart_handle dh;
  typedef Linear_cell_complex_3::Vertex_attribute_handle vh;

  { // For dart handle
  std::map<dh, int> M;
  dh e;
  M.find(e);
  boost::unordered_map<dh, int> U;
  U[e] = 12;
  }

  { // For vertex attribute handle
  std::map<vh, int> M;
  vh e;
  M.find(e);
  boost::unordered_map<vh, int> U;
  U[e] = 12;
  }
}

template <typename P>
void
fct3(const P& )
{
  typedef typename P::Vertex_handle vertex_descriptor;

  std::map<vertex_descriptor,int> M;
  vertex_descriptor vd;
  M.find(vd);

  boost::unordered_map<vertex_descriptor, int> U;
  U[vd] = 12;
}

int main()
{
  Arrangement_2 A;
  fct(A);

  Polyhedron P;
  fct(P);

  Surface_mesh S;
  fct(S);

  Triangulation_2 T;
  fct(T);

  fct2();

#ifdef CGAL_LINKED_WITH_TBB
  Triangulation_3 T3;
fct3(T3);
#endif
  return 0;
}


/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2019, Individual contributors, see AUTHORS file     |
   | See: https://www.mrpt.org/Authors - All rights reserved.               |
   | Released under BSD License. See: https://www.mrpt.org/License          |
   +------------------------------------------------------------------------+ */

#include "vision-precomp.h"  // Precompiled headers

#include <mrpt/math/ops_matrices.h>
#include <mrpt/vision/CFeatureExtraction.h>
#include <Eigen/Dense>

using namespace mrpt;
using namespace mrpt::vision;
using namespace mrpt::img;
using namespace mrpt::img;
using namespace mrpt::system;
using namespace mrpt::math;
using namespace std;

void CFeatureExtraction::internal_computeSpinImageDescriptors(
	const CImage& in_img, CFeatureList& in_features)
{
	MRPT_START
	mrpt::system::CTimeLoggerEntry tle(
		profiler, "internal_computeSpinImageDescriptors");

	ASSERT_(options.SpinImagesOptions.radius > 1);

	// This is a C++ implementation of the descriptor "Intensity-domain spin
	// images" as described
	//  in "A sparse texture representation using affine-invariant regions", S
	//  Lazebnik, C Schmid, J Ponce,
	//  2003 IEEE Computer Society Conference on Computer Vision.

	const unsigned int HIST_N_INT =
		options.SpinImagesOptions.hist_size_intensity;
	const unsigned int HIST_N_DIS =
		options.SpinImagesOptions.hist_size_distance;
	const unsigned int R = options.SpinImagesOptions.radius;
	const int img_w = static_cast<int>(in_img.getWidth());
	const int img_h = static_cast<int>(in_img.getHeight());
	const bool img_color = in_img.isColor();

	// constant for passing intensity [0,255] to histogram index
	// [0,HIST_N_INT-1]:
	const float k_int2idx = (HIST_N_INT - 1) / 255.0f;
	const float k_idx2int = 1.0f / k_int2idx;

	// constant for passing distances in pixels [0,R] to histogram index
	// [0,HIST_N_DIS-1]:
	const float k_dis2idx = (HIST_N_DIS - 1) / static_cast<float>(R);
	const float k_idx2dis = 1.0f / k_dis2idx;

	// The Gaussian kernel used below is approximated up to a given distance
	//  in pixels, given by 2 times the appropriate std. deviations:
	const int STD_TIMES = 2;
	const int kernel_size_dist = static_cast<int>(
		ceil(k_dis2idx * STD_TIMES * options.SpinImagesOptions.std_dist));

	const float _2var_int =
		-1.0f / (2 * square(options.SpinImagesOptions.std_intensity));
	const float _2var_dist =
		-1.0f / (2 * square(options.SpinImagesOptions.std_dist));

	// Create the 2D histogram:
	CMatrixDouble hist2d(HIST_N_INT, HIST_N_DIS);

	// Compute intensity-domain spin images
	for (auto& in_feature : in_features)
	{
		// Overwrite scale with the descriptor scale:
		in_feature.keypoint.octave = options.SpinImagesOptions.radius;

		// Reset histogram to zeros:
		hist2d.setZero();

		// Define the ROI around the interest point which counts for the
		// histogram:
		int px0 = round(in_feature.keypoint.pt.x - R);
		int px1 = round(in_feature.keypoint.pt.x + R);
		int py0 = round(in_feature.keypoint.pt.y - R);
		int py1 = round(in_feature.keypoint.pt.y + R);

		// Clip at img borders:
		px0 = max(0, px0);
		px1 = min(img_w - 1, px1);
		py0 = max(0, py0);
		py1 = min(img_h - 1, py1);

		for (int px = px0; px <= px1; px++)
		{
			for (int py = py0; py <= py1; py++)
			{
				uint8_t pix_val;
				// get the pixel color [0,255]
				if (!img_color)
					pix_val = in_img.at<uint8_t>(px, py);
				else
				{
					auto aux_pix_ptr = in_img.ptr<uint8_t>(px, py);
					pix_val =
						(aux_pix_ptr[0] + aux_pix_ptr[1] + aux_pix_ptr[2]) / 3;
				}

				const float pix_dist = hypot(
					in_feature.keypoint.pt.x - px,
					in_feature.keypoint.pt.y - py);
				const int center_bin_dist = k_dis2idx * pix_dist;

				// A factor to correct the histogram due to the existence of
				// more pixels at larger radius:
				//  Obtained as Area = PI ( R1^2 - R2^2 ) for R1,R2 being
				//  pix_dist +- Delta/2
				// const double density_circular_area = 1.0/ (M_2PI *
				// (center_bin_dist+0.5) * square(k_idx2dis) );

				// Apply a "soft-histogram", so each pixel counts into several
				// bins,
				//  weighted by a exponential function to model a Gaussian
				//  kernel
				const int bin_int_low =
					max(0, static_cast<int>(ceil(
							   k_int2idx *
							   (pix_val -
								STD_TIMES *
									options.SpinImagesOptions.std_intensity))));
				const int bin_int_hi =
					min(static_cast<int>(HIST_N_INT - 1),
						static_cast<int>(ceil(
							k_int2idx *
							(pix_val +
							 STD_TIMES *
								 options.SpinImagesOptions.std_intensity))));

				// cout << "d: " << pix_dist << "v: " << (int)pix_val << "\t";

				if (center_bin_dist <
					static_cast<int>(HIST_N_DIS))  // this accounts for the
				// "square" or "circle"
				// shapes of the area to
				// account for
				{
					const int bin_dist_low =
						max(0, center_bin_dist - kernel_size_dist);
					const int bin_dist_hi =
						min(static_cast<int>(HIST_N_DIS - 1),
							center_bin_dist + kernel_size_dist);

					int bin_dist, bin_int;
					float pix_dist_cur_dist =
						pix_dist - bin_dist_low * k_idx2dis;

					for (bin_dist = bin_dist_low; bin_dist <= bin_dist_hi;
						 bin_dist++, pix_dist_cur_dist -= k_idx2dis)
					{
						float pix_val_cur_val =
							pix_val - (bin_int_low * k_idx2int);

						for (bin_int = bin_int_low; bin_int <= bin_int_hi;
							 bin_int++, pix_val_cur_val -= k_idx2int)
						{
							// Gaussian kernel:
							double v = _2var_dist * square(pix_dist_cur_dist) +
									   _2var_int * square(pix_val_cur_val);

							hist2d(bin_int, bin_dist) += exp(v);
						}
					}
					// hist2d(bin_int,bin_dist) *= ;
				}  // in range

			}  // end py
		}  // end px

		// Normalize [0,1]
		mrpt::math::normalize(hist2d, 0, 1);

		// Save the histogram as a vector:
		unsigned idx = 0;
		in_feature.descriptors.SpinImg.emplace();
		std::vector<float>& ptr_trg = *in_feature.descriptors.SpinImg;
		ptr_trg.resize(HIST_N_INT * HIST_N_DIS);

		for (unsigned i = 0; i < HIST_N_DIS; i++)
			for (unsigned j = 0; j < HIST_N_INT; j++)
				ptr_trg[idx++] = hist2d(j, i);

		in_feature.descriptors.SpinImg_range_rows = HIST_N_DIS;

	}  // end for each feature

	MRPT_END
}


/* pcmsolver_copyright_start */
/*
 *     PCMSolver, an API for the Polarizable Continuum Model
 *     Copyright (C) 2013-2016 Roberto Di Remigio, Luca Frediani and contributors
 *
 *     This file is part of PCMSolver.
 *
 *     PCMSolver is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     PCMSolver is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 *
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with PCMSolver.  If not, see <http://www.gnu.org/licenses/>.
 *
 *     For information on the complete list of contributors to the
 *     PCMSolver API, see: <http://pcmsolver.readthedocs.io/>
 */
/* pcmsolver_copyright_end */

#include <cmath>

#include "Config.hpp"

#include <Eigen/Core>
#include <Eigen/LU>

#include "cavity/Cavity.hpp"
#include "cavity/Element.hpp"
#include "green/IGreensFunction.hpp"
#include "utils/MathUtils.hpp"

/*! \file SolverImpl.cpp
 *  \brief Functions common to all solvers
 *  \author Roberto Di Remigio
 *  \date 2015
 */

/*! \brief Builds the **anisotropic** IEFPCM matrix
 *  \param[in] cav the discretized cavity
 *  \param[in] gf_i Green's function inside the cavity
 *  \param[in] gf_o Green's function outside the cavity
 *  \return the \f$ \mathbf{K} = \mathbf{T}^{-1}\mathbf{R}\mathbf{A} \f$ matrix
 *
 *  This function calculates the PCM matrix. We use the following definitions:
 *  \f[
 *     \begin{align}
 *       \mathbf{T} &=
 *      \left(2\pi\mathbf{I} - \mathbf{D}_\mathrm{e}\mathbf{A}\right)\mathbf{S}_\mathrm{i}
 *      +\mathbf{S}_\mathrm{e}\left(2\pi\mathbf{I} +
 *      \mathbf{A}\mathbf{D}_\mathrm{i}^\dagger\right) \\
 *      \mathbf{R} &=
 *      \left(2\pi\mathbf{A}^{-1} - \mathbf{D}_\mathrm{e}\right) -
 *      \mathbf{S}_\mathrm{e}\mathbf{S}^{-1}_\mathrm{i}\left(2\pi\mathbf{A}^{-1}-\mathbf{D}_\mathrm{i}\right)
 *     \end{align}
 *  \f]
 *  The matrix is not symmetrized and is not symmetry packed.
 */
inline Eigen::MatrixXd anisotropicIEFMatrix(const Cavity & cav, const IGreensFunction & gf_i, const IGreensFunction & gf_o)
{
  // The total size of the cavity
  PCMSolverIndex cavitySize = cav.size();
  // The number of irreps in the group
  int nrBlocks = cav.pointGroup().nrIrrep();
  // The size of the irreducible portion of the cavity
  int dimBlock = cav.irreducible_size();

  // Compute SI, DI and SE, DE on the whole cavity, regardless of symmetry
  TIMER_ON("Computing SI");
  Eigen::MatrixXd SI = gf_i.singleLayer(cav.elements());
  TIMER_OFF("Computing SI");
  TIMER_ON("Computing DI");
  Eigen::MatrixXd DI = gf_i.doubleLayer(cav.elements());
  TIMER_OFF("Computing DI");
  TIMER_ON("Computing SE");
  Eigen::MatrixXd SE = gf_o.singleLayer(cav.elements());
  TIMER_OFF("Computing SE");
  TIMER_ON("Computing DE");
  Eigen::MatrixXd DE = gf_o.doubleLayer(cav.elements());
  TIMER_OFF("Computing DE");

  // Perform symmetry blocking
  // If the group is C1 avoid symmetry blocking, we will just pack the fullPCMMatrix
  // into "block diagonal" when all other manipulations are done.
  if (cav.pointGroup().nrGenerators() != 0) {
    TIMER_ON("Symmetry blocking");
    symmetryBlocking(DI, cavitySize, dimBlock, nrBlocks);
    symmetryBlocking(SI, cavitySize, dimBlock, nrBlocks);
    symmetryBlocking(DE, cavitySize, dimBlock, nrBlocks);
    symmetryBlocking(SE, cavitySize, dimBlock, nrBlocks);
    TIMER_OFF("Symmetry blocking");
  }

  Eigen::MatrixXd a = cav.elementArea().asDiagonal();
  Eigen::MatrixXd Id = Eigen::MatrixXd::Identity(cavitySize, cavitySize);

  TIMER_ON("Assemble T matrix");
  Eigen::MatrixXd T = ((2 * M_PI * Id - DE * a) * SI + SE * (2 * M_PI * Id + a * DI.adjoint().eval()));
  TIMER_OFF("Assemble T matrix");

  TIMER_ON("Assemble R matrix");
  Eigen::MatrixXd R = ((2 * M_PI * Id - DE * a) - SE * SI.ldlt().solve((2 * M_PI * Id - DI * a)));
  TIMER_OFF("Assemble R matrix");

  TIMER_ON("Assemble T^-1R matrix");
  Eigen::MatrixXd fullPCMMatrix = T.partialPivLu().solve(R);
  TIMER_OFF("Assemble T^-1R matrix");

  return fullPCMMatrix;
}

/*! \brief Builds the **isotropic** IEFPCM matrix
 *  \param[in] cav the discretized cavity
 *  \param[in] gf_i Green's function inside the cavity
 *  \param[in] epsilon permittivity outside the cavity
 *  \return the \f$ \mathbf{K} = \mathbf{T}^{-1}\mathbf{R}\mathbf{A} \f$ matrix
 *
 *  This function calculates the PCM matrix. We use the following definitions:
 *  \f[
 *     \begin{align}
 *       \mathbf{T} &=
 *      \left(2\pi\frac{\varepsilon+1}{\varepsilon-1}\mathbf{I} - \mathbf{D}_\mathrm{i}\mathbf{A}\right)\mathbf{S}_\mathrm{i} \\
 *      \mathbf{R} &=
 *      \left(2\pi\mathbf{A}^{-1} - \mathbf{D}_\mathrm{i}\right)
 *     \end{align}
 *  \f]
 *  The matrix is not symmetrized and is not symmetry packed.
 */
inline Eigen::MatrixXd isotropicIEFMatrix(const Cavity & cav, const IGreensFunction & gf_i, double epsilon)
{
  // The total size of the cavity
  PCMSolverIndex cavitySize = cav.size();
  // The number of irreps in the group
  int nrBlocks = cav.pointGroup().nrIrrep();
  // The size of the irreducible portion of the cavity
  int dimBlock = cav.irreducible_size();

  // Compute SI and DI on the whole cavity, regardless of symmetry
  TIMER_ON("Computing SI");
  Eigen::MatrixXd SI = gf_i.singleLayer(cav.elements());
  TIMER_OFF("Computing SI");
  TIMER_ON("Computing DI");
  Eigen::MatrixXd DI = gf_i.doubleLayer(cav.elements());
  TIMER_OFF("Computing DI");

  // Perform symmetry blocking
  // If the group is C1 avoid symmetry blocking, we will just pack the fullPCMMatrix
  // into "block diagonal" when all other manipulations are done.
  if (cav.pointGroup().nrGenerators() != 0) {
    TIMER_ON("Symmetry blocking");
    symmetryBlocking(DI, cavitySize, dimBlock, nrBlocks);
    symmetryBlocking(SI, cavitySize, dimBlock, nrBlocks);
    TIMER_OFF("Symmetry blocking");
  }

  Eigen::MatrixXd a = cav.elementArea().asDiagonal();
  Eigen::MatrixXd Id = Eigen::MatrixXd::Identity(cavitySize, cavitySize);

  // Tq = -Rv -> q = -(T^-1 * R)v = -Kv
  // T = (2 * M_PI * fact * aInv - DI) * a * SI; R = (2 * M_PI * aInv - DI)
  // fullPCMMatrix_ = K = T^-1 * R * a
  // 1. Form T
  double fact = (epsilon + 1.0)/(epsilon - 1.0);
  TIMER_ON("Assemble T matrix");
  Eigen::MatrixXd T = (2 * M_PI * fact * Id - DI * a) * SI;
  TIMER_OFF("Assemble T matrix");

  TIMER_ON("Assemble R matrix");
  Eigen::MatrixXd R = (2 * M_PI * Id - DI * a);
  TIMER_OFF("Assemble R matrix");

  TIMER_ON("Assemble T^-1R matrix");
  Eigen::MatrixXd fullPCMMatrix = T.partialPivLu().solve(R);
  TIMER_OFF("Assemble T^-1R matrix");

  return fullPCMMatrix;
}

/*! \brief Builds the **anisotropic** \f$ \mathbf{T}_\varepsilon \f$ matrix
 *  \param[in] cav the discretized cavity
 *  \param[in] gf_i Green's function inside the cavity
 *  \param[in] gf_o Green's function outside the cavity
 *  \return the \f$ \mathbf{T}_\varepsilon \f$ matrix
 *
 *  We use the following definition:
 *  \f[
 *      \mathbf{T}_\varepsilon =
 *      \left(2\pi\mathbf{I} - \mathbf{D}_\mathrm{e}\mathbf{A}\right)\mathbf{S}_\mathrm{i}
 *      +\mathbf{S}_\mathrm{e}\left(2\pi\mathbf{I} +
 *      \mathbf{A}\mathbf{D}_\mathrm{i}^\dagger\right)
 *  \f]
 *  The matrix is not symmetrized and is not symmetry packed.
 */
inline Eigen::MatrixXd anisotropicTEpsilon(const Cavity & cav, const IGreensFunction & gf_i, const IGreensFunction & gf_o)
{
  // The total size of the cavity
  PCMSolverIndex cavitySize = cav.size();
  // The number of irreps in the group
  int nrBlocks = cav.pointGroup().nrIrrep();
  // The size of the irreducible portion of the cavity
  int dimBlock = cav.irreducible_size();

  // Compute SI, DI and SE, DE on the whole cavity, regardless of symmetry
  Eigen::MatrixXd SI = gf_i.singleLayer(cav.elements());
  Eigen::MatrixXd DI = gf_i.doubleLayer(cav.elements());
  Eigen::MatrixXd SE = gf_o.singleLayer(cav.elements());
  Eigen::MatrixXd DE = gf_o.doubleLayer(cav.elements());

  // Perform symmetry blocking
  // If the group is C1 avoid symmetry blocking, we will just pack the matrix
  // into "block diagonal" when all other manipulations are done.
  if (cav.pointGroup().nrGenerators() != 0) {
    symmetryBlocking(DI, cavitySize, dimBlock, nrBlocks);
    symmetryBlocking(SI, cavitySize, dimBlock, nrBlocks);
    symmetryBlocking(DE, cavitySize, dimBlock, nrBlocks);
    symmetryBlocking(SE, cavitySize, dimBlock, nrBlocks);
  }

  Eigen::MatrixXd a = cav.elementArea().asDiagonal();
  Eigen::MatrixXd Id = Eigen::MatrixXd::Identity(cavitySize, cavitySize);

  // Form T
  return ((2 * M_PI * Id - DE * a) * SI + SE * (2 * M_PI * Id + a * DI.adjoint().eval()));
}

/*! \brief Builds the **isotropic** \f$ \mathbf{T}_\varepsilon \f$ matrix
 *  \param[in] cav the discretized cavity
 *  \param[in] gf_i Green's function inside the cavity
 *  \param[in] epsilon permittivity outside the cavity
 *  \return the \f$ \mathbf{T}_\varepsilon \f$ matrix
 *
 *  We use the following definition:
 *  \f[
 *      \mathbf{T}_\varepsilon =
 *      \left(2\pi\frac{\varepsilon+1}{\varepsilon-1}\mathbf{I} - \mathbf{D}_\mathrm{i}\mathbf{A}\right)\mathbf{S}_\mathrm{i}
 *  \f]
 *  The matrix is not symmetrized and is not symmetry packed.
 */
inline Eigen::MatrixXd isotropicTEpsilon(const Cavity & cav, const IGreensFunction & gf_i, double epsilon)
{
  // The total size of the cavity
  PCMSolverIndex cavitySize = cav.size();
  // The number of irreps in the group
  int nrBlocks = cav.pointGroup().nrIrrep();
  // The size of the irreducible portion of the cavity
  int dimBlock = cav.irreducible_size();

  // Compute SI, DI and SE, DE on the whole cavity, regardless of symmetry
  Eigen::MatrixXd SI = gf_i.singleLayer(cav.elements());
  Eigen::MatrixXd DI = gf_i.doubleLayer(cav.elements());

  // Perform symmetry blocking
  // If the group is C1 avoid symmetry blocking, we will just pack the matrix
  // into "block diagonal" when all other manipulations are done.
  if (cav.pointGroup().nrGenerators() != 0) {
    symmetryBlocking(DI, cavitySize, dimBlock, nrBlocks);
    symmetryBlocking(SI, cavitySize, dimBlock, nrBlocks);
  }

  Eigen::MatrixXd a = cav.elementArea().asDiagonal();
  Eigen::MatrixXd Id = Eigen::MatrixXd::Identity(cavitySize, cavitySize);

  double fact = (epsilon + 1.0)/(epsilon - 1.0);
  return (2 * M_PI * fact * Id - DI * a) * SI;
}

/*! \brief Builds the **anisotropic** \f$ \mathbf{R}_\infty \f$ matrix
 *  \param[in] cav the discretized cavity
 *  \param[in] gf_i Green's function inside the cavity
 *  \param[in] gf_o Green's function outside the cavity
 *  \return the \f$ \mathbf{R}_\infty\mathbf{A} \f$ matrix
 *
 *  We use the following definition:
 *  \f[
 *      \mathbf{R}_\infty =
 *      \left(2\pi\mathbf{A}^{-1} - \mathbf{D}_\mathrm{e}\right) -
 *      \mathbf{S}_\mathrm{e}\mathbf{S}^{-1}_\mathrm{i}\left(2\pi\mathbf{A}^{-1}-\mathbf{D}_\mathrm{i}\right)
 *  \f]
 *  The matrix is not symmetrized and is not symmetry packed.
 */
inline Eigen::MatrixXd anisotropicRinfinity(const Cavity & cav, const IGreensFunction & gf_i, const IGreensFunction & gf_o)
{
  // The total size of the cavity
  PCMSolverIndex cavitySize = cav.size();
  // The number of irreps in the group
  int nrBlocks = cav.pointGroup().nrIrrep();
  // The size of the irreducible portion of the cavity
  int dimBlock = cav.irreducible_size();

  // Compute SI, DI and SE, DE on the whole cavity, regardless of symmetry
  Eigen::MatrixXd SI = gf_i.singleLayer(cav.elements());
  Eigen::MatrixXd DI = gf_i.doubleLayer(cav.elements());
  Eigen::MatrixXd SE = gf_o.singleLayer(cav.elements());
  Eigen::MatrixXd DE = gf_o.doubleLayer(cav.elements());

  // Perform symmetry blocking
  // If the group is C1 avoid symmetry blocking, we will just pack the matrix
  // into "block diagonal" when all other manipulations are done.
  if (cav.pointGroup().nrGenerators() != 0) {
    symmetryBlocking(DI, cavitySize, dimBlock, nrBlocks);
    symmetryBlocking(SI, cavitySize, dimBlock, nrBlocks);
    symmetryBlocking(DE, cavitySize, dimBlock, nrBlocks);
    symmetryBlocking(SE, cavitySize, dimBlock, nrBlocks);
  }

  Eigen::MatrixXd a = cav.elementArea().asDiagonal();
  Eigen::MatrixXd Id = Eigen::MatrixXd::Identity(cavitySize, cavitySize);

  // Form R
  return ((2 * M_PI * Id - DE * a) - SE * SI.ldlt().solve((2 * M_PI * Id - DI * a)));
}

/*! \brief Builds the **isotropic** \f$ \mathbf{R}_\infty \f$ matrix
 *  \param[in] cav the discretized cavity
 *  \param[in] gf_i Green's function inside the cavity
 *  \return the \f$ \mathbf{R}_\infty\mathbf{A} \f$ matrix
 *
 *  We use the following definition:
 *  \f[
 *      \mathbf{R}_\infty =
 *      \left(2\pi\mathbf{A}^{-1} - \mathbf{D}_\mathrm{i}\right)
 *  \f]
 *  The matrix is not symmetrized and is not symmetry packed.
 */
inline Eigen::MatrixXd isotropicRinfinity(const Cavity & cav, const IGreensFunction & gf_i)
{
  // The total size of the cavity
  PCMSolverIndex cavitySize = cav.size();
  // The number of irreps in the group
  int nrBlocks = cav.pointGroup().nrIrrep();
  // The size of the irreducible portion of the cavity
  int dimBlock = cav.irreducible_size();

  // Compute SI, DI and SE, DE on the whole cavity, regardless of symmetry
  Eigen::MatrixXd DI = gf_i.doubleLayer(cav.elements());

  // Perform symmetry blocking
  // If the group is C1 avoid symmetry blocking, we will just pack the matrix
  // into "block diagonal" when all other manipulations are done.
  if (cav.pointGroup().nrGenerators() != 0) {
    symmetryBlocking(DI, cavitySize, dimBlock, nrBlocks);
  }

  Eigen::MatrixXd a = cav.elementArea().asDiagonal();
  Eigen::MatrixXd Id = Eigen::MatrixXd::Identity(cavitySize, cavitySize);

  return (2 * M_PI * Id - DI * a);
}


/* Copyright (C) 2012-2017 IBM Corp.
 * This program is Licensed under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *   http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. See accompanying LICENSE file.
 */

/**
 * @file PIR.cpp
 * @brief A simple PIR implementation
 */
#include <cstdint>
#include <NTL/GF2X.h>
#include <NTL/BasicThreadPool.h>
#include "intraSlot.h"
#include "matmul.h"

#if (defined(__unix__) || defined(__unix) || defined(unix))
#include <sys/time.h>
#include <sys/resource.h>
#endif

NTL_CLIENT

// Simplistic un-optimized encoding/decoding routines
static void encodeBits(GF2X& poly,
                       const vector<uint8_t>& data, long idx, long d)
{
  FHE_TIMER_START;
  for (long i=0; i<d; i++, idx++) {
    // compute byte index from bit index
    long byteIdx = idx / 8;
    if (byteIdx < lsize(data)) {
      long coef = (data[byteIdx] >> (idx % 8))&1;
      NTL::SetCoeff(poly, i, coef);
    }
  }
}
static void decodeBits(vector<uint8_t>& data, long idx,
                       const GF2X& poly, long d)
{
  FHE_TIMER_START;
  for (long i=0; i<d; i++, idx++) {
    // compute byte index from bit index
    long byteIdx = idx / 8;
    if (byteIdx < lsize(data)) {
      uint8_t coef = NTL::conv<uint>(NTL::coeff(poly,i)) & 1;
      data[byteIdx] |= (coef << (idx % 8));
    }
  }
}

static void decodeDBentry(vector<uint8_t>& entry,
                          const vector<GF2X>& encoded, long d)
{
  long dbSize = divc(lsize(encoded)*d, 8); // size in bytes

}

// Encoding a "database" in a matrix, the number of entries in the
// database is upto the size of the 1st dimension, i.e. sizeOfDim(0).
// The number of bits per entry is upto phi(m)/sizeOfDim(0).
class DBMatrix : public MatMul1D_derived<PA_GF2> {
public:
  PA_INJECT(PA_GF2)

private:
  vector< vector< vector< RX > > > data;
  const EncryptedArray& ea;

public:
  virtual ~DBMatrix() {}
  DBMatrix(const EncryptedArray& _ea, const vector< vector<uint8_t> >&db):
    ea(_ea)
  {
    RBak bak; bak.save(); ea.getAlMod().restoreContext();
    long n = ea.size();
    long d = ea.getDegree();
    long D = ea.sizeOfDimension(0);

    // vector< vector<uint8_t> > db2(lsize(db)); // sanity check
    // for (long i=0; i<lsize(db); i++)
    //   db2[i].resize(db[i].size(), 0);

    // Allocate space for the matrices
    data.resize(n/D);
    for (long k=0; k < n/D; k++) {
      data[k].resize(D);
      for (long i=0; i<D; i++)
	data[k][i].resize(D, RX()); // initialize to D empty polynomials
    }

    /* Each database entry is encoded in upto n/D partial rows, each partial
     * row has D cells of d bits each. In the picture here D=3, n/D=2.
     *
     *  (X1 X2 X3 | X4 X5 X6)  // database entry X
     *  (Y1 Y2 Y3 | Y4 Y5 Y6)  // database entry Y
     *  (Z1 Z2 Z3 | Z4 Z5 Z6)  // database entry Z
     *
     *  For example, the database entry Z is encoded in the two row vectors
     *  (Z1 Z2 Z3), (Z4 Z5 Z6). Each Zi is a degree-d binary polynomial.
     */

    for (long k=0; k<n/D; k++) { // go over row vectors

      // Encode the next d*D bits of each entry db[i]
      for (long i=0; i<D; i++) {
	for (long j=0; j<D; j++) { // encode next d bit of db[i]
          long dataIdx= (k*D+j)*d; // how many bits were already encoded
          if (dataIdx < 8*lsize(db[i])) { // more bits to encode
            encodeBits(data[k][i][j], db[i], dataIdx, d);
            // decodeBits(db2[i], dataIdx, data[k][i][j], d);
          }
	}
      }
    }
    // for (long i=0; i<lsize(db); i++)
    //   for (long j=0; j<lsize(db[i]); j++)
    //     assert(db[i][j] == db2[i][j]);
  }

  const EncryptedArray& getEA() const override { return ea; }
  bool multipleTransforms() const override { return true; }
  long getDim() const override { return 0; }

  bool get(RX& out, long i, long j, long k) const override {
    long n = ea.size();
    long D = ea.sizeOfDimension(0);

    assert(i >= 0 && i < D);
    assert(j >= 0 && j < D);
    assert(k >= 0 && k < n/D);
    if (IsZero(data[k][i][j])) return true;
    out = data[k][i][j];
    return false;
  }
};

static MatMul1D*
buildDBMatrix(const EncryptedArray& ea, const vector< vector<uint8_t> >&db)
{
  FHE_TIMER_START;
  assert (ea.getTag()==PA_GF2_tag);
  return new DBMatrix(ea, db);
}

static vector< vector<uint8_t> >*
buildRandomDB(const EncryptedArray& ea)
{
  FHE_TIMER_START;
  assert (ea.getTag()==PA_GF2_tag);
  vector< vector<uint8_t> >* v = new(vector< vector<uint8_t> >);
  v->resize(ea.sizeOfDimension(0));
  long entrySize = ea.size()/(ea.sizeOfDimension(0)*8);
  for (long i=0; i<ea.sizeOfDimension(0); i++) {
    (*v)[i].resize(entrySize);
    for (long j=0; j<entrySize; j++)
      (*v)[i][j] = NTL::RandomBits_long(8);
  }
  return v;
}

bool DoTest(const EncryptedArray& ea,
            const SecKey& secretKey, bool minimal, bool verbose)
{
  // choose a random database that fits in one ciphertext
  std::unique_ptr<vector< vector<uint8_t> > > db(buildRandomDB(ea));

  // Encode the database as a matrix
  FHE_NTIMER_START(PIR_EncodeDBasMatrix);
  std::unique_ptr< MatMul1D > mat(buildDBMatrix(ea, *db));
  MatMul1D::ExecType mat_exec(*mat, minimal);
  mat_exec.upgrade();
  FHE_NTIMER_STOP(PIR_EncodeDBasMatrix);

  // Choose a random index into the database
  long idx = NTL::RandomBnd(lsize(*db));

  // Encrypt index as a unit vector
  FHE_NTIMER_START(PIR_EncryptSelectionAsVector);
  Ctxt ctxt(secretKey);
  {vector<long> slots(ea.size(), 0);
  for (long i=0; i<ea.size(); i++) {
    if (ea.coordinate(0,i) == idx)
      slots[i]=1;
  }
  ea.encrypt(ctxt, secretKey, slots);
  }
  FHE_NTIMER_STOP(PIR_EncryptSelectionAsVector);

  // Do the matrix-vector multiply
  mat_exec.mul(ctxt);

  // Decrypt and check the result
  FHE_NTIMER_START(PIR_Decrypt_Result);

  vector<ZZX> slots;
  ea.decrypt(ctxt, secretKey, slots); // decrypt the ciphertext vector

  // maximum size of an entry in bits
  long d = ea.getDegree();
  long nOverD = ea.size()/ea.sizeOfDimension(0);
  long entrySize = (ea.size()*d) / ea.sizeOfDimension(0);
  vector<uint8_t> dbEntry(divc(entrySize,8), 0);

  // Go over the slots and put each one in its right place
  for (long i=0; i<ea.size(); i++) {
    // represent i as (ii,jj) with ii index along dim 0 and jj the rest
    std::pair<long,long> pp = ea.getAlMod().getZMStar().breakIndexByDim(i,0);
    long idxx = pp.first*ea.sizeOfDimension(0) + pp.second;
    decodeBits(dbEntry, idxx*d, conv<GF2X>(slots[i]), d);
  }
  FHE_NTIMER_STOP(PIR_Decrypt_Result);

  // check that we've got the right answer
  for (long i=0; i<std::min(lsize(dbEntry), lsize((*db)[idx])); i++)
    if (dbEntry[i] != (*db)[idx][i]) {
      cout << "Grrr@*, entry["<<i<<"]="<<((int)dbEntry[i])<<"!=db["<<idx<<"]["
           <<i<<"]="<<((int)(*db)[idx][i])<<endl;
      return false;
    }
  return true;
}

int ks_strategy = 0;
// 0 == default
// 1 == full
// 2 == BSGS
// 3 == minimal

void  TestIt(Context& context, bool verbose)
{
  if (verbose) {
    context.zMStar.printout();
    std::cout <<"  #threads="<<NTL::AvailableThreads()
              <<", security=" << context.securityLevel()<<endl<< endl;
  }

  SecKey secretKey(context);
  const PubKey& publicKey = secretKey;
  secretKey.GenSecKey(/*w=*/64); // A Hamming-weight-w secret key

  bool minimal = (ks_strategy == 3);

  // we call addSomeFrbMatrices for all strategies except minimal

  switch (ks_strategy) {
  case 0:
    addSome1DMatrices(secretKey);
    addSomeFrbMatrices(secretKey);
    break;
  case 1:
    add1DMatrices(secretKey);
    addSomeFrbMatrices(secretKey);
    break;
  case 2:
    addBSGS1DMatrices(secretKey);
    addSomeFrbMatrices(secretKey);
    break;
  case 3:
    addMinimal1DMatrices(secretKey);
    addMinimalFrbMatrices(secretKey);
    break;

   default:
     Error("bad ks_strategy");
   }
  EncryptedArray ea(context, context.alMod);
  bool okSoFar=true;

  cout << " * "<<ea.sizeOfDimension(0)<<"-entry DB, |entry|="
       << (ea.size()*ea.getDegree()) << " bits. ";
  for (long i=0; i<5; i++)
    if (!DoTest(ea, secretKey, minimal, verbose)) {
      okSoFar = false;
      break;
    }
  if (okSoFar)
    cout << "Nice!!\n\n";

  if (verbose) {
    printAllTimers(cout);
#if (defined(__unix__) || defined(__unix) || defined(unix))
      struct rusage rusage;
      getrusage( RUSAGE_SELF, &rusage );
      cout << "  rusage.ru_maxrss="<<rusage.ru_maxrss << endl;
#endif
  }

}

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

  long m=2047;
  amap.arg("m", m, "defines the cyclotomic polynomial Phi_m(X)");
  long p=2;
  long L=3;
  amap.arg("L", L, "# of levels in the modulus chain");
  long verbose=0;
  amap.arg("verbose", verbose, "print timing and other info");
  long nt=1;
  amap.arg("nt", nt, "# threads");

  amap.arg("force_bsgs", fhe_test_force_bsgs,
           "1 to force on, -1 to force off");
  amap.arg("force_hoist", fhe_test_force_hoist,
           "-1 to force off");
  amap.arg("ks_strategy", ks_strategy,
           "0: default, 1:full, 2:bsgs, 3:minimal");

  NTL::Vec<long> gens;
  amap.arg("gens", gens, "use specified vector of generators", NULL);
  amap.note("e.g., gens='[562 1871 751]'");
  NTL::Vec<long> ords;
  amap.arg("ords", ords, "use specified vector of orders", NULL);
  amap.note("e.g., ords='[4 2 -4]', negative means 'bad'");

  amap.parse(argc, argv);

  if (verbose) {
    cout << "*** matmul1D: m=" << m
	 << ", p^r=2"
	 << ", L=" << L
	 << ", nt=" << nt
	 << ", force_bsgs=" << fhe_test_force_bsgs
	 << ", force_hoist=" << fhe_test_force_hoist
	 << ", ks_strategy=" << ks_strategy
	 << endl;
   }

  vector<long> gens1, ords1;
  convert(gens1, gens);
  convert(ords1, ords);

  if (nt > 1) SetNumThreads(nt);

  setTimersOn();

  Context context(m, 2, 1, gens1, ords1);
  buildModChain(context, L, /*c=*/3);

  TestIt(context, verbose);
}


#pragma once

#include <cassert>
#include <vector>

#include <Eigen/Core>

#include <FMath/detail/Expression_Function.hpp>
#include <FMath/detail/Expression_Lambda.hpp>
#include <FMath/detail/Expression_Slice.hpp>
#include <FMath/detail/Expression_SubSet.hpp>
#include <FMath/detail/Using.hpp>

namespace FMath::detail
{
    template<typename T, typename Container = std::vector<T>>
    class Field
    {
        Container _container;

        // Assignment function which explicitly evaluates an expression
        template<typename Container2>
        void assign(const Container2 & container_from);
        void assign(const T & value);

      public:
        using value_type = T;

        ///////////// Constructors //////////////////////////////////////////////////////

        // Field with initial size
        Field(const std::size_t n) : _container(n) {}

        // Field with initial size and value
        Field(const std::size_t n, const T & value) : _container(n, value) {}

        // Field via initializer list
        Field(const std::initializer_list<T> & list) : _container(list) {}

        // Constructor for underlying container
        Field(const Container & other) : _container(other) {}

        // A Field can be constructed such as to force its evaluation.
        template<typename T2, typename R2>
        Field(const Field<T2, R2> & other) : _container(other.size())
        {
            static_assert(
                std::is_same_v<T, T2>, "FMATH USAGE ERROR: Field<> template parameter "
                                       "must be identical for assignment to work");
            if constexpr (std::is_same_v<T, T2>)
            {
                this->assign(other);
            }
        }

        // Assignment operator for Field of same type (copy assignment)
        Field & operator=(const Field & other)
        {
            assert(size() == other.size());
            this->assign(other);
            return *this;
        }

        ///////////// Assignment ////////////////////////////////////////////////////////

        // Assignment operator for Field of different type
        template<typename T2, typename R2>
        Field & operator=(const Field<T2, R2> & other)
        {
            static_assert(
                std::is_same_v<T, T2>, "FMATH USAGE ERROR: Field<> template parameter "
                                       "must be identical for assignment to work");
            if constexpr (std::is_same_v<T, T2>)
            {
                assert(size() == other.size());
                this->assign(other);
                return *this;
            }
        }

        // Assignment operator for entity
        Field & operator=(const T & value)
        {
            this->assign(value);
            return *this;
        }

        ///////////// Basics ////////////////////////////////////////////////////////////

        void resize(const std::size_t size)
        {
            _container.resize(size);
        }
        void resize(const std::size_t size, const T & value)
        {
            _container.resize(size, value);
        }

        // If the container is a std::vector, data can be retrieved as a pointer
        T * data()
        {
            static_assert(
                std::is_same_v<Container, std::vector<T>>,
                "FMATH USAGE ERROR: data() is only available for evaluated "
                "Fields, not Expressions");
            if constexpr (std::is_same_v<Container, std::vector<T>>)
            {
                return _container.data();
            }
        }

        // Size of underlying container
        auto size() const
        {
            return _container.size();
        }

        // Index operators
        T operator[](const std::size_t i) const
        {
            return _container[i];
        }
        T & operator[](const std::size_t i)
        {
            return _container[i];
        }

        // Returns the underlying data
        const Container & contents() const
        {
            return _container;
        }
        Container & contents()
        {
            return _container;
        }

        ///////////// Transformation ////////////////////////////////////////////////////

        // Re-interpretation as a reference to an Eigen::VectorX
        template<typename RefT>
        Eigen::Ref<RefT> asRef()
        {
            // Field<Vector3> to VectorX of size 3*N
            if constexpr (std::is_same_v<T, Vector3>)
                return Eigen::Ref<RefT>(
                    Eigen::Map<RefT>(this->_container[0].data(), 3 * this->size()));
            // Field<scalar> etc. to VectorX of size N
            else
                return Eigen::Ref<RefT>(Eigen::Map<RefT>(this->data(), this->size()));
        }

        ///////////// Lambda ////////////////////////////////////////////////////////////

        // Applies a given lambda to every entry of the Field.
        // The lambda is passed the index and the entry corresponding to the index.
        // This function is applied immediately, not on assignment.
        template<typename Lambda>
        void apply_lambda(const Lambda & lambda)
        {
            static_assert(
                std::is_convertible<
                    Lambda, std::function<void(std::size_t, const T &)>>::value,
                "FMATH USAGE ERROR: you cannot use apply_lambda with a type that "
                "is not convertible to std::function<void(std::size_t, const T &)>.");
            if constexpr (
                std::is_convertible<
                    Lambda, std::function<void(std::size_t, const T &)>>::value)
            {
                this->assign(Field<T, FieldLambda<T, Container, Lambda>>(
                    FieldLambda<T, Container, Lambda>(this->contents(), lambda)));
            }
        }

        // Applies a given lambda to every entry of the Field.
        // The lambda is passed the index and the entry corresponding to the index.
        template<typename Lambda>
        auto applied_lambda(const Lambda & lambda)
        {
            static_assert(
                std::is_convertible<
                    Lambda, std::function<void(std::size_t, const T &)>>::value,
                "FMATH USAGE ERROR: you cannot use applied_lambda with a type that "
                "is not convertible to std::function<void(std::size_t, const T &)>.");
            if constexpr (
                std::is_convertible<
                    Lambda, std::function<void(std::size_t, const T &)>>::value)
            {
                return Field<T, FieldLambda<T, Container, Lambda>>(
                    FieldLambda<T, Container, Lambda>(this->contents(), lambda));
            }
        }

        ///////////// Reductions ////////////////////////////////////////////////////////

        // Returns the sum over all entries of the Field
        T sum() const;

        // Returns the average over all entries of the Field
        T mean() const;

        // This is only valid for scalar contents
        // This will return the minimum value.
        scalar min() const;

        // This is only valid for scalar contents
        // This will return the maximum value.
        scalar max() const;

        // This is only valid for scalar contents
        // This will return the minimum and maximum value.
        std::pair<scalar, scalar> minmax() const;

        // This is only valid for Vector3 contents
        // Returns the minium and maximum value of the components of all vectorfield
        // entries
        scalar min_component() const;

        // This is only valid for Vector3 contents
        // Returns the minium and maximum value of the components of all vectorfield
        // entries
        scalar max_component() const;

        // This is only valid for Vector3 contents
        // Returns the minium and maximum value of the components of all vectorfield
        // entries
        std::pair<scalar, scalar> minmax_component() const;

        ///////////// VectorField Operations on self ////////////////////////////////////

        // For a VectorField, this returns a Field of the Vector3 norms
        auto norm() const
        {
            static_assert(
                std::is_same_v<T, Vector3>,
                "FMATH USAGE ERROR: norm() is only available on Field<Vector3>");
            if constexpr (std::is_same_v<T, Vector3>)
            {
                return Field<scalar, NormEx<T, Container>>(
                    NormEx<T, Container>(this->contents()));
            }
        }

        // For a VectorField, this returns a Field of the squared Vector3 norms
        auto squaredNorm() const
        {
            static_assert(
                std::is_same_v<T, Vector3>,
                "FMATH USAGE ERROR: squaredNorm() is only available on "
                "Field<Vector3>");
            if constexpr (std::is_same_v<T, Vector3>)
            {
                return Field<scalar, SquaredNormEx<T, Container>>(
                    SquaredNormEx<T, Container>(this->contents()));
            }
        }

        // Normalizes the Vector3 entries of a VectorField to norm 1.
        // If a norm is zero, nothing is done.
        // This function is applied immediately, not on assignment.
        void normalize()
        {
            static_assert(
                std::is_same_v<T, Vector3>,
                "FMATH USAGE ERROR: normalize() is only available on Field<Vector3>");
            if constexpr (std::is_same_v<T, Vector3>)
            {
                this->assign(Field<T, NormalizedEx<T, Container>>(
                    NormalizedEx<T, Container>(this->contents())));
            }
        }

        // Normalizes the Vector3 entries of a VectorField to norm 1.
        // If a norm is zero, nothing is done.
        auto normalized() const
        {
            static_assert(
                std::is_same_v<T, Vector3>,
                "FMATH USAGE ERROR: normalized() is only available on "
                "Field<Vector3>");
            if constexpr (std::is_same_v<T, Vector3>)
            {
                return Field<T, NormalizedEx<T, Container>>(
                    NormalizedEx<T, Container>(this->contents()));
            }
        }

        ///////////// VectorField Operations with others ////////////////////////////////

        // Element-wise dot-product between vector-fields, yielding a scalar-field
        template<typename Container2>
        auto dot(const Field<Vector3, Container2> & field) const
        {
            static_assert(
                std::is_same_v<T, Vector3>,
                "FMATH USAGE ERROR: dot() is only available on Field<Vector3>");
            if constexpr (std::is_same_v<T, Vector3>)
            {
                return Field<scalar, FieldDotFieldEx<T, Container, Container2>>(
                    FieldDotFieldEx<T, Container, Container2>(
                        this->contents(), field.contents()));
            }
        }

        // Element-wise dot-product between a vector-field and a vector, yielding a scalar-field
        auto dot(const Vector3 & vec) const
        {
            static_assert(
                std::is_same_v<T, Vector3>,
                "FMATH USAGE ERROR: dot() is only available on Field<Vector3>");
            if constexpr (std::is_same_v<T, Vector3>)
            {
                return Field<scalar, VectorDotFieldEx<T, Container>>(
                    VectorDotFieldEx<T, Container>(this->contents(), vec));
            }
        }

        // Element-wise cross-product between vector-fields, yielding a vector-field
        template<typename Container2>
        auto cross(const Field<Vector3, Container2> & field) const
        {
            static_assert(
                std::is_same_v<T, Vector3>,
                "FMATH USAGE ERROR: cross() is only available on Field<Vector3>");
            if constexpr (std::is_same_v<T, Vector3>)
            {
                return Field<Vector3, FieldCrossFieldEx<T, Container, Container2>>(
                    FieldCrossFieldEx<T, Container, Container2>(
                        this->contents(), field.contents()));
            }
        }

        // Element-wise cross-product between a vector-field and a vector, yielding a vector-field
        auto cross(const Vector3 & vec) const
        {
            static_assert(
                std::is_same_v<T, Vector3>,
                "FMATH USAGE ERROR: cross() is only available on Field<Vector3>");
            if constexpr (std::is_same_v<T, Vector3>)
            {
                return Field<Vector3, VectorCrossFieldEx<T, Container>>(
                    VectorCrossFieldEx<T, Container>(this->contents(), vec));
            }
        }

        ///////////// SubSet Extraction /////////////////////////////////////////////////

        // Extract a 1D slice of a Field
        auto slice(
            const std::size_t begin = 0, const std::optional<std::size_t> end = {},
            const std::size_t stride = 1)
        {
            return Field<T, SliceEx<T, Container>>(
                SliceEx<T, Container>(this->contents(), begin, end, stride));
        }

        // Extract a subset of a Field's values via a list of indices
        auto operator[](const std::vector<std::size_t> & indices)
        {
            return Field<T, SubSetEx<T, Container>>(
                SubSetEx<T, Container>(this->contents(), indices));
        }
    };
}


#include <scitbx/array_family/boost_python/flex_fwd.h>

#include <boost/python/class.hpp>
#include <scitbx/math/halton.h>
#include <scitbx/boost_python/iterator_wrappers.h>

namespace scitbx { namespace math {

namespace {

  struct halton_wrappers
  {
    typedef halton::halton<double> w_t;

    static void
    wrap()
    {
      using namespace boost::python;
      class_<w_t>("halton",no_init)
      .def(init<int const& > (( arg("dimension")) ))
      .def("nth_given_base", &w_t::nth_given_base)
      .def("nth_all", &w_t::nth_all)
      ;
    }
  };

  struct square_halton_sampling_wrappers
  {
    typedef halton::square_halton_sampling<double> w_t;
    static void
    wrap()
    {
       using namespace boost::python;
       class_<w_t>("square_halton_sampling",no_init)
         .def(init<
              double const&,
              double const&,
              double const&,
              double const&>(( arg("low_x"),
                               arg("high_x"),
                               arg("low_y"),
                               arg("high_y") )))
         .def("next", &w_t::next )
         .def("__next__", &w_t::next )
         .def("start", &w_t::start )
         .def("state", &w_t::state )
         .def("set_state", &w_t::set_state )
         ;

    }

  };

} // namespace <anonymous>

namespace boost_python {

  void wrap_halton()
  {
    halton_wrappers::wrap();
    square_halton_sampling_wrappers::wrap();
  }

}}} // namespace scitbx::math::boost_python


/*******************************************************************************
Copyright (c) 2011, Dr. D. Studios
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
Neither the name of the Dr. D. Studios nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/

#ifndef _PIMATH_EULER__H_
#define _PIMATH_EULER__H_

#include <boost/python.hpp>
#include <ImathQuat.h>
#include <ImathVec.h>
#include <ImathEuler.h>
#include <ImathMatrix.h>
#include <string>
#include <sstream>

namespace pimath
{
	namespace bp = boost::python;

	template<typename T>
	struct EulerBind
	{
		typedef T 									scalar_type;
		typedef Imath::Euler<T> 					euler_type;
		typedef typename euler_type::Order 			order_type;
		typedef typename euler_type::Axis 			axis_type;
		typedef typename euler_type::InputLayout 	input_layout_type;

		typedef Imath::Vec3<T> 						vec_type;
		typedef Imath::Quat<T> 						quat_type;
		typedef Imath::Matrix33<T> 					mat33_type;
		typedef Imath::Matrix44<T> 					mat44_type;

		EulerBind(const char* name)
		{
			void (euler_type::*fn_extractM33)(const mat33_type&) = &euler_type::extract;
			void (euler_type::*fn_extractM44)(const mat44_type&) = &euler_type::extract;
			void (euler_type::*fn_extractQuat)(const quat_type&) = &euler_type::extract;

			bp::scope eulerScope =
				bp::class_<euler_type, bp::bases<vec_type> >(name)
				.def(bp::init<>())
				.def(bp::init<euler_type>())
				.def(bp::init<order_type>())
				.def(bp::init<vec_type, bp::optional<order_type, input_layout_type> >())
				.def(bp::init<T, T, T, bp::optional<order_type, input_layout_type> >())
				.def(bp::init<euler_type, bp::optional<order_type> >())
				.def(bp::init<mat33_type, bp::optional<order_type> > ())
				.def(bp::init<mat44_type, bp::optional<order_type> > ())

				.def("setXYZVector", &euler_type::setXYZVector)
				.add_property("order", &euler_type::order, &euler_type::setOrder)
				.def("set", &euler_type::set)
				.def("extract", fn_extractM33)
				.def("extract", fn_extractM44)
				.def("extract", fn_extractQuat)
				.def("toMatrix33", &euler_type::toMatrix33)
				.def("toMatrix44", &euler_type::toMatrix44)
				.def("toQuat", &euler_type::toQuat)
				.def("toXYZVector", &euler_type::toXYZVector)
				.def("angleOrder", angleOrder)
				.def("angleMapping", angleMapping)
				.def("makeNear", &euler_type::makeNear)
				.def("frameStatic", &euler_type::frameStatic)
				.def("initialRepeated", &euler_type::initialRepeated)
				.def("parityEven", &euler_type::parityEven)
				.def("initialAxis", &euler_type::initialAxis)

				.def("legal", &euler_type::legal)
				.def("angleMod", &euler_type::angleMod)
				.def("simpleXYZRotation", simpleXYZRotation)
				.def("nearestRotation", nearestRotation)
				.def("nearestRotation", nearestRotation_)
				.staticmethod("legal")
				.staticmethod("angleMod")
				.staticmethod("simpleXYZRotation")
				.staticmethod("nearestRotation")
				;

			bp::enum_<order_type>("Order")
				.value("XYZ", euler_type::XYZ)
				.value("XZY", euler_type::XZY)
				.value("YZX", euler_type::YZX)
				.value("YXZ", euler_type::YXZ)
				.value("ZXY", euler_type::ZXY)
				.value("ZYX", euler_type::ZYX)

				.value("XZX", euler_type::XZX)
				.value("XYX", euler_type::XYX)
				.value("YXY", euler_type::YXY)
				.value("YZY", euler_type::YZY)
				.value("ZYZ", euler_type::ZYZ)
				.value("ZXZ", euler_type::ZXZ)

				.value("XYZr", euler_type::XYZr)
				.value("XZYr", euler_type::XZYr)
				.value("YZXr", euler_type::YZXr)
				.value("YXZr", euler_type::YXZr)
				.value("ZXYr", euler_type::ZXYr)
				.value("ZYXr", euler_type::ZYXr)

				.value("XZXr", euler_type::XZXr)
				.value("XYXr", euler_type::XYXr)
				.value("YXYr", euler_type::YXYr)
				.value("YZYr", euler_type::YZYr)
				.value("ZYZr", euler_type::ZYZr)
				.value("ZXZr", euler_type::ZXZr)
				;

			bp::enum_<axis_type>("Axis")
				.value("X", euler_type::X)
				.value("Y", euler_type::Y)
				.value("Z", euler_type::Z)
				;

			bp::enum_<input_layout_type>("InputLayout")
				.value("XYZLayout", euler_type::XYZLayout)
				.value("IJKLayout", euler_type::IJKLayout)
				;
		}

		static bp::tuple angleOrder(const euler_type& self)
		{
			int i, j, k;
			self.angleOrder(i,j,k);
			return bp::make_tuple(i,j,k);
		}

		static bp::tuple angleMapping(const euler_type& self)
		{
			int i, j, k;
			self.angleMapping(i,j,k);
			return bp::make_tuple(i,j,k);
		}

		static vec_type simpleXYZRotation(const vec_type& xyzRot, const vec_type& targetXyzRot)
		{
			vec_type v(xyzRot);
			euler_type::simpleXYZRotation(v, targetXyzRot);
			return v;
		}

		static vec_type nearestRotation(const vec_type& xyzRot, const vec_type& targetXyzRot,
			order_type order)
		{
			vec_type v(xyzRot);
			euler_type::nearestRotation(v, targetXyzRot, order);
			return v;
		}

		static vec_type nearestRotation_(const vec_type& xyzRot, const vec_type& targetXyzRot) {
			return nearestRotation(xyzRot, targetXyzRot, euler_type::XYZ);
		}
	};

}

#endif