#include <boost/program_options/options_description.hpp>
#include <boost/program_options.hpp>

#include <frovedis.hpp>
#include <frovedis/ml/lda/lda_cgs.hpp>

using namespace frovedis;

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

    use_frovedis use(argc, argv);

    using namespace boost::program_options;

    options_description opt("option");
    opt.add_options()
        ("help,h", "print help message")
        ("algorithm,g", value<std::string>(), "sampling algorithm: \n\"original\": original cgs, "
            "\n\"dp\": doc_proposal, \n\"wp\": word_proposal, \n\"cp\":cycle_proposal, \n\"sparse\": sparse_lda")
        ("model,m", value<std::string>(), "path for trained LDA model")
        ("test,t", value<std::string>(), "path for test corpus of crs matrix")
        ("perplexity", value<std::string>(), "path for output perplexity")
        ("likelihood", value<std::string>(), "path for output likelihood")
        ("test_iter", value<int>(), "number of testing iterations.")
        ("explore_iter", value<int>(), "number of iteration to explore optimal hyperparams")
        // ("delay_update", "update model after sampling for all tokens")
        ("trace", "set log level to TRACE")
        ("debug", "set log level to DEBUG")
        ("binary", "save/load file with binary")
        ("alpha", value<double>(), "alpha")
        ("beta" , value<double>(), "beta");

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

    std::string algorithm;
    std::string model_path;
    std::string test_path;
    std::string perplexity_path;
    std::string likelihood_path;
    double alpha = 0.1;
    double beta  = 0.01;
    int test_iter = 0;
    int explore_iter = 10;
    bool vectorized  = true;
    // bool delay_update = false;
    // bool save_doc_model = false;
    set_loglevel(INFO);
    bool binary = false;

#if !(defined(_SX) || defined(__ve__))
    vectorized = false;
#endif

    if(argmap.count("help")){
        std::cerr << opt << std::endl;
        finalizefrovedis(0);
    }
    if(argmap.count("algorithm")){
        algorithm = argmap["algorithm"].as<std::string>();
        if(algorithm!="original" && algorithm!="dp" && algorithm!="wp" && algorithm!="cp" && algorithm!="sparse"){
            std::cerr<<"ERROR: Algorithm "<<algorithm<<" doesn't exist!"<<std::endl;
            std::cerr << opt << std::endl;
            finalizefrovedis(1);
        }
    } else {
      std::cerr << "algorithm is not specified" << std::endl;
      std::cerr << opt << std::endl;
      finalizefrovedis(1);
    }
    if(argmap.count("model")){
      model_path = argmap["model"].as<std::string>();
    } else {
      std::cerr << "path for LDA model is not specified" << std::endl;
      std::cerr << opt << std::endl;
      finalizefrovedis(1);
    }
    if(argmap.count("test")){
      test_path = argmap["test"].as<std::string>();
    } else {
      std::cerr << "path for test corpus is not specified" << std::endl;
      std::cerr << opt << std::endl;
      finalizefrovedis(1);
    }
    if(argmap.count("alpha")) alpha = argmap["alpha"].as<double>();
    if(argmap.count("beta"))  beta = argmap["beta"].as<double>();
    if(argmap.count("test_iter")) { test_iter = argmap["test_iter"].as<int>(); }
    if(argmap.count("explore_iter")) { explore_iter = argmap["explore_iter"].as<int>(); }
    // if(argmap.count("delay_update")) { delay_update = true; }
    if (argmap.count("debug")) { set_loglevel(DEBUG); }
    if (argmap.count("trace")) { set_loglevel(TRACE); }
    if (argmap.count("binary")) { binary = true; }

    // if(delay_update && algorithm!="dp" && algorithm!="wp" && algorithm!="cp" && algorithm!="original"){
    //     std::cerr<<"WARNING: delay update for algorithm *" << algorithm << "* has not been implemented!" << " ";
    //     std::cerr<<"The model will be updated after per-token sampling!" << std::endl;
    //     delay_update = false;
    // }
    if(vectorized && algorithm!="dp" && algorithm!="wp" && algorithm!="cp" && algorithm!="original") {
        std::cerr<<"WARNING: vectorized sampling for algorithm *" << algorithm << "* has not been implemented!" << " ";
        std::cerr<<"Running original CGS" << std::endl;
        algorithm = "original";
    }

    auto data_test = binary ? make_crs_matrix_loadbinary<int32_t>(test_path) : make_crs_matrix_load<int32_t>(test_path);
    lda_model<int32_t> model;
    model.load_as(model_path, binary);
    std::vector<double> perplexity_buf;
    std::vector<double> likelihood_buf;
    lda_test<int32_t>(data_test, alpha,beta,test_iter,algorithm,explore_iter,
                      model, perplexity_buf, likelihood_buf);

    if(argmap.count("perplexity")){
        perplexity_path = argmap["perplexity"].as<std::string>();
        if (binary) {
            make_dvector_scatter(perplexity_buf).savebinary(perplexity_path);
        } else {
            make_dvector_scatter(perplexity_buf).saveline(perplexity_path);
        }
    }
    if(argmap.count("likelihood")){
        likelihood_path = argmap["likelihood"].as<std::string>();
        if (binary) {
            make_dvector_scatter(likelihood_buf).savebinary(likelihood_path);
        } else {
            make_dvector_scatter(likelihood_buf).saveline(likelihood_path);
        }
    }
}


/********************************************************************************
 * Copyright 2009 The Robotics Group, The Maersk Mc-Kinney Moller Institute,
 * Faculty of Engineering, University of Southern Denmark
 *
 * 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 "XQPController.hpp"

#include <rw/core/macros.hpp>
#include <rw/math/Math.hpp>
#include <rw/models/Device.hpp>
#include <rwlibs/algorithms/qpcontroller/QPSolver.hpp>

#include <Eigen/Core>

using namespace rwlibs::algorithms;
using namespace rw::math;
using namespace rw::kinematics;
using namespace rw::models;
using namespace rwlibs::algorithms::qpcontroller;

XQPController::XQPController (Device::Ptr device, Frame* controlFrame, const State& state,
                              double dt) :
    _device (device),
    _controlFrame (controlFrame), _state (state), _dt (dt), _dof (device->getDOF ()),
    _space (BaseFrame), _P (Eigen::MatrixXd::Identity (6, 6))
{
    _qlower = _device->getBounds ().first;
    _qupper = _device->getBounds ().second;

    _dqlimit  = _device->getVelocityLimits ();
    _ddqlimit = _device->getAccelerationLimits ();
}

XQPController::~XQPController ()
{}

void XQPController::setAccScale (double scale)
{
    _ddqlimit = scale * _device->getAccelerationLimits ();
}

void XQPController::setVelScale (double scale)
{
    _dqlimit = scale * _device->getVelocityLimits ();
}

Q XQPController::inequalitySolve (const Eigen::MatrixXd& G, const Eigen::VectorXd& b,
                                  const Eigen::VectorXd& lower, const Eigen::VectorXd& upper,
                                  const std::list< Constraint >& constraints)
{
    Eigen::MatrixXd cmat =
        Eigen::MatrixXd::Zero (2 * lower.size () + constraints.size (), lower.size ());
    Eigen::VectorXd limits (2 * lower.size () + constraints.size ());
    for (int i = 0; i < lower.size (); i++) {
        cmat (2 * i, i)     = 1;
        cmat (2 * i + 1, i) = -1;
        limits (2 * i)      = lower (i);
        limits (2 * i + 1)  = -upper (i);
    }

    size_t index = 2 * lower.size ();
    for (std::list< Constraint >::const_iterator it = constraints.begin ();
         it != constraints.end ();
         ++it) {
        const Constraint& c = *it;
        for (size_t i = 0; i < c._a.size (); i++) {
            cmat (index, i) = c._a (i);
        }
        limits (index) = c._b;
    }

    // Eigen::VectorXd qstart = (lower+upper)/2.0;
    Eigen::VectorXd qstart (Eigen::VectorXd::Zero (lower.size ()));
    // Try to start it with the minimal velocity
    for (int i = 0; i < qstart.size (); i++) {
        if (qstart (i) < lower (i))
            qstart (i) = lower (i);
        if (qstart (i) > upper (i))
            qstart (i) = upper (i);
    }
    for (std::list< Constraint >::const_iterator it = constraints.begin ();
         it != constraints.end ();
         ++it) {
        const Constraint& c = *it;
        /*if (inner_prod(c._a.m(), lower) > c._b) {
            std::cout<<"Sets start to lower"<<std::endl;
            qstart = lower;
        } else if (inner_prod(c._a.m(), upper) > c._b) {
            qstart = upper;
            std::cout<<"Sets start to upper"<<std::endl;
        } else {*/
        for (int i = 0; i < qstart.size (); i++) {
            if (c._a (i) < 0)
                qstart (i) = lower (i);
            else
                qstart (i) = upper (i);
        }
        if (c._a.e ().dot (qstart) < c._b) {
            std::cout << "Could not find a valid starting velocity" << std::endl;
            std::cout << "a = " << c._a << std::endl;
            std::cout << "b = " << c._b << std::endl;
            std::cout << "lower = " << lower << std::endl;
            std::cout << "upper = " << upper << std::endl;
            std::cout << "qstart = " << qstart << std::endl;
        }
        //  std::cout<<"None of the bounds works to start it"<<std::endl;
        //}
    }

    QPSolver::Status status;
    Eigen::VectorXd res = QPSolver::inequalitySolve (G, -1 * b, cmat, limits, qstart, status);
    if (status == QPSolver::SUBOPTIMAL)
        std::cout << "Solution appears to be suboptimal" << std::endl;
    if (status == QPSolver::FAILURE)
        std::cout << "Solution appears to be invalid" << std::endl;

    return Q (res);
}

void XQPController::calculateVelocityLimits (Eigen::VectorXd& lower, Eigen::VectorXd& upper,
                                             const Q& q, const Q& dq)
{
    Q joint_pos = q;
    Q joint_vel = dq;
    double accmin, accmax, velmin, velmax, posmin, posmax;
    double x;
    for (size_t i = 0; i < _dof; i++) {
        // For the acceleration
        accmin = _dt * (-_ddqlimit)[i] + joint_vel (i);
        accmax = _dt * _ddqlimit[i] + joint_vel (i);
        // For the velocity
        velmin = (-_dqlimit)[i];
        velmax = _dqlimit[i];
        // For the position
        // If v_current<=v_max(X)
        x = _qupper[i] - joint_pos (i);
        if (x <= 0) {
            posmax = 0;
        }
        else {
            // For qmax
            double j_x = Math::round (sqrt (1 - 8 * x / (_dt * _dt * (-_ddqlimit)[i])) / 2 - 1);
            double q_end_x =
                (x + _dt * _dt * (-_ddqlimit)[i] * (j_x * (j_x + 1)) / 2) / (_dt * (j_x + 1));
            double q_max_x = q_end_x - j_x * (-_ddqlimit)[i] * _dt;
            double X       = x - _dt * q_max_x;
            if (X <= 0) {
                posmax = 0;
            }
            else {
                double j_X =
                    Math::round (sqrt (1. - 8 * X / (_dt * _dt * (-_ddqlimit)[i])) / 2. - 1);
                double q_end_X =
                    (X + _dt * _dt * (-_ddqlimit)[i] * (j_X * (j_X + 1)) / 2) / (_dt * (j_X + 1));
                posmax = q_end_X - j_X * (-_ddqlimit)[i] * _dt;
            }
        }
        x = joint_pos (i) - _qlower[i];
        if (x <= 0) {
            posmin = 0;
        }
        else {    // For qmin
            double j_x = Math::round (sqrt (1 + 8 * x / (_dt * _dt * _ddqlimit[i])) / 2 - 1);
            double q_end_x =
                (-x + _dt * _dt * _ddqlimit[i] * (j_x * (j_x + 1)) / 2) / (_dt * (j_x + 1));
            double q_min_x = q_end_x - j_x * _ddqlimit[i] * _dt;
            double X       = x + _dt * q_min_x;
            if (X <= 0) {
                posmin = 0;
            }
            else {
                double j_X = Math::round (sqrt (1 + 8 * X / (_dt * _dt * _ddqlimit[i])) / 2 - 1);
                double q_end_X =
                    (-X + _dt * _dt * _ddqlimit[i] * (j_X * (j_X + 1)) / 2) / (_dt * (j_X + 1));
                posmin = q_end_X - j_X * _ddqlimit[i] * _dt;
            }
        }
        upper (i) = std::min (accmax, std::min (velmax, posmax));
        lower (i) = std::max (accmin, std::max (velmin, posmin));

        // Because of numerical uncertainties we need to test whether upper>lower.
        if (upper (i) < lower (i)) {
            lower (i) = upper (i);
        }
    }
}

Q XQPController::solve (const rw::math::Q& q, const rw::math::Q& dq,
                        const rw::math::VelocityScrew6D<>& tcpscrew,
                        const std::list< Constraint >& constraints)
{
    if (q.size () != _dof)
        RW_THROW ("Length of input configuration does not match the Device");

    /*(6);
    Vector3D<> linvel = tcpscrew.linear();
    EAA<> angvel = tcpscrew.angular();

    tcp_vel(0) = linvel(0);
    tcp_vel(1) = linvel(1);
    tcp_vel(2) = linvel(2);
    tcp_vel(3) = angvel.angle()*angvel.axis()(0);
    tcp_vel(4) = angvel.angle()*angvel.axis()(1);
    tcp_vel(5) = angvel.angle()*angvel.axis()(2);
    */

    _device->setQ (q, _state);
    Eigen::MatrixXd P;
    if (_space == ControlFrame) {
        Rotation3D<> rot       = inverse (_device->baseTframe (_controlFrame, _state).R ());
        Eigen::MatrixXd R      = Eigen::MatrixXd::Zero (6, 6);
        R.block< 3, 3 > (0, 0) = rot.e ();
        R.block< 3, 3 > (3, 3) = rot.e ();
        P                      = _P * R;
    }
    else {
        P = _P;
    }

    Eigen::VectorXd tcp_vel = P * tcpscrew.e ();
    Eigen::MatrixXd jac     = P * _device->baseJframe (_controlFrame, _state).e ();

    // trim jacobian to remove world z-rotation
    //    matrix_range<Eigen::MatrixXd > jac(jac6, range(0,6), range(0, jac6.size2()));

    Eigen::MatrixXd A = jac.transpose () * jac;
    Eigen::VectorXd b = jac.transpose () * tcp_vel;

    Eigen::VectorXd lower (_dof);
    Eigen::VectorXd upper (_dof);

    calculateVelocityLimits (lower, upper, q, dq);
    Q sol1 = inequalitySolve (A, b, lower, upper, constraints);

    return sol1;
}

void XQPController::setProjection (const Eigen::MatrixXd& P, ProjectionFrame space)
{
    _P     = P;
    _space = space;
}


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

  @copyright 2017 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)
*/
//==================================================================================================
#ifndef BOOST_SIMD_FUNCTION_FMASUBADD_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_FMASUBADD_HPP_INCLUDED

namespace boost { namespace simd
{
  /*!
    @ingroup group-swar
    @ingroup swar-fmasubadd

    Computes the interleaved sum/difference of two the product of two values with a
    third one

    @par Header <boost/simd/function/fmasubadd.hpp>
    @par Example:

    @snippet fmasubadd.cpp fmasubadd

    @par Possible output:

    @snippet fmasubadd.txt fmasubadd

  **/
} }

#include <boost/simd/function/scalar/fmasubadd.hpp>
#include <boost/simd/function/simd/fmasubadd.hpp>

#endif


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

// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2013 Adam Wulkiewicz, Lodz, Poland.

// This file was modified by Oracle on 2013, 2014, 2016, 2017.
// Modifications copyright (c) 2013-2017 Oracle and/or its affiliates.
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.

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

#ifndef BOOST_GEOMETRY_STRATEGY_SPHERICAL_POINT_IN_POLY_WINDING_HPP
#define BOOST_GEOMETRY_STRATEGY_SPHERICAL_POINT_IN_POLY_WINDING_HPP

#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/coordinate_system.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/core/tags.hpp>

#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/select_calculation_type.hpp>
#include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>

#include <boost/geometry/strategies/covered_by.hpp>
#include <boost/geometry/strategies/side.hpp>
#include <boost/geometry/strategies/spherical/ssf.hpp>
#include <boost/geometry/strategies/within.hpp>

namespace boost { namespace geometry
{

namespace strategy { namespace within
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail
{

template
<
    typename Point,
    typename PointOfSegment = Point,
    typename SideStrategy = typename strategy::side::services::default_strategy
                                <
                                    typename cs_tag<Point>::type
                                >::type,
    typename CalculationType = void
>
class spherical_winding_base
{
    typedef typename select_calculation_type
        <
            Point,
            PointOfSegment,
            CalculationType
        >::type calculation_type;

    typedef typename coordinate_system<Point>::type::units units_t;
    typedef math::detail::constants_on_spheroid<calculation_type, units_t> constants;

    /*! subclass to keep state */
    class counter
    {
        int m_count;
        //int m_count_n;
        int m_count_s;
        int m_raw_count;
        int m_raw_count_anti;
        bool m_touches;

        inline int code() const
        {
            if (m_touches)
            {
                return 0;
            }

            if (m_raw_count != 0 && m_raw_count_anti != 0)
            {
                if (m_raw_count > 0) // right, wrap around south pole
                {
                    return (m_count + m_count_s) == 0 ? -1 : 1;
                }
                else // left, wrap around north pole
                {
                    //return (m_count + m_count_n) == 0 ? -1 : 1;
                    // m_count_n is 0
                    return m_count == 0 ? -1 : 1;
                }
            }

            return m_count == 0 ? -1 : 1;
        }

    public :
        friend class spherical_winding_base;

        inline counter()
            : m_count(0)
            //, m_count_n(0)
            , m_count_s(0)
            , m_raw_count(0)
            , m_raw_count_anti(0)
            , m_touches(false)
        {}

    };

    struct count_info
    {
        explicit count_info(int c = 0, bool ia = false)
            : count(c)
            , is_anti(ia)
        {}

        int count;
        bool is_anti;
    };

public:
    typedef typename SideStrategy::envelope_strategy_type envelope_strategy_type;

    inline envelope_strategy_type get_envelope_strategy() const
    {
        return m_side_strategy.get_envelope_strategy();
    }

    typedef typename SideStrategy::disjoint_strategy_type disjoint_strategy_type;

    inline disjoint_strategy_type get_disjoint_strategy() const
    {
        return m_side_strategy.get_disjoint_strategy();
    }

    spherical_winding_base()
    {}

    template <typename Model>
    explicit spherical_winding_base(Model const& model)
        : m_side_strategy(model)
    {}

    // Typedefs and static methods to fulfill the concept
    typedef Point point_type;
    typedef PointOfSegment segment_point_type;
    typedef counter state_type;

    inline bool apply(Point const& point,
                      PointOfSegment const& s1, PointOfSegment const& s2,
                      counter& state) const
    {
        bool eq1 = false;
        bool eq2 = false;
        bool s_antipodal = false;

        count_info ci = check_segment(point, s1, s2, state, eq1, eq2, s_antipodal);
        if (ci.count != 0)
        {
            if (! ci.is_anti)
            {
                int side = 0;
                if (ci.count == 1 || ci.count == -1)
                {
                    side = side_equal(point, eq1 ? s1 : s2, ci, s1, s2);
                }
                else // count == 2 || count == -2
                {
                    if (! s_antipodal)
                    {
                        // 1 left, -1 right
                        side = m_side_strategy.apply(s1, s2, point);
                    }
                    else
                    {
                        calculation_type const pi = constants::half_period();
                        calculation_type const s1_lat = get<1>(s1);
                        calculation_type const s2_lat = get<1>(s2);

                        side = math::sign(ci.count)
                             * (pi - s1_lat - s2_lat <= pi // segment goes through north pole
                                ? -1 // going right all points will be on right side
                                : 1); // going right all points will be on left side
                    }
                }

                if (side == 0)
                {
                    // Point is lying on segment
                    state.m_touches = true;
                    state.m_count = 0;
                    return false;
                }

                // Side is NEG for right, POS for left.
                // The count is -2 for left, 2 for right (or -1/1)
                // Side positive thus means RIGHT and LEFTSIDE or LEFT and RIGHTSIDE
                // See accompagnying figure (TODO)
                if (side * ci.count > 0)
                {
                    state.m_count += ci.count;
                }

                state.m_raw_count += ci.count;
            }
            else
            {
                // Count negated because the segment is on the other side of the globe
                // so it is reversed to match this side of the globe

                // Assuming geometry wraps around north pole, for segments on the other side of the globe
                // the point will always be RIGHT+RIGHTSIDE or LEFT+LEFTSIDE, so side*-count always < 0
                //state.m_count_n -= 0;

                // Assuming geometry wraps around south pole, for segments on the other side of the globe
                // the point will always be RIGHT+LEFTSIDE or LEFT+RIGHTSIDE, so side*-count always > 0
                state.m_count_s -= ci.count;

                state.m_raw_count_anti -= ci.count;
            }
        }
        return ! state.m_touches;
    }

    static inline int result(counter const& state)
    {
        return state.code();
    }

private:

    static inline count_info check_segment(Point const& point,
                                    PointOfSegment const& seg1,
                                    PointOfSegment const& seg2,
                                    counter& state,
                                    bool& eq1, bool& eq2, bool& s_antipodal)
    {
        if (check_touch(point, seg1, seg2, state, eq1, eq2, s_antipodal))
        {
            return count_info(0, false);
        }

        return calculate_count(point, seg1, seg2, eq1, eq2, s_antipodal);
    }

    static inline int check_touch(Point const& point,
                                  PointOfSegment const& seg1,
                                  PointOfSegment const& seg2,
                                  counter& state,
                                  bool& eq1,
                                  bool& eq2,
                                  bool& s_antipodal)
    {
        calculation_type const c0 = 0;
        calculation_type const c2 = 2;
        calculation_type const pi = constants::half_period();
        calculation_type const half_pi = pi / c2;

        calculation_type const p_lon = get<0>(point);
        calculation_type const s1_lon = get<0>(seg1);
        calculation_type const s2_lon = get<0>(seg2);
        calculation_type const p_lat = get<1>(point);
        calculation_type const s1_lat = get<1>(seg1);
        calculation_type const s2_lat = get<1>(seg2);

        // NOTE: lat in {-90, 90} and arbitrary lon
        //  it doesn't matter what lon it is if it's a pole
        //  so e.g. if one of the segment endpoints is a pole
        //  then only the other lon matters

        bool eq1_strict = longitudes_equal(s1_lon, p_lon);
        bool eq2_strict = longitudes_equal(s2_lon, p_lon);
        bool eq1_anti = false;
        bool eq2_anti = false;

        calculation_type const anti_p_lon = p_lon + (p_lon <= c0 ? pi : -pi);

        eq1 = eq1_strict // lon strictly equal to s1
            || (eq1_anti = longitudes_equal(s1_lon, anti_p_lon)) // anti-lon strictly equal to s1
            || math::equals(math::abs(s1_lat), half_pi); // s1 is pole
        eq2 = eq2_strict // lon strictly equal to s2
            || (eq2_anti = longitudes_equal(s2_lon, anti_p_lon)) // anti-lon strictly equal to s2
            || math::equals(math::abs(s2_lat), half_pi); // s2 is pole

        // segment overlapping pole
        calculation_type const s_lon_diff = math::longitude_distance_signed<units_t>(s1_lon, s2_lon);
        s_antipodal = math::equals(s_lon_diff, pi);
        if (s_antipodal)
        {
            eq1 = eq2 = eq1 || eq2;

            // segment overlapping pole and point is pole
            if (math::equals(math::abs(p_lat), half_pi))
            {
                eq1 = eq2 = true;
            }
        }

        // Both equal p -> segment vertical
        // The only thing which has to be done is check if point is ON segment
        if (eq1 && eq2)
        {
            // segment endpoints on the same sides of the globe
            if (! s_antipodal)
            {
                // p's lat between segment endpoints' lats
                if ( (s1_lat <= p_lat && s2_lat >= p_lat) || (s2_lat <= p_lat && s1_lat >= p_lat) )
                {
                    if (!eq1_anti || !eq2_anti)
                    {
                        state.m_touches = true;
                    }
                }
            }
            else
            {
                // going through north or south pole?
                if (pi - s1_lat - s2_lat <= pi)
                {
                    if ( (eq1_strict && s1_lat <= p_lat) || (eq2_strict && s2_lat <= p_lat) // north
                            || math::equals(p_lat, half_pi) ) // point on north pole
                    {
                        state.m_touches = true;
                    }
                    else if (! eq1_strict && ! eq2_strict && math::equals(p_lat, -half_pi) ) // point on south pole
                    {
                        return false;
                    }
                }
                else // south pole
                {
                    if ( (eq1_strict && s1_lat >= p_lat) || (eq2_strict && s2_lat >= p_lat) // south
                            || math::equals(p_lat, -half_pi) ) // point on south pole
                    {
                        state.m_touches = true;
                    }
                    else if (! eq1_strict && ! eq2_strict && math::equals(p_lat, half_pi) ) // point on north pole
                    {
                        return false;
                    }
                }
            }

            return true;
        }

        return false;
    }

    // Called if point is not aligned with a vertical segment
    static inline count_info calculate_count(Point const& point,
                                             PointOfSegment const& seg1,
                                             PointOfSegment const& seg2,
                                             bool eq1, bool eq2, bool s_antipodal)
    {
        // If both segment endpoints were poles below checks wouldn't be enough
        // but this means that either both are the same or that they are N/S poles
        // and therefore the segment is not valid.
        // If needed (eq1 && eq2 ? 0) could be returned

        calculation_type const c0 = 0;
        calculation_type const pi = constants::half_period();

        calculation_type const p = get<0>(point);
        calculation_type const s1 = get<0>(seg1);
        calculation_type const s2 = get<0>(seg2);

        calculation_type const s1_p = math::longitude_distance_signed<units_t>(s1, p);

        if (s_antipodal)
        {
            return count_info(s1_p < c0 ? -2 : 2, false); // choose W/E
        }

        calculation_type const s1_s2 = math::longitude_distance_signed<units_t>(s1, s2);

        if (eq1 || eq2) // Point on level s1 or s2
        {
            return count_info(s1_s2 < c0 ? -1 : 1, // choose W/E
                              longitudes_equal(p + pi, (eq1 ? s1 : s2)));
        }

        // Point between s1 and s2
        if ( math::sign(s1_p) == math::sign(s1_s2)
          && math::abs(s1_p) < math::abs(s1_s2) )
        {
            return count_info(s1_s2 < c0 ? -2 : 2, false); // choose W/E
        }

        calculation_type const s1_p_anti = math::longitude_distance_signed<units_t>(s1, p + pi);

        // Anti-Point between s1 and s2
        if ( math::sign(s1_p_anti) == math::sign(s1_s2)
          && math::abs(s1_p_anti) < math::abs(s1_s2) )
        {
            return count_info(s1_s2 < c0 ? -2 : 2, true); // choose W/E
        }

        return count_info(0, false);
    }

    // Fix for https://svn.boost.org/trac/boost/ticket/9628
    // For floating point coordinates, the <D> coordinate of a point is compared
    // with the segment's points using some EPS. If the coordinates are "equal"
    // the sides are calculated. Therefore we can treat a segment as a long areal
    // geometry having some width. There is a small ~triangular area somewhere
    // between the segment's effective area and a segment's line used in sides
    // calculation where the segment is on the one side of the line but on the
    // other side of a segment (due to the width).
    // Below picture assuming D = 1, if D = 0 horiz<->vert, E<->N, RIGHT<->UP.
    // For the s1 of a segment going NE the real side is RIGHT but the point may
    // be detected as LEFT, like this:
    //                     RIGHT
    //                 ___----->
    //                  ^      O Pt  __ __
    //                 EPS     __ __
    //                  v__ __ BUT DETECTED AS LEFT OF THIS LINE
    //             _____7
    //       _____/
    // _____/
    // In the code below actually D = 0, so segments are nearly-vertical
    // Called when the point is on the same level as one of the segment's points
    // but the point is not aligned with a vertical segment
    inline int side_equal(Point const& point,
                          PointOfSegment const& se,
                          count_info const& ci,
                          PointOfSegment const& s1, PointOfSegment const& s2) const
    {
        typedef typename coordinate_type<PointOfSegment>::type scoord_t;
        typedef typename coordinate_system<PointOfSegment>::type::units units_t;

        if (math::equals(get<1>(point), get<1>(se)))
        {
            return 0;
        }

        // Create a horizontal segment intersecting the original segment's endpoint
        // equal to the point, with the derived direction (E/W).
        PointOfSegment ss1, ss2;
        set<1>(ss1, get<1>(se));
        set<0>(ss1, get<0>(se));
        set<1>(ss2, get<1>(se));
        scoord_t ss20 = get<0>(se);
        if (ci.count > 0)
        {
            ss20 += small_angle();
        }
        else
        {
            ss20 -= small_angle();
        }
        math::normalize_longitude<units_t>(ss20);
        set<0>(ss2, ss20);

        // Check the side using this vertical segment
        return m_side_strategy.apply(ss1, ss2, point);
    }

    // 1 deg or pi/180 rad
    static inline calculation_type small_angle()
    {
        return constants::half_period() / calculation_type(180);
    };

    static inline bool longitudes_equal(calculation_type const& lon1, calculation_type const& lon2)
    {
        return math::equals(
                math::longitude_distance_signed<units_t>(lon1, lon2),
                calculation_type(0));
    }

    SideStrategy m_side_strategy;
};

} // namespace detail
#endif // DOXYGEN_NO_DETAIL

/*!
\brief Within detection using winding rule in spherical coordinate system.
\ingroup strategies
\tparam Point \tparam_point
\tparam PointOfSegment \tparam_segment_point
\tparam CalculationType \tparam_calculation

\qbk{
[heading See also]
[link geometry.reference.algorithms.within.within_3_with_strategy within (with strategy)]
}
 */
template
<
    typename Point,
    typename PointOfSegment = Point,
    typename CalculationType = void
>
class spherical_winding
    : public within::detail::spherical_winding_base
        <
            Point,
            PointOfSegment,
            side::spherical_side_formula<CalculationType>,
            CalculationType
        >
{};

#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS

namespace services
{

template <typename PointLike, typename Geometry, typename AnyTag1, typename AnyTag2>
struct default_strategy<PointLike, Geometry, AnyTag1, AnyTag2, pointlike_tag, polygonal_tag, spherical_tag, spherical_tag>
{
    typedef within::detail::spherical_winding_base
        <
            typename geometry::point_type<PointLike>::type,
            typename geometry::point_type<Geometry>::type
        > type;
};

template <typename PointLike, typename Geometry, typename AnyTag1, typename AnyTag2>
struct default_strategy<PointLike, Geometry, AnyTag1, AnyTag2, pointlike_tag, linear_tag, spherical_tag, spherical_tag>
{
    typedef within::detail::spherical_winding_base
        <
            typename geometry::point_type<PointLike>::type,
            typename geometry::point_type<Geometry>::type
        > type;
};

} // namespace services

#endif

}} // namespace strategy::within

#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
namespace strategy { namespace covered_by { namespace services
{

template <typename PointLike, typename Geometry, typename AnyTag1, typename AnyTag2>
struct default_strategy<PointLike, Geometry, AnyTag1, AnyTag2, pointlike_tag, polygonal_tag, spherical_tag, spherical_tag>
{
    typedef within::detail::spherical_winding_base
        <
            typename geometry::point_type<PointLike>::type,
            typename geometry::point_type<Geometry>::type
        > type;
};

template <typename PointLike, typename Geometry, typename AnyTag1, typename AnyTag2>
struct default_strategy<PointLike, Geometry, AnyTag1, AnyTag2, pointlike_tag, linear_tag, spherical_tag, spherical_tag>
{
    typedef within::detail::spherical_winding_base
        <
            typename geometry::point_type<PointLike>::type,
            typename geometry::point_type<Geometry>::type
        > type;
};

}}} // namespace strategy::covered_by::services
#endif

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_STRATEGY_SPHERICAL_POINT_IN_POLY_WINDING_HPP


#include <jni.h>
#include <android/log.h>

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <Eigen/Geometry>
#include <Eigen/SVD>
#include <Eigen/LU> // Required for determinant computation

using namespace std;

#define  LOG_TAG	"EigenOp"
#define  LOGI(...)	__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define  LOGE(...)	__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

extern "C"
{
	JNIEXPORT jdoubleArray JNICALL Java_it_unibo_slam_sampleconsensus_RANSAC_calculateCurrentTransformation(JNIEnv *env, jobject obj,
			jdoubleArray ref1, jdoubleArray ref2, jdoubleArray ref3, jdoubleArray query1, jdoubleArray query2, jdoubleArray query3);
	JNIEXPORT jdoubleArray JNICALL Java_it_unibo_slam_sampleconsensus_abstracts_SampleConsensus_calculateMeanTransformation(JNIEnv *env, jobject obj,
			jdoubleArray H, jdoubleArray meanReference, jdoubleArray meanQuery);
	JNIEXPORT jdoubleArray JNICALL Java_it_unibo_slam_tracker_FeatureTracker_calculateIncrementalPose(JNIEnv *env, jobject obj,
			jdoubleArray lastTrackedPose, jdoubleArray activeWindowPose);
	JNIEXPORT jdoubleArray JNICALL Java_it_unibo_slam_graph_GraphBackend_calculateRelativeTransformation(JNIEnv *env, jobject obj,
			jdoubleArray to, jdoubleArray from);
	JNIEXPORT jdoubleArray JNICALL Java_it_unibo_slam_graph_GraphBackend_calculateFrameTransformation(JNIEnv *env, jobject obj,
			jdoubleArray pivotToWorld, jdoubleArray poseFromMap);
}

JNIEXPORT jdoubleArray JNICALL Java_it_unibo_slam_sampleconsensus_RANSAC_calculateCurrentTransformation(JNIEnv *env, jobject obj,
		jdoubleArray ref1, jdoubleArray ref2, jdoubleArray ref3, jdoubleArray query1, jdoubleArray query2, jdoubleArray query3)
{
	Eigen::Matrix3d referenceMatrix, queryMatrix;
	Eigen::Isometry3d currentTransformation = Eigen::Isometry3d::Identity();

	jdoubleArray currentTransformationArray;
	currentTransformationArray = env->NewDoubleArray(16);
	double *tempCurrentTransformationArray;
	double tempRef1[3], tempRef2[3], tempRef3[3];
	double tempQuery1[3], tempQuery2[3], tempQuery3[3];

	env->GetDoubleArrayRegion(ref1, 0, 3, tempRef1);
	env->GetDoubleArrayRegion(ref2, 0, 3, tempRef2);
	env->GetDoubleArrayRegion(ref3, 0, 3, tempRef3);
	env->GetDoubleArrayRegion(query1, 0, 3, tempQuery1);
	env->GetDoubleArrayRegion(query2, 0, 3, tempQuery2);
	env->GetDoubleArrayRegion(query3, 0, 3, tempQuery3);

	referenceMatrix.row(0) = Eigen::Vector3d(tempRef1[0], tempRef1[1], tempRef1[2]);
	referenceMatrix.row(1) = Eigen::Vector3d(tempRef2[0], tempRef2[1], tempRef2[2]);
	referenceMatrix.row(2) = Eigen::Vector3d(tempRef3[0], tempRef3[1], tempRef3[2]);
	queryMatrix.col(0) = Eigen::Vector3d(tempQuery1[0], tempQuery1[1], tempQuery1[2]);
	queryMatrix.col(1) = Eigen::Vector3d(tempQuery2[0], tempQuery2[1], tempQuery2[2]);
	queryMatrix.col(2) = Eigen::Vector3d(tempQuery3[0], tempQuery3[1], tempQuery3[2]);

	// Compute centroids
	const Eigen::Vector3d cRef = (referenceMatrix.row(0) + referenceMatrix.row(1) + referenceMatrix.row(2)) * (0.33333333333333333333);
	const Eigen::Vector3d cQry = (queryMatrix.col(0) + queryMatrix.col(1) + queryMatrix.col(2)) * (0.33333333333333333333);

	referenceMatrix.row(0) -= cRef;
	referenceMatrix.row(1) -= cRef;
	referenceMatrix.row(2) -= cRef;
	queryMatrix.col(0) -= cQry;
	queryMatrix.col(1) -= cQry;
	queryMatrix.col(2) -= cQry;
	const Eigen::Matrix3d H = queryMatrix * referenceMatrix;

	// SVD
	Eigen::JacobiSVD<Eigen::Matrix3d> svd(H, Eigen::ComputeFullU | Eigen::ComputeFullV);
	Eigen::Matrix3d V = svd.matrixV();
	Eigen::Matrix3d R = V * svd.matrixU().transpose();
	if (R.determinant() < 0.0)
	{
		V.col(2) = V.col(2) * -1.0;
		R.noalias() = V * svd.matrixU().transpose();
	}

	currentTransformation = Eigen::Isometry3d(R);
	currentTransformation.translation() = cRef - R * cQry;

	tempCurrentTransformationArray = currentTransformation.data();
	env->SetDoubleArrayRegion(currentTransformationArray, 0, 16, tempCurrentTransformationArray);
	return currentTransformationArray;
}

JNIEXPORT jdoubleArray JNICALL Java_it_unibo_slam_sampleconsensus_abstracts_SampleConsensus_calculateMeanTransformation(JNIEnv *env, jobject obj,
		jdoubleArray H, jdoubleArray meanReference, jdoubleArray meanQuery)
{
	jdoubleArray currentTransformationArray;
	currentTransformationArray = env->NewDoubleArray(16);
	double *tempCurrentTransformationArray;
	double tempH[9];
	double tempMeanReference[3];
	double tempMeanQuery[3];

	env->GetDoubleArrayRegion(H, 0, 9, tempH);
	env->GetDoubleArrayRegion(meanReference, 0, 3, tempMeanReference);
	env->GetDoubleArrayRegion(meanQuery, 0, 3, tempMeanQuery);

	const Eigen::Map<Eigen::Matrix3d> HMap(tempH);
	const Eigen::Vector3d meanReferenceEigen(tempMeanReference[0], tempMeanReference[1], tempMeanReference[2]);
	const Eigen::Vector3d meanQueryEigen(tempMeanQuery[0], tempMeanQuery[1], tempMeanQuery[2]);

	// SVD
	Eigen::JacobiSVD<Eigen::Matrix3d> svd(HMap, Eigen::ComputeFullU | Eigen::ComputeFullV);
	Eigen::Matrix3d V = svd.matrixV();
	Eigen::Matrix3d R = V * svd.matrixU().transpose();
	if (R.determinant() < 0.0)
	{
		V.col(2) = V.col(2) * -1.0;
		R.noalias() = V * svd.matrixU().transpose();
	}

	Eigen::Isometry3d currentTransformation = Eigen::Isometry3d::Identity();
	currentTransformation.rotate(R);
	currentTransformation.translation() = meanReferenceEigen - R * meanQueryEigen;

	tempCurrentTransformationArray = currentTransformation.data();
	env->SetDoubleArrayRegion(currentTransformationArray, 0, 16, tempCurrentTransformationArray);
	return currentTransformationArray;
}

JNIEXPORT jdoubleArray JNICALL Java_it_unibo_slam_tracker_FeatureTracker_calculateIncrementalPose(JNIEnv *env, jobject obj,
		jdoubleArray lastTrackedPose, jdoubleArray activeWindowPose)
{
	jdoubleArray incrementalPoseArray;
	incrementalPoseArray = env->NewDoubleArray(16);
	double *tempIncrementalPoseArray;
	double tempLastTrackedPose[16];
	double tempActiveWindowPose[16];

	env->GetDoubleArrayRegion(lastTrackedPose, 0, 16, tempLastTrackedPose);
	env->GetDoubleArrayRegion(activeWindowPose, 0, 16, tempActiveWindowPose);

	const Eigen::Isometry3d lastTrackedPoseEigen(Eigen::Matrix4d::Map(tempLastTrackedPose));
	const Eigen::Isometry3d activeWindowPoseEigen(Eigen::Matrix4d::Map(tempActiveWindowPose));

	Eigen::Isometry3d incrementalPose = lastTrackedPoseEigen.inverse() * activeWindowPoseEigen;

	tempIncrementalPoseArray = incrementalPose.data();
	env->SetDoubleArrayRegion(incrementalPoseArray, 0, 16, tempIncrementalPoseArray);
	return incrementalPoseArray;
}

JNIEXPORT jdoubleArray JNICALL Java_it_unibo_slam_graph_GraphBackend_calculateRelativeTransformation(JNIEnv *env, jobject obj,
		jdoubleArray to, jdoubleArray from)
{
	jdoubleArray relativeTransformationArray;
	relativeTransformationArray = env->NewDoubleArray(16);
	double *tempRelativeTransformationArray;
	double tempTo[16];
	double tempFrom[16];

	env->GetDoubleArrayRegion(to, 0, 16, tempTo);
	env->GetDoubleArrayRegion(from, 0, 16, tempFrom);

	const Eigen::Isometry3d toEigen(Eigen::Matrix4d::Map(tempTo));
	const Eigen::Isometry3d fromEigen(Eigen::Matrix4d::Map(tempFrom));

	Eigen::Isometry3d relativeTransformation = toEigen.inverse() * fromEigen;

	tempRelativeTransformationArray = relativeTransformation.data();
	env->SetDoubleArrayRegion(relativeTransformationArray, 0, 16, tempRelativeTransformationArray);
	return relativeTransformationArray;
}

JNIEXPORT jdoubleArray JNICALL Java_it_unibo_slam_graph_GraphBackend_calculateFrameTransformation(JNIEnv *env, jobject obj,
		jdoubleArray pivotToWorld, jdoubleArray poseFromMap)
{
	jdoubleArray frameTransformationArray;
	frameTransformationArray = env->NewDoubleArray(16);
	double *tempFrameTransformationArray;
	double tempPivotToWorld[16];
	double tempPoseFromMap[16];

	env->GetDoubleArrayRegion(pivotToWorld, 0, 16, tempPivotToWorld);
	env->GetDoubleArrayRegion(poseFromMap, 0, 16, tempPoseFromMap);

	const Eigen::Isometry3d pivotToWorldEigen(Eigen::Matrix4d::Map(tempPivotToWorld));
	const Eigen::Isometry3d poseFromMapEigen(Eigen::Matrix4d::Map(tempPoseFromMap));

	Eigen::Isometry3d frameTransformation = pivotToWorldEigen * poseFromMapEigen.inverse();

	tempFrameTransformationArray = frameTransformation.data();
	env->SetDoubleArrayRegion(frameTransformationArray, 0, 16, tempFrameTransformationArray);
	return frameTransformationArray;
}


//  Copyright John Maddock 2007.

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

#include <boost/math/tools/traits.hpp>
#include <boost/math/distributions.hpp>

using namespace boost::math;

static_assert(::boost::math::tools::is_distribution<double>::value == false, "double is erroneously identified as a distribution");
static_assert(::boost::math::tools::is_distribution<int>::value == false, "int is erroneously identified as a distribution");
static_assert(::boost::math::tools::is_distribution<bernoulli>::value, "bernoulli distribution should be identified as a distribution");
static_assert(::boost::math::tools::is_distribution<beta_distribution<> >::value, "beta distribution should be identified a distribution");
static_assert(::boost::math::tools::is_distribution<binomial>::value, "binomial distribution should be identified a distribution");
static_assert(::boost::math::tools::is_distribution<cauchy>::value, "cauchy distribution should be identified a distribution");
static_assert(::boost::math::tools::is_distribution<chi_squared>::value, "chi squared distribution should be identified a distribution");
static_assert(::boost::math::tools::is_distribution<exponential>::value, "exponential distribution should be identified a distribution");
static_assert(::boost::math::tools::is_distribution<extreme_value>::value, "extreme value distribution should be identified a distribution");
static_assert(::boost::math::tools::is_distribution<fisher_f>::value, "fisher f distribution should be identified a distribution");
static_assert(::boost::math::tools::is_distribution<gamma_distribution<> >::value, "gamma distribution should be identified a distribution");
static_assert(::boost::math::tools::is_distribution<lognormal>::value, "lognormal distribution should be identified a distribution");
static_assert(::boost::math::tools::is_distribution<negative_binomial>::value, "negative distribution should be identified a distribution");
static_assert(::boost::math::tools::is_distribution<normal>::value, "normal distribution should be identified a distribution");
static_assert(::boost::math::tools::is_distribution<pareto>::value, "pareto distribution should be identified a distribution");
static_assert(::boost::math::tools::is_distribution<poisson>::value, "poisson distribution should be identified a distribution");
static_assert(::boost::math::tools::is_distribution<rayleigh>::value, "rayleigh distribution should be identified a distribution");
static_assert(::boost::math::tools::is_distribution<students_t>::value, "students t distribution should be identified a distribution");
static_assert(::boost::math::tools::is_distribution<triangular>::value, "triangular distribution should be identified a distribution");
static_assert(::boost::math::tools::is_distribution<uniform>::value, "uniform distribution should be identified a distribution");
static_assert(::boost::math::tools::is_distribution<weibull>::value, "weibull distribution should be identified a distribution");

static_assert(::boost::math::tools::is_scaled_distribution<double>::value == false, "double is erroneously identified as a scaled distribution");
static_assert(::boost::math::tools::is_scaled_distribution<int>::value == false, "int double is erroneously identified as a scaled distribution");
static_assert(::boost::math::tools::is_scaled_distribution<bernoulli>::value == false, "bernoulli distribution is erroneously identified as a scaled distribution");
static_assert(::boost::math::tools::is_scaled_distribution<beta_distribution<> >::value == false, "beta distribution is erroneously identified as a scaled distribution");
static_assert(::boost::math::tools::is_scaled_distribution<binomial>::value == false, "binomial distribution should be identified as a scaled distribution");
static_assert(::boost::math::tools::is_scaled_distribution<cauchy>::value, "cauchy distribution should be identified as a scaled distribution");
static_assert(::boost::math::tools::is_scaled_distribution<chi_squared>::value == false, "chi squared is erroneously identified as a scaled distribution");
static_assert(::boost::math::tools::is_scaled_distribution<exponential>::value == false, "exponential distribution is erroneously identified as a scaled distribution");
static_assert(::boost::math::tools::is_scaled_distribution<extreme_value>::value, "extreme value distribution should be identified as a scaled distribution");
static_assert(::boost::math::tools::is_scaled_distribution<fisher_f>::value == false, "fisher f is erroneously identified as a scaled distribution");
static_assert(::boost::math::tools::is_scaled_distribution<gamma_distribution<> >::value == false, "gamma distribution is erroneously identified as a scaled distribution");
static_assert(::boost::math::tools::is_scaled_distribution<lognormal>::value, "lognormal distribution should be identified as a scaled distribution");
static_assert(::boost::math::tools::is_scaled_distribution<negative_binomial>::value == false, "negative binomial is erroneously identified as a scaled distribution");
static_assert(::boost::math::tools::is_scaled_distribution<normal>::value, "normal distribution should be identified as a scaled distribution");
static_assert(::boost::math::tools::is_scaled_distribution<pareto>::value == false, "pareto is erroneously identified as a scaled distribution");
static_assert(::boost::math::tools::is_scaled_distribution<poisson>::value == false, "poisson is erroneously identified as a scaled distribution");
static_assert(::boost::math::tools::is_scaled_distribution<rayleigh>::value == false, "rayleigh is erroneously identified as a scaled distribution");
static_assert(::boost::math::tools::is_scaled_distribution<students_t>::value == false, "students t is erroneously identified as a scaled distribution");
static_assert(::boost::math::tools::is_scaled_distribution<triangular>::value == false, "triangular is erroneously identified as a scaled distribution");
static_assert(::boost::math::tools::is_scaled_distribution<uniform>::value == false, "uniform distribution is erroneously identified as a scaled distribution");
static_assert(::boost::math::tools::is_scaled_distribution<weibull>::value == false, "weibull distribution is erroneously identified as a scaled distribution");