// Copyright 2008 Gautam Sewani
//
// 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)

#ifndef BOOST_MATH_DISTRIBUTIONS_LOGISTIC
#define BOOST_MATH_DISTRIBUTIONS_LOGISTIC

#include <boost/math/distributions/fwd.hpp>
#include <boost/math/distributions/detail/common_error_handling.hpp>
#include <boost/math/distributions/complement.hpp>
#include <boost/math/special_functions/log1p.hpp>
#include <boost/math/constants/constants.hpp>
#include <utility>

namespace boost { namespace math {

    template <class RealType = double, class Policy = policies::policy<> >
    class logistic_distribution
    {
    public:
      typedef RealType value_type;
      typedef Policy policy_type;

      logistic_distribution(RealType l_location=0, RealType l_scale=1) // Constructor.
        : m_location(l_location), m_scale(l_scale)
      {
        static const char* function = "boost::math::logistic_distribution<%1%>::logistic_distribution";

        RealType result;
        detail::check_scale(function, l_scale, &result, Policy());
        detail::check_location(function, l_location, &result, Policy());
      }
      // Accessor functions.
      RealType scale()const
      {
        return m_scale;
      }

      RealType location()const
      {
        return m_location;
      }
    private:
      // Data members:
      RealType m_location;  // distribution location aka mu.
      RealType m_scale;  // distribution scale aka s.
    }; // class logistic_distribution

    typedef logistic_distribution<double> logistic;

    template <class RealType, class Policy>
    inline const std::pair<RealType, RealType> range(const logistic_distribution<RealType, Policy>& /* dist */)
    { // Range of permissible values for random variable x.
      using boost::math::tools::max_value;
      return std::pair<RealType, RealType>(
         std::numeric_limits<RealType>::has_infinity ? -std::numeric_limits<RealType>::infinity() : -max_value<RealType>(),
         std::numeric_limits<RealType>::has_infinity ? std::numeric_limits<RealType>::infinity() : max_value<RealType>());
    }

    template <class RealType, class Policy>
    inline const std::pair<RealType, RealType> support(const logistic_distribution<RealType, Policy>& /* dist */)
    { // Range of supported values for random variable x.
      // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero.
      using boost::math::tools::max_value;
      return std::pair<RealType, RealType>(-max_value<RealType>(), max_value<RealType>()); // - to + infinity
    }

    template <class RealType, class Policy>
    inline RealType pdf(const logistic_distribution<RealType, Policy>& dist, const RealType& x)
    {
       static const char* function = "boost::math::pdf(const logistic_distribution<%1%>&, %1%)";
       RealType scale = dist.scale();
       RealType location = dist.location();
       RealType result = 0;

       if(false == detail::check_scale(function, scale , &result, Policy()))
       {
          return result;
       }
       if(false == detail::check_location(function, location, &result, Policy()))
       {
          return result;
       }

       if((boost::math::isinf)(x))
       {
          return 0; // pdf + and - infinity is zero.
       }

       if(false == detail::check_x(function, x, &result, Policy()))
       {
          return result;
       }

       BOOST_MATH_STD_USING
       RealType exp_term = (location - x) / scale;
       if(fabs(exp_term) > tools::log_max_value<RealType>())
          return 0;
       exp_term = exp(exp_term);
       if((exp_term * scale > 1) && (exp_term > tools::max_value<RealType>() / (scale * exp_term)))
          return 1 / (scale * exp_term);
       return (exp_term) / (scale * (1 + exp_term) * (1 + exp_term));
    }

    template <class RealType, class Policy>
    inline RealType cdf(const logistic_distribution<RealType, Policy>& dist, const RealType& x)
    {
       RealType scale = dist.scale();
       RealType location = dist.location();
       RealType result = 0; // of checks.
       static const char* function = "boost::math::cdf(const logistic_distribution<%1%>&, %1%)";
       if(false == detail::check_scale(function, scale, &result, Policy()))
       {
          return result;
       }
       if(false == detail::check_location(function, location, &result, Policy()))
       {
          return result;
       }

       if((boost::math::isinf)(x))
       {
          if(x < 0) return 0; // -infinity
          return 1; // + infinity
       }

       if(false == detail::check_x(function, x, &result, Policy()))
       {
          return result;
       }
       BOOST_MATH_STD_USING
       RealType power = (location - x) / scale;
       if(power > tools::log_max_value<RealType>())
          return 0;
       if(power < -tools::log_max_value<RealType>())
          return 1;
       return 1 / (1 + exp(power));
    }

    template <class RealType, class Policy>
    inline RealType quantile(const logistic_distribution<RealType, Policy>& dist, const RealType& p)
    {
       BOOST_MATH_STD_USING
       RealType location = dist.location();
       RealType scale = dist.scale();

       static const char* function = "boost::math::quantile(const logistic_distribution<%1%>&, %1%)";

       RealType result = 0;
       if(false == detail::check_scale(function, scale, &result, Policy()))
          return result;
       if(false == detail::check_location(function, location, &result, Policy()))
          return result;
       if(false == detail::check_probability(function, p, &result, Policy()))
          return result;

       if(p == 0)
       {
          return -policies::raise_overflow_error<RealType>(function,"probability argument is 0, must be >0 and <1",Policy());
       }
       if(p == 1)
       {
          return policies::raise_overflow_error<RealType>(function,"probability argument is 1, must be >0 and <1",Policy());
       }
       //Expressions to try
       //return location+scale*log(p/(1-p));
       //return location+scale*log1p((2*p-1)/(1-p));

       //return location - scale*log( (1-p)/p);
       //return location - scale*log1p((1-2*p)/p);

       //return -scale*log(1/p-1) + location;
       return location - scale * log((1 - p) / p);
     } // RealType quantile(const logistic_distribution<RealType, Policy>& dist, const RealType& p)

    template <class RealType, class Policy>
    inline RealType cdf(const complemented2_type<logistic_distribution<RealType, Policy>, RealType>& c)
    {
       BOOST_MATH_STD_USING
       RealType location = c.dist.location();
       RealType scale = c.dist.scale();
       RealType x = c.param;
       static const char* function = "boost::math::cdf(const complement(logistic_distribution<%1%>&), %1%)";

       RealType result = 0;
       if(false == detail::check_scale(function, scale, &result, Policy()))
       {
          return result;
       }
       if(false == detail::check_location(function, location, &result, Policy()))
       {
          return result;
       }
       if((boost::math::isinf)(x))
       {
          if(x < 0) return 1; // cdf complement -infinity is unity.
          return 0; // cdf complement +infinity is zero.
       }
       if(false == detail::check_x(function, x, &result, Policy()))
       {
          return result;
       }
       RealType power = (x - location) / scale;
       if(power > tools::log_max_value<RealType>())
          return 0;
       if(power < -tools::log_max_value<RealType>())
          return 1;
       return 1 / (1 + exp(power));
    }

    template <class RealType, class Policy>
    inline RealType quantile(const complemented2_type<logistic_distribution<RealType, Policy>, RealType>& c)
    {
       BOOST_MATH_STD_USING
       RealType scale = c.dist.scale();
       RealType location = c.dist.location();
       static const char* function = "boost::math::quantile(const complement(logistic_distribution<%1%>&), %1%)";
       RealType result = 0;
       if(false == detail::check_scale(function, scale, &result, Policy()))
          return result;
       if(false == detail::check_location(function, location, &result, Policy()))
          return result;
       RealType q = c.param;
       if(false == detail::check_probability(function, q, &result, Policy()))
          return result;
       using boost::math::tools::max_value;

       if(q == 1)
       {
          return -policies::raise_overflow_error<RealType>(function,"probability argument is 1, but must be >0 and <1",Policy());
       }
       if(q == 0)
       {
          return policies::raise_overflow_error<RealType>(function,"probability argument is 0, but must be >0 and <1",Policy());
       }
       //Expressions to try
       //return location+scale*log((1-q)/q);
       return location + scale * log((1 - q) / q);

       //return location-scale*log(q/(1-q));
       //return location-scale*log1p((2*q-1)/(1-q));

       //return location+scale*log(1/q-1);
       //return location+scale*log1p(1/q-2);
    }

    template <class RealType, class Policy>
    inline RealType mean(const logistic_distribution<RealType, Policy>& dist)
    {
      return dist.location();
    } // RealType mean(const logistic_distribution<RealType, Policy>& dist)

    template <class RealType, class Policy>
    inline RealType variance(const logistic_distribution<RealType, Policy>& dist)
    {
      BOOST_MATH_STD_USING
      RealType scale = dist.scale();
      return boost::math::constants::pi<RealType>()*boost::math::constants::pi<RealType>()*scale*scale/3;
    } // RealType variance(const logistic_distribution<RealType, Policy>& dist)

    template <class RealType, class Policy>
    inline RealType mode(const logistic_distribution<RealType, Policy>& dist)
    {
      return dist.location();
    }

    template <class RealType, class Policy>
    inline RealType median(const logistic_distribution<RealType, Policy>& dist)
    {
      return dist.location();
    }
    template <class RealType, class Policy>
    inline RealType skewness(const logistic_distribution<RealType, Policy>& /*dist*/)
    {
      return 0;
    } // RealType skewness(const logistic_distribution<RealType, Policy>& dist)

    template <class RealType, class Policy>
    inline RealType kurtosis_excess(const logistic_distribution<RealType, Policy>& /*dist*/)
    {
      return static_cast<RealType>(6)/5;
    } // RealType kurtosis_excess(const logistic_distribution<RealType, Policy>& dist)

    template <class RealType, class Policy>
    inline RealType kurtosis(const logistic_distribution<RealType, Policy>& dist)
    {
      return kurtosis_excess(dist) + 3;
    } // RealType kurtosis_excess(const logistic_distribution<RealType, Policy>& dist)
  }}

// Must come at the end:
#include <boost/math/distributions/detail/derived_accessors.hpp>

#endif // BOOST_MATH_DISTRIBUTIONS_LOGISTIC


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

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

namespace scitbx { namespace math {

namespace {

  struct gauss_legendre_engine_wrappers
  {
    typedef scitbx::math::quadrature::gauss_legendre_engine<double> w_t;

    static void
    wrap()
    {
      using namespace boost::python;
      class_<w_t>("gauss_legendre_engine",no_init)
        .def(init<int const& > ((arg("n_points"))))
        .def("f", &w_t::f)
        .def("refine", &w_t::refine)
        .def("x", &w_t::x)
        .def("w", &w_t::w)
        ;
    }
  };

  struct gauss_hermite_engine_wrappers
  {
    typedef scitbx::math::quadrature::gauss_hermite_engine<double> w_t;

    static void
    wrap()
    {
      using namespace boost::python;
      class_<w_t>("gauss_hermite_engine",no_init)
        .def(init<int const& > ((arg("n_points"))))
        .def("f", &w_t::f)
        .def("refine", &w_t::refine)
        .def("x", &w_t::x)
        .def("w", &w_t::w)
        .def("w_exp_x_squared", &w_t::w_exp_x_squared)
        ;
    }
  };

  struct seven_twelve_0120_wrappers
  {
    typedef scitbx::math::quadrature::seven_twelve_0120<double> w_t;

    static void
    wrap()
    {
      using namespace boost::python;
      class_<w_t>("seven_twelve_0120",no_init)
        .def(init<> () )
        .def("coord", &w_t::coord)
        .def("weight", &w_t::weight)
        ;
    }
  };

  struct five_nine_1001_wrappers
  {
    typedef scitbx::math::quadrature::five_nine_1001<double> w_t;

    static void
    wrap()
    {
      using namespace boost::python;
      class_<w_t>("five_nine_1001",no_init)
        .def(init<> () )
        .def("coord", &w_t::coord)
        .def("weight", &w_t::weight)
        ;
    }
  };

  struct five_nine_1110_wrappers
  {
    typedef scitbx::math::quadrature::five_nine_1110<double> w_t;

    static void
    wrap()
    {
      using namespace boost::python;
      class_<w_t>("five_nine_1110",no_init)
        .def(init<> () )
        .def("coord", &w_t::coord)
        .def("weight", &w_t::weight)
        ;
    }
  };

  struct nine_twentyone_1012_wrappers
  {
    typedef scitbx::math::quadrature::nine_twentyone_1012<double> w_t;

    static void
    wrap()
    {
      using namespace boost::python;
      class_<w_t>("nine_twentyone_1012",no_init)
        .def(init<> () )
        .def("coord", &w_t::coord)
        .def("weight", &w_t::weight)
        ;
    }
  };

} // namespace <anonymous>

namespace boost_python {

  void wrap_quadrature()
  {
    gauss_legendre_engine_wrappers::wrap();
    gauss_hermite_engine_wrappers::wrap();
    seven_twelve_0120_wrappers::wrap();
    five_nine_1001_wrappers::wrap();
    five_nine_1110_wrappers::wrap();
    nine_twentyone_1012_wrappers::wrap();
  }

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


/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2015 Artem Pavlenko
 *
 * This library 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 2.1 of the License, or (at your option) any later version.
 *
 * This library 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 library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_IMAGE_FILTER_HPP
#define MAPNIK_IMAGE_FILTER_HPP

//mapnik
#include <mapnik/image_filter_types.hpp>
#include <mapnik/image_util.hpp>
#include <mapnik/util/hsl.hpp>

#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/gil/gil_all.hpp>
#pragma GCC diagnostic pop

#pragma GCC diagnostic push
#include <mapnik/warning_ignore_agg.hpp>
#include "agg_basics.h"
#include "agg_rendering_buffer.h"
#include "agg_color_rgba.h"
#include "agg_pixfmt_rgba.h"
#include "agg_scanline_u.h"
#include "agg_blur.h"
#include "agg_gradient_lut.h"
#pragma GCC diagnostic pop

// stl
#include <cmath>

// 8-bit YUV
//Y = ( (  66 * R + 129 * G +  25 * B + 128) >> 8) +  16
//U = ( ( -38 * R -  74 * G + 112 * B + 128) >> 8) + 128
//V = ( ( 112 * R -  94 * G -  18 * B + 128) >> 8) + 128

//bits_type x_gradient = (0.125f*c2 + 0.25f*c5 + 0.125f*c8)
//    - (0.125f*c0 + 0.25f*c3 + 0.125f*c6);
//bits_type y_gradient = (0.125f*c0 + 0.25f*c1 + 0.125f*c2)
//    - (0.125f*c6 + 0.25f*c7 + 0.125f*c8);

// c0 c1 c2
// c3 c4 c5
// c6 c7 c8

//sharpen
//  0 -1  0
// -1  5 -1
//  0 -1  0
//bits_type out_value = -c1 - c3 + 5.0*c4 - c5 - c7;

// edge detect
//  0  1  0
//  1 -4  1
//  0  1  0
//bits_type out_value = c1 + c3 - 4.0*c4 + c5 + c7;

//
//if (out_value < 0) out_value = 0;
//if (out_value > 255) out_value = 255;

// emboss
// -2 -1  0
// -1  1  1
//  0  1  2

// bits_type out_value = -2*c0 - c1 - c3 + c4 + c5 + c7 +  2*c8;

// blur
//float out_value = (0.1f*c0 + 0.1f*c1 + 0.1f*c2 +
//                   0.1f*c3 + 0.1f*c4 + 0.1f*c5 +
//                  0.1f*c6 + 0.1f*c7 + 0.1f*c8);

//float out_value  = std::sqrt(std::pow(x_gradient,2) + std::pow(y_gradient,2));
//float theta = std::atan2(x_gradient,y_gradient);
//if (out_value < 0.0) out_value = 0.0;
//if (out_value < 1.0) out_value = 1.0;

//float conv_matrix[]={1/3.0,1/3.0,1/3.0};

//float gaussian_1[]={0.00022923296f,0.0059770769f,0.060597949f,0.24173197f,0.38292751f,
//                    0.24173197f,0.060597949f,0.0059770769f,0.00022923296f};

//float gaussian_2[]={
//    0.00048869418f,0.0024031631f,0.0092463447f,
//   0.027839607f,0.065602221f,0.12099898f,0.17469721f,
//   0.19744757f,
//   0.17469721f,0.12099898f,0.065602221f,0.027839607f,
//   0.0092463447f,0.0024031631f,0.00048869418f
//};

//  kernel_1d_fixed<float,9> kernel(conv,4);

// color_converted_view<rgb8_pixel_t>(src_view);
//using view_t = kth_channel_view_type< 0, const rgba8_view_t>::type;

//view_t red = kth_channel_view<0>(const_view(src_view));

//kernel_1d_fixed<float,3> kernel(sharpen,0);
//convolve_rows_fixed<rgba32f_pixel_t>(src_view,kernel,src_view);
// convolve_cols_fixed<rgba32f_pixel_t>(src_view,kernel,dst_view);

namespace mapnik {  namespace filter { namespace detail {

static const float blur_matrix[] = {0.1111f,0.1111f,0.1111f,0.1111f,0.1111f,0.1111f,0.1111f,0.1111f,0.1111f};
static const float emboss_matrix[] = {-2,-1,0,-1,1,1,0,1,2};
static const float sharpen_matrix[] = {0,-1,0,-1,5,-1,0,-1,0 };
static const float edge_detect_matrix[] = {0,1,0,1,-4,1,0,1,0 };

}

using boost::gil::rgba8_image_t;
using boost::gil::rgba8_view_t;

template <typename Image>
boost::gil::rgba8_view_t rgba8_view(Image & img)
{
    using boost::gil::interleaved_view;
    using boost::gil::rgba8_pixel_t;
    return interleaved_view(img.width(), img.height(),
                            reinterpret_cast<rgba8_pixel_t*>(img.bytes()),
                            img.width() * sizeof(rgba8_pixel_t));
}

template <typename Image>
struct double_buffer
{
    boost::gil::rgba8_image_t   dst_buffer;
    boost::gil::rgba8_view_t    dst_view;
    boost::gil::rgba8_view_t    src_view;

    explicit double_buffer(Image & src)
        : dst_buffer(src.width(), src.height())
        , dst_view(view(dst_buffer))
        , src_view(rgba8_view(src)) {}

    ~double_buffer()
    {
        copy_pixels(dst_view, src_view);
    }
};

template <typename Src, typename Dst, typename Conv>
void process_channel_impl (Src const& src, Dst & dst, Conv const& k)
{
    using boost::gil::bits32f;

    bits32f out_value =
        k[0]*src[0] + k[1]*src[1] + k[2]*src[2] +
        k[3]*src[3] + k[4]*src[4] + k[5]*src[5] +
        k[6]*src[6] + k[7]*src[7] + k[8]*src[8]
        ;
    if (out_value < 0) out_value = 0;
    if (out_value > 255) out_value = 255;
    dst = out_value;
}

template <typename Src, typename Dst, typename Conv>
void process_channel (Src const&, Dst &, Conv const&)
{
}

template <typename Src, typename Dst>
void process_channel (Src const& src, Dst & dst, mapnik::filter::blur)
{
    process_channel_impl(src,dst,mapnik::filter::detail::blur_matrix);
}

template <typename Src, typename Dst>
void process_channel (Src const& src, Dst & dst, mapnik::filter::emboss)
{
    process_channel_impl(src,dst,mapnik::filter::detail::emboss_matrix);
}

template <typename Src, typename Dst>
void process_channel (Src const& src, Dst & dst, mapnik::filter::sharpen)
{
    process_channel_impl(src,dst,mapnik::filter::detail::sharpen_matrix);
}

template <typename Src, typename Dst>
void process_channel (Src const& src, Dst & dst, mapnik::filter::edge_detect)
{
    process_channel_impl(src,dst,mapnik::filter::detail::edge_detect_matrix);
}

template <typename Src, typename Dst>
void process_channel (Src const& src, Dst & dst, mapnik::filter::sobel)
{
    using boost::gil::bits32f;

    bits32f x_gradient = (src[2] + 2*src[5] + src[8])
        - (src[0] + 2*src[3] + src[6]);

    bits32f y_gradient = (src[0] + 2*src[1] + src[2])
        - (src[6] + 2*src[7] + src[8]);

    bits32f  out_value  = std::sqrt(std::pow(x_gradient,2) + std::pow(y_gradient,2));
    //bts32f theta = std::atan2(x_gradient,y_gradient);
    if (out_value < 0) out_value = 0;
    if (out_value > 255) out_value = 255;
    dst = out_value;
}

template <typename Src, typename Dst, typename Filter>
void apply_convolution_3x3(Src const& src_view, Dst & dst_view, Filter const& filter)
{
    using boost::gil::bits32f;
    using boost::gil::point2;

    // p0 p1 p2
    // p3 p4 p5
    // p6 p7 p8

    typename Src::xy_locator src_loc = src_view.xy_at(0,0);
    typename Src::xy_locator::cached_location_t loc00 = src_loc.cache_location(-1,-1);
    typename Src::xy_locator::cached_location_t loc10 = src_loc.cache_location( 0,-1);
    typename Src::xy_locator::cached_location_t loc20 = src_loc.cache_location( 1,-1);
    typename Src::xy_locator::cached_location_t loc01 = src_loc.cache_location(-1, 0);
    typename Src::xy_locator::cached_location_t loc11 = src_loc.cache_location( 0, 0);
    typename Src::xy_locator::cached_location_t loc21 = src_loc.cache_location( 1, 0);
    typename Src::xy_locator::cached_location_t loc02 = src_loc.cache_location(-1, 1);
    typename Src::xy_locator::cached_location_t loc12 = src_loc.cache_location( 0, 1);
    typename Src::xy_locator::cached_location_t loc22 = src_loc.cache_location( 1, 1);

    typename Src::x_iterator dst_it = dst_view.row_begin(0);

    // top row
    for (std::ptrdiff_t x = 0 ; x < src_view.width(); ++x)
    {
        (*dst_it)[3] = src_loc[loc11][3]; // Dst.a = Src.a
        for (std::ptrdiff_t i = 0; i < 3; ++i)
        {
            bits32f p[9];

            p[4] = src_loc[loc11][i];
            p[7] = src_loc[loc12][i];

            if (x == 0)
            {
                p[3] = p[4];
                p[6] = p[7];
            }
            else
            {
                p[3] = src_loc[loc01][i];
                p[6] = src_loc[loc02][i];
            }

            if ( x == (src_view.width())-1)
            {
                p[5] = p[4];
                p[8] = p[7];
            }
            else
            {
                p[5] = src_loc[loc21][i];
                p[8] = src_loc[loc22][i];
            }

            p[0] = p[6];
            p[1] = p[7];
            p[2] = p[8];

            process_channel(p, (*dst_it)[i], filter);
        }
        ++src_loc.x();
        ++dst_it;
    }
    // carrige-return
    src_loc += point2<std::ptrdiff_t>(-src_view.width(),1);

    // 1... height-1 rows
    for (std::ptrdiff_t y = 1; y < src_view.height()-1; ++y)
    {
        for (std::ptrdiff_t x = 0; x < src_view.width(); ++x)
        {
            (*dst_it)[3] = src_loc[loc11][3]; // Dst.a = Src.a
            for (std::ptrdiff_t i = 0; i < 3; ++i)
            {
                bits32f p[9];

                p[1] = src_loc[loc10][i];
                p[4] = src_loc[loc11][i];
                p[7] = src_loc[loc12][i];

                if (x == 0)
                {
                    p[0] = p[1];
                    p[3] = p[4];
                    p[6] = p[7];
                }
                else
                {
                    p[0] = src_loc[loc00][i];
                    p[3] = src_loc[loc01][i];
                    p[6] = src_loc[loc02][i];
                }

                if ( x == (src_view.width()) - 1)
                {
                    p[2] = p[1];
                    p[5] = p[4];
                    p[8] = p[7];
                }
                else
                {
                    p[2] = src_loc[loc20][i];
                    p[5] = src_loc[loc21][i];
                    p[8] = src_loc[loc22][i];
                }
                process_channel(p, (*dst_it)[i], filter);
            }
            ++dst_it;
            ++src_loc.x();
        }
        // carrige-return
        src_loc += point2<std::ptrdiff_t>(-src_view.width(),1);
    }

    // bottom row
    //src_loc = src_view.xy_at(0,src_view.height()-1);
    for (std::ptrdiff_t x = 0 ; x < src_view.width(); ++x)
    {
        (*dst_it)[3] = src_loc[loc11][3]; // Dst.a = Src.a
        for (std::ptrdiff_t i = 0; i < 3; ++i)
        {
            bits32f p[9];

            p[1] = src_loc[loc10][i];
            p[4] = src_loc[loc11][i];

            if (x == 0)
            {
                p[0] = p[1];
                p[3] = p[4];
            }
            else
            {
                p[0] = src_loc[loc00][i];
                p[3] = src_loc[loc01][i];
            }

            if ( x == (src_view.width())-1)
            {
                p[2] = p[1];
                p[5] = p[4];

            }
            else
            {
                p[2] = src_loc[loc20][i];
                p[5] = src_loc[loc21][i];
            }

            p[6] = p[0];
            p[7] = p[1];
            p[8] = p[2];
            process_channel(p, (*dst_it)[i], filter);
        }
        ++src_loc.x();
        ++dst_it;
    }
}

template <typename Src, typename Filter>
void apply_filter(Src & src, Filter const& filter, double /*scale_factor*/)
{
    demultiply_alpha(src);
    double_buffer<Src> tb(src);
    apply_convolution_3x3(tb.src_view, tb.dst_view, filter);
}

template <typename Src>
void apply_filter(Src & src, agg_stack_blur const& op, double scale_factor)
{
    premultiply_alpha(src);
    agg::rendering_buffer buf(src.bytes(),src.width(),src.height(), src.row_size());
    agg::pixfmt_rgba32_pre pixf(buf);
    agg::stack_blur_rgba32(pixf, op.rx * scale_factor, op.ry * scale_factor);
}

inline double channel_delta(double source, double match)
{
    if (source > match) return (source - match) / (1.0 - match);
    if (source < match) return (match - source) / match;
    return (source - match);
}

inline uint8_t apply_alpha_shift(double source, double match, double alpha)
{
    source = (((source - match) / alpha) + match) * alpha;
    return static_cast<uint8_t>(std::floor((source*255.0)+.5));
}

template <typename Src>
void apply_filter(Src & src, color_to_alpha const& op, double /*scale_factor*/)
{
    using namespace boost::gil;
    bool premultiplied = src.get_premultiplied();
    rgba8_view_t src_view = rgba8_view(src);
    double cr = static_cast<double>(op.color.red())/255.0;
    double cg = static_cast<double>(op.color.green())/255.0;
    double cb = static_cast<double>(op.color.blue())/255.0;
    for (std::ptrdiff_t y = 0; y < src_view.height(); ++y)
    {
        rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast<long>(y));
        for (std::ptrdiff_t x = 0; x < src_view.width(); ++x)
        {
            uint8_t & r = get_color(src_it[x], red_t());
            uint8_t & g = get_color(src_it[x], green_t());
            uint8_t & b = get_color(src_it[x], blue_t());
            uint8_t & a = get_color(src_it[x], alpha_t());
            double sr = static_cast<double>(r)/255.0;
            double sg = static_cast<double>(g)/255.0;
            double sb = static_cast<double>(b)/255.0;
            double sa = static_cast<double>(a)/255.0;
            // demultiply
            if (sa <= 0.0)
            {
                r = g = b = 0;
                continue;
            }
            else if (premultiplied)
            {
                sr /= sa;
                sg /= sa;
                sb /= sa;
            }
            // get that maximum color difference
            double xa = std::max(channel_delta(sr,cr),std::max(channel_delta(sg,cg),channel_delta(sb,cb)));
            if (xa > 0)
            {
                // apply difference to each channel, returning premultiplied
                // TODO - experiment with difference in hsl color space
                r = apply_alpha_shift(sr,cr,xa);
                g = apply_alpha_shift(sg,cg,xa);
                b = apply_alpha_shift(sb,cb,xa);
                // combine new alpha with original
                xa *= sa;
                a = static_cast<uint8_t>(std::floor((xa*255.0)+.5));
                // all color values must be <= alpha
                if (r>a) r=a;
                if (g>a) g=a;
                if (b>a) b=a;
            }
            else
            {
                r = g = b = a = 0;
            }
        }
    }
    // set as premultiplied
    set_premultiplied_alpha(src, true);
}

template <typename Src>
void apply_filter(Src & src, colorize_alpha const& op, double /*scale_factor*/)
{
    using namespace boost::gil;
    std::ptrdiff_t size = op.size();
    if (op.size() == 1)
    {
        // no interpolation if only one stop
        mapnik::filter::color_stop const& stop = op[0];
        mapnik::color const& c = stop.color;
        rgba8_view_t src_view = rgba8_view(src);
        for (std::ptrdiff_t y = 0; y < src_view.height(); ++y)
        {
            rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast<long>(y));
            for (std::ptrdiff_t x = 0; x < src_view.width(); ++x)
            {
                uint8_t & r = get_color(src_it[x], red_t());
                uint8_t & g = get_color(src_it[x], green_t());
                uint8_t & b = get_color(src_it[x], blue_t());
                uint8_t & a = get_color(src_it[x], alpha_t());
                if ( a > 0)
                {
                    a = (c.alpha() * a + 255) >> 8;
                    r = (c.red() * a + 255) >> 8;
                    g = (c.green() * a + 255) >> 8;
                    b = (c.blue() * a + 255) >> 8;
                }
            }
        }
        // set as premultiplied
        set_premultiplied_alpha(src, true);
    }
    else if (size > 1)
    {
        // interpolate multiple stops
        agg::gradient_lut<agg::color_interpolator<agg::rgba8> > grad_lut;
        double step = 1.0/(size-1);
        double offset = 0.0;
        for ( mapnik::filter::color_stop const& stop : op)
        {
            mapnik::color const& c = stop.color;
            double stop_offset = stop.offset;
            if (stop_offset == 0)
            {
                stop_offset = offset;
            }
            grad_lut.add_color(stop_offset, agg::rgba(c.red()/255.0,
                                                      c.green()/255.0,
                                                      c.blue()/255.0,
                                                      c.alpha()/255.0));
            offset += step;
        }
        if (grad_lut.build_lut())
        {
            rgba8_view_t src_view = rgba8_view(src);
            for (std::ptrdiff_t y = 0; y < src_view.height(); ++y)
            {
                rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast<long>(y));
                for (std::ptrdiff_t x = 0; x < src_view.width(); ++x)
                {
                    uint8_t & r = get_color(src_it[x], red_t());
                    uint8_t & g = get_color(src_it[x], green_t());
                    uint8_t & b = get_color(src_it[x], blue_t());
                    uint8_t & a = get_color(src_it[x], alpha_t());
                    if ( a > 0)
                    {
                        agg::rgba8 c = grad_lut[a];
                        a = (c.a * a + 255) >> 8;
                        r = (c.r * a + 255) >> 8;
                        g = (c.g * a + 255) >> 8;
                        b = (c.b * a + 255) >> 8;
        #if 0
                        // rainbow
                        r = 0;
                        g = 0;
                        b = 0;
                        if (a < 64)
                        {
                            g = a * 4;
                            b = 255;
                        }
                        else if (a >= 64 && a < 128)
                        {
                            g = 255;
                            b = 255 - ((a - 64) * 4);
                        }
                        else if (a >= 128 && a < 192)
                        {
                            r = (a - 128) * 4;
                            g = 255;
                        }
                        else // >= 192
                        {
                            r = 255;
                            g = 255 - ((a - 192) * 4);
                        }
                        r = (r * a + 255) >> 8;
                        g = (g * a + 255) >> 8;
                        b = (b * a + 255) >> 8;
        #endif
                    }
                }
            }
        }
        // set as premultiplied
        set_premultiplied_alpha(src, true);
    }
}

template <typename Src>
void apply_filter(Src & src, scale_hsla const& transform, double /*scale_factor*/)
{
    using namespace boost::gil;
    bool tinting = !transform.is_identity();
    bool set_alpha = !transform.is_alpha_identity();
    // todo - filters be able to report if they
    // should be run to avoid overhead of temp buffer
    if (tinting || set_alpha)
    {
        bool premultiplied = src.get_premultiplied();
        rgba8_view_t src_view = rgba8_view(src);
        for (std::ptrdiff_t y = 0; y < src_view.height(); ++y)
        {
            rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast<long>(y));
            for (std::ptrdiff_t x = 0; x < src_view.width(); ++x)
            {
                uint8_t & r = get_color(src_it[x], red_t());
                uint8_t & g = get_color(src_it[x], green_t());
                uint8_t & b = get_color(src_it[x], blue_t());
                uint8_t & a = get_color(src_it[x], alpha_t());
                double r2 = static_cast<double>(r)/255.0;
                double g2 = static_cast<double>(g)/255.0;
                double b2 = static_cast<double>(b)/255.0;
                double a2 = static_cast<double>(a)/255.0;
                // demultiply
                if (a2 <= 0.0)
                {
                    r = g = b = 0;
                    continue;
                }
                else if (premultiplied)
                {
                    r2 /= a2;
                    g2 /= a2;
                    b2 /= a2;
                }

                if (set_alpha)
                {
                    a2 = transform.a0 + (a2 * (transform.a1 - transform.a0));
                    if (a2 <= 0)
                    {
                        r = g = b = a = 0;
                        continue;
                    }
                    else if (a2 > 1)
                    {
                        a2 = 1;
                        a = 255;
                    }
                    else
                    {
                        a = static_cast<uint8_t>(std::floor((a2 * 255.0) +.5));
                    }
                }
                if (tinting)
                {
                    double h;
                    double s;
                    double l;
                    rgb2hsl(r2,g2,b2,h,s,l);
                    double h2 = transform.h0 + (h * (transform.h1 - transform.h0));
                    double s2 = transform.s0 + (s * (transform.s1 - transform.s0));
                    double l2 = transform.l0 + (l * (transform.l1 - transform.l0));
                    if (h2 > 1) { h2 = 1; }
                    else if (h2 < 0) { h2 = 0; }
                    if (s2 > 1) { s2 = 1; }
                    else if (s2 < 0) { s2 = 0; }
                    if (l2 > 1) { l2 = 1; }
                    else if (l2 < 0) { l2 = 0; }
                    hsl2rgb(h2,s2,l2,r2,g2,b2);
                }
                // premultiply
                r2 *= a2;
                g2 *= a2;
                b2 *= a2;
                r = static_cast<uint8_t>(std::floor((r2*255.0)+.5));
                g = static_cast<uint8_t>(std::floor((g2*255.0)+.5));
                b = static_cast<uint8_t>(std::floor((b2*255.0)+.5));
                // all color values must be <= alpha
                if (r>a) r=a;
                if (g>a) g=a;
                if (b>a) b=a;
            }
        }
        // set as premultiplied
        set_premultiplied_alpha(src, true);
    }
}

template <typename Src, typename ColorBlindFilter>
void color_blind_filter(Src & src, ColorBlindFilter const& op)
{
    using namespace boost::gil;
    rgba8_view_t src_view = rgba8_view(src);
    bool premultiplied = src.get_premultiplied();

    for (std::ptrdiff_t y = 0; y < src_view.height(); ++y)
    {
        rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast<long>(y));
        for (std::ptrdiff_t x = 0; x < src_view.width(); ++x)
        {
            // formula taken from boost/gil/color_convert.hpp:rgb_to_luminance
            uint8_t & r = get_color(src_it[x], red_t());
            uint8_t & g = get_color(src_it[x], green_t());
            uint8_t & b = get_color(src_it[x], blue_t());
            uint8_t & a = get_color(src_it[x], alpha_t());
            double dr = static_cast<double>(r)/255.0;
            double dg = static_cast<double>(g)/255.0;
            double db = static_cast<double>(b)/255.0;
            double da = static_cast<double>(a)/255.0;
            // demultiply
            if (da <= 0.0)
            {
                r = g = b = 0;
                continue;
            }
            else if (premultiplied)
            {
                dr /= da;
                dg /= da;
                db /= da;
            }
            // Convert source color into XYZ color space
            double pow_r = std::pow(dr, 2.2);
            double pow_g = std::pow(dg, 2.2);
            double pow_b = std::pow(db, 2.2);
            double X = (0.412424 * pow_r) + (0.357579 * pow_g) + (0.180464 * pow_b);
            double Y = (0.212656 * pow_r) + (0.715158 * pow_g) + (0.0721856 * pow_b);
            double Z = (0.0193324 * pow_r) + (0.119193 * pow_g) + (0.950444 * pow_b);
            // Convert XYZ into xyY Chromacity Coordinates (xy) and Luminance (Y)
            double chroma_x = X / (X + Y + Z);
            double chroma_y = Y / (X + Y + Z);
            // Generate the "Confusion Line" between the source color and the Confusion Point
            double m_div = chroma_x - op.x;
            if (std::abs(m_div) < (std::numeric_limits<double>::epsilon())) continue;
            double m = (chroma_y - op.y) / (chroma_x - op.x); // slope of Confusion Line
            double yint = chroma_y - chroma_x * m; // y-intercept of confusion line (x-intercept = 0.0)
            // How far the xy coords deviate from the simulation
            double m_div2 = m - op.m;
            if (std::abs(m_div2) < (std::numeric_limits<double>::epsilon())) continue;
            double deviate_x = (op.yint - yint) / (m - op.m);
            double deviate_y = (m * deviate_x) + yint;
            if (std::abs(deviate_y) < (std::numeric_limits<double>::epsilon()))
            {
                deviate_y = std::numeric_limits<double>::epsilon() * 2.0;
            }
            // Compute the simulated color's XYZ coords
            X = deviate_x * Y / deviate_y;
            Z = (1.0 - (deviate_x + deviate_y)) * Y / deviate_y;
            // Neutral grey calculated from luminance (in D65)
            double neutral_X = 0.312713 * Y / 0.329016;
            double neutral_Z = 0.358271 * Y / 0.329016;
            // Difference between simulated color and neutral grey
            double diff_X = neutral_X - X;
            double diff_Z = neutral_Z - Z;
            double diff_r = diff_X * 3.24071 + diff_Z * -0.498571; // XYZ->RGB (sRGB:D65)
            double diff_g = diff_X * -0.969258 + diff_Z * 0.0415557;
            double diff_b = diff_X * 0.0556352 + diff_Z * 1.05707;
            if (std::abs(diff_r) < (std::numeric_limits<double>::epsilon()))
            {
                diff_r = std::numeric_limits<double>::epsilon() * 2.0;
            }
            if (std::abs(diff_g) < (std::numeric_limits<double>::epsilon()))
            {
                diff_g = std::numeric_limits<double>::epsilon() * 2.0;
            }
            if (std::abs(diff_b) < (std::numeric_limits<double>::epsilon()))
            {
                diff_b = std::numeric_limits<double>::epsilon() * 2.0;
            }
            // Convert to RGB color space
            dr = X * 3.24071 + Y * -1.53726 + Z * -0.498571; // XYZ->RGB (sRGB:D65)
            dg = X * -0.969258 + Y * 1.87599 + Z * 0.0415557;
            db = X * 0.0556352 + Y * -0.203996 + Z * 1.05707;
            // Compensate simulated color towards a neutral fit in RGB space
            double fit_r = ((dr < 0.0 ? 0.0 : 1.0) - dr) / diff_r;
            double fit_g = ((dg < 0.0 ? 0.0 : 1.0) - dg) / diff_g;
            double fit_b = ((db < 0.0 ? 0.0 : 1.0) - db) / diff_b;
            double adjust = std::max( (fit_r > 1.0 || fit_r < 0.0) ? 0.0 : fit_r,
                                      (fit_g > 1.0 || fit_g < 0.0) ? 0.0 : fit_g
                                    );
            adjust = std::max((fit_b > 1.0 || fit_b < 0.0) ? 0.0 : fit_b, adjust);
            // Shift proportional to the greatest shift
            dr = dr + (adjust * diff_r);
            dg = dg + (adjust * diff_g);
            db = db + (adjust * diff_b);
            // Apply gamma correction
            dr = std::pow(dr, 1.0 / 2.2);
            dg = std::pow(dg, 1.0 / 2.2);
            db = std::pow(db, 1.0 / 2.2);
            // premultiply
            dr *= da;
            dg *= da;
            db *= da;
            // Clamp values
            if(dr < 0.0)  dr = 0.0;
            if(dr > 1.0) dr = 1.0;
            if(dg < 0.0) dg = 0.0;
            if(dg > 1.0) dg = 1.0;
            if(db < 0.0) db = 0.0;
            if(db > 1.0) db = 1.0;
            r = static_cast<uint8_t>(dr * 255.0);
            g = static_cast<uint8_t>(dg * 255.0);
            b = static_cast<uint8_t>(db * 255.0);
        }
    }
    // set as premultiplied
    set_premultiplied_alpha(src, true);
}

template <typename Src>
void apply_filter(Src & src, color_blind_protanope const& op, double /*scale_factor*/)
{
    color_blind_filter(src, op);
}

template <typename Src>
void apply_filter(Src & src, color_blind_deuteranope const& op, double /*scale_factor*/)
{
    color_blind_filter(src, op);
}

template <typename Src>
void apply_filter(Src & src, color_blind_tritanope const& op, double /*scale_factor*/)
{
    color_blind_filter(src, op);
}

template <typename Src>
void apply_filter(Src & src, gray const& /*op*/, double /*scale_factor*/)
{
    premultiply_alpha(src);
    using namespace boost::gil;

    rgba8_view_t src_view = rgba8_view(src);

    for (std::ptrdiff_t y = 0; y < src_view.height(); ++y)
    {
        rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast<long>(y));
        for (std::ptrdiff_t x = 0; x < src_view.width(); ++x)
        {
            // formula taken from boost/gil/color_convert.hpp:rgb_to_luminance
            uint8_t & r = get_color(src_it[x], red_t());
            uint8_t & g = get_color(src_it[x], green_t());
            uint8_t & b = get_color(src_it[x], blue_t());
            uint8_t   v = uint8_t((4915 * r + 9667 * g + 1802 * b + 8192) >> 14);
            r = g = b = v;
        }
    }
}

template <typename Src, typename Dst>
void x_gradient_impl(Src const& src_view, Dst const& dst_view)
{
    for (std::ptrdiff_t y = 0; y < src_view.height(); ++y)
    {
        typename Src::x_iterator src_it = src_view.row_begin(static_cast<long>(y));
        typename Dst::x_iterator dst_it = dst_view.row_begin(static_cast<long>(y));

        dst_it[0][0] = 128 + (src_it[0][0] - src_it[1][0]) / 2;
        dst_it[0][1] = 128 + (src_it[0][1] - src_it[1][1]) / 2;
        dst_it[0][2] = 128 + (src_it[0][2] - src_it[1][2]) / 2;

        dst_it[dst_view.width()-1][0] = 128 + (src_it[(src_view.width())-2][0] - src_it[(src_view.width())-1][0]) / 2;
        dst_it[dst_view.width()-1][1] = 128 + (src_it[(src_view.width())-2][1] - src_it[(src_view.width())-1][1]) / 2;
        dst_it[dst_view.width()-1][2] = 128 + (src_it[(src_view.width())-2][2] - src_it[(src_view.width())-1][2]) / 2;

        dst_it[0][3] = dst_it[(src_view.width())-1][3] = 255;

        for (std::ptrdiff_t x = 1; x < src_view.width()-1; ++x)
        {
            dst_it[x][0] = 128 + (src_it[x-1][0] - src_it[x+1][0]) / 2;
            dst_it[x][1] = 128 + (src_it[x-1][1] - src_it[x+1][1]) / 2;
            dst_it[x][2] = 128 + (src_it[x-1][2] - src_it[x+1][2]) / 2;
            dst_it[x][3] = 255;
        }
    }
}

template <typename Src>
void apply_filter(Src & src, x_gradient const& /*op*/, double /*scale_factor*/)
{
    premultiply_alpha(src);
    double_buffer<Src> tb(src);
    x_gradient_impl(tb.src_view, tb.dst_view);
}

template <typename Src>
void apply_filter(Src & src, y_gradient const& /*op*/, double /*scale_factor*/)
{
    premultiply_alpha(src);
    double_buffer<Src> tb(src);
    x_gradient_impl(rotated90ccw_view(tb.src_view),
                    rotated90ccw_view(tb.dst_view));
}

template <typename Src>
void apply_filter(Src & src, invert const& /*op*/, double /*scale_factor*/)
{
    premultiply_alpha(src);
    using namespace boost::gil;

    rgba8_view_t src_view = rgba8_view(src);

    for (std::ptrdiff_t y = 0; y < src_view.height(); ++y)
    {
        rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast<long>(y));
        for (std::ptrdiff_t x = 0; x < src_view.width(); ++x)
        {
            // we only work with premultiplied source,
            // thus all color values must be <= alpha
            uint8_t   a = get_color(src_it[x], alpha_t());
            uint8_t & r = get_color(src_it[x], red_t());
            uint8_t & g = get_color(src_it[x], green_t());
            uint8_t & b = get_color(src_it[x], blue_t());
            r = a - r;
            g = a - g;
            b = a - b;
        }
    }
}

template <typename Src>
struct filter_visitor
{
    filter_visitor(Src & src, double scale_factor=1.0)
    : src_(src),
      scale_factor_(scale_factor) {}

    template <typename T>
    void operator () (T const& filter) const
    {
        apply_filter(src_, filter, scale_factor_);
    }

    Src & src_;
    double scale_factor_;
};

struct filter_radius_visitor
{
    int & radius_;
    filter_radius_visitor(int & radius)
        : radius_(radius) {}
    template <typename T>
    void operator () (T const& /*filter*/)  const {}

    void operator () (agg_stack_blur const& op) const
    {
        if (static_cast<int>(op.rx) > radius_) radius_ = static_cast<int>(op.rx);
        if (static_cast<int>(op.ry) > radius_) radius_ = static_cast<int>(op.ry);
    }
};

template<typename Src>
void filter_image(Src & src, std::string const& filter, double scale_factor=1)
{
    std::vector<filter_type> filter_vector;
    if(!parse_image_filters(filter, filter_vector))
    {
        throw std::runtime_error("Failed to parse filter argument in filter_image: '" + filter + "'");
    }
    filter_visitor<Src> visitor(src, scale_factor);
    for (filter_type const& filter_tag : filter_vector)
    {
        util::apply_visitor(visitor, filter_tag);
    }
}

template<typename Src>
Src filter_image(Src const& src, std::string const& filter, double scale_factor=1)
{
    std::vector<filter_type> filter_vector;
    if(!parse_image_filters(filter, filter_vector))
    {
        throw std::runtime_error("Failed to parse filter argument in filter_image: '" + filter + "'");
    }
    Src new_src(src);
    filter_visitor<Src> visitor(new_src, scale_factor);
    for (filter_type const& filter_tag : filter_vector)
    {
        util::apply_visitor(visitor, filter_tag);
    }
    return new_src;
}

} // End Namespace Filter

} // End Namespace Mapnik

#endif // MAPNIK_IMAGE_FILTER_HPP


#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/complex.h>
#include <pybind11/numpy.h>
#include <boost/lexical_cast.hpp>

#include <assert.h>
#include <vector>

#include <CGAL/Delaunay_triangulation_3.h>
#include <CGAL/Delaunay_triangulation_3.h>
#include <CGAL/Triangulation_vertex_base_with_info_3.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Delaunay_triangulation_2.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Delaunay_triangulation_2.h>
#include <CGAL/Triangulation_vertex_base_with_info_2.h>

typedef CGAL::Exact_predicates_inexact_constructions_kernel            Kernel;
typedef CGAL::Triangulation_vertex_base_with_info_2<unsigned int, Kernel> Vb;
typedef CGAL::Triangulation_data_structure_2<Vb>                       Tds;
typedef CGAL::Delaunay_triangulation_2<Kernel, Tds>                    Delaunay;
typedef Kernel::Point_2 Point;

typedef CGAL::Triangulation_vertex_base_with_info_3<unsigned int, Kernel> Vb3;
typedef CGAL::Triangulation_data_structure_3<Vb3>                       Tds3;
typedef CGAL::Delaunay_triangulation_3<Kernel, Tds3>                    Delaunay3;
typedef Kernel::Point_3 Point3;

std::vector<int> c_delaunay2(std::vector<double> &x, std::vector<double> &y)
{
  int num_points = x.size();
  assert(y.size()!=num_points);
  std::vector< std::pair<Point,unsigned> > points;
  // add index information to form face table later
  for(std::size_t i = 0; i < num_points; ++i)
  {
     points.push_back( std::make_pair( Point(x[i],y[i]), i ) );
  }

  Delaunay triangulation;
  triangulation.insert(points.begin(),points.end());

  // save the face table
  int num_faces = triangulation.number_of_faces();
  std::vector<int> faces;
  faces.resize(num_faces*3);

  int i=0;
  for(Delaunay::Finite_faces_iterator fit = triangulation.finite_faces_begin();
    fit != triangulation.finite_faces_end(); ++fit) {

    Delaunay::Face_handle face = fit;
    faces[i*3]=face->vertex(0)->info();
    faces[i*3+1]=face->vertex(1)->info();
    faces[i*3+2]=face->vertex(2)->info();
    i+=1;
  }
  return faces;
}

std::vector<int> c_delaunay3(std::vector<double> &x, std::vector<double> &y, std::vector<double> &z)
{
  int num_points = x.size();
  assert(y.size()!=num_points);
  assert(z.size()!=num_points);
  std::vector< std::pair<Point3,unsigned> > points;
  // add index information to form face table later
  for(std::size_t i = 0; i < num_points; ++i)
  {
     points.push_back( std::make_pair( Point3(x[i],y[i],z[i]), i ) );
  }
  Delaunay3 triangulation;
  triangulation.insert(points.begin(),points.end());
  // save the indices of all cells
  int num_cells = triangulation.number_of_finite_cells();
  std::vector<int> cells;
  cells.resize(num_cells*4);

  int i=0;
  for(Delaunay3::Finite_cells_iterator cit = triangulation.finite_cells_begin();
    cit != triangulation.finite_cells_end(); ++cit) {

    Delaunay3::Cell_handle cell = cit;
    cells[i*4]=cell->vertex(0)->info();
    cells[i*4+1]=cell->vertex(1)->info();
    cells[i*4+2]=cell->vertex(2)->info();
    cells[i*4+3]=cell->vertex(3)->info();
    i+=1;
  }
  return cells;
}

// ----------------
// Python interface
// ----------------
// (from https://github.com/tdegeus/pybind11_examples/blob/master/04_numpy-2D_cpp-vector/example.cpp)

namespace py = pybind11;
py::array delaunay2(py::array_t<double, py::array::c_style | py::array::forcecast> x,
                    py::array_t<double, py::array::c_style | py::array::forcecast> y)
{

  // check input dimensions
  if ( x.ndim() != 1 )
    throw std::runtime_error("Input should be 2 1D NumPy arrays");
  if ( y.ndim() != 1 )
    throw std::runtime_error("Input should be 2 1D NumPy arrays");

  int num_points = x.shape()[0];

  // allocate std::vector (to pass to the C++ function)
  std::vector<double> cppx(num_points);
  std::vector<double> cppy(num_points);

  // copy py::array -> std::vector
  std::memcpy(cppx.data(),x.data(),num_points*sizeof(double));
  std::memcpy(cppy.data(),y.data(),num_points*sizeof(double));
  std::vector<int> faces = c_delaunay2(cppx, cppy);

  ssize_t              soint      = sizeof(int);
  ssize_t              num_faces = faces.size()/3;
  ssize_t              ndim      = 2;
  std::vector<ssize_t> shape     = {num_faces, 3};
  std::vector<ssize_t> strides   = {soint*3, soint};

  // return 2-D NumPy array
  return py::array(py::buffer_info(
    faces.data(),                           /* data as contiguous array  */
    sizeof(int),                          /* size of one scalar        */
    py::format_descriptor<int>::format(), /* data type                 */
    2,                                    /* number of dimensions      */
    shape,                                   /* shape of the matrix       */
    strides                                  /* strides for each axis     */
  ));
}

py::array delaunay3(py::array_t<double, py::array::c_style | py::array::forcecast> x,
                    py::array_t<double, py::array::c_style | py::array::forcecast> y,
                    py::array_t<double, py::array::c_style | py::array::forcecast> z)
{

  // check input dimensions
  if ( x.ndim() != 1 )
    throw std::runtime_error("Input should be three 1D NumPy arrays");
  if ( y.ndim() != 1 )
    throw std::runtime_error("Input should be three 1D NumPy arrays");
  if ( z.ndim() != 1 )
    throw std::runtime_error("Input should be three 1D NumPy arrays");

  int num_points = x.shape()[0];

  // allocate std::vector (to pass to the C++ function)
  std::vector<double> cppx(num_points);
  std::vector<double> cppy(num_points);
  std::vector<double> cppz(num_points);

  // copy py::array -> std::vector
  std::memcpy(cppx.data(),x.data(),num_points*sizeof(double));
  std::memcpy(cppy.data(),y.data(),num_points*sizeof(double));
  std::memcpy(cppz.data(),z.data(),num_points*sizeof(double));
  std::vector<int> cells = c_delaunay3(cppx, cppy, cppz);

  ssize_t              num_cells = cells.size()/4;
  ssize_t              ndim      = 2;
  ssize_t              soint      = sizeof(int);
  std::vector<ssize_t> shape     = {num_cells, 4};
  std::vector<ssize_t> strides   = {soint*4, soint};

  // return 2-D NumPy array
  return py::array(py::buffer_info(
    cells.data(),                           /* data as contiguous array  */
    sizeof(int),                          /* size of one scalar        */
    py::format_descriptor<int>::format(), /* data type                 */
    2,                                    /* number of dimensions      */
    shape,                                   /* shape of the matrix       */
    strides                                  /* strides for each axis     */
  ));
}

PYBIND11_MODULE(simple_cgal, m) {
    m.def("delaunay2", &delaunay2);
    m.def("delaunay3", &delaunay3);
}


#pragma once

#include <Eigen/Dense>
#include "base/array_buffer.hpp"
#include "fft/shift.hpp"

/**
 * @brief fold operation,
 *
 *
 * @param[out] dst (resized)
 *  dim == 0: width x m matrix
 *  dim == 1: n x width matrix
 * @param[in]  src   Fourier coefficients of f(x), matrix of size n x m
 * @param[in]  width
 * @param[in]  dim   dimension along which to fold
 *
 */
template <typename DERIVED1, typename DERIVED2>
void
fold(Eigen::DenseBase<DERIVED1> &dst,
     const Eigen::DenseBase<DERIVED2> &src,
     unsigned int width,
     int dim)
{
  const int ncols = src.cols();
  const int nrows = src.rows();

  typedef typename DERIVED1::Scalar numeric_t;
  typedef Eigen::Array<numeric_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> local_array_t;

  thread_local static ArrayBuffer<> buf;

  if (dim == 0) {
    assert(nrows % width == 0);
    if ((src.rows() / width) % 2 == 0) {
      const int s = nrows / width;
      auto tmp = buf.get<local_array_t>(width, ncols);
      tmp.setZero();
      for (int i = 0; i < (int)width; ++i) {
        for (int k = 0; k < s; ++k) {
          tmp.row(i) += src.row(i + k * width);
        }
      }
      // apply fftshift along dim
      fftshift(dst, tmp, dim);
    } else {
      const int s = nrows / width;
      assert(dst.rows() == width);
      assert(dst.cols() == ncols);
      dst.setZero();
      for (int i = 0; i < (int)width; ++i) {
        for (int k = 0; k < s; ++k) {
          dst.row(i) += src.row(i + k * width);
        }
      }
    }
  } else if (dim == 1) {
    assert(ncols % width == 0);
    if ((src.cols() / width) % 2 == 0) {
      const int s = ncols / width;
      auto tmp = buf.get<local_array_t>(nrows, width);
      tmp.setZero();
      for (int j = 0; j < (int)width; ++j) {
        for (int k = 0; k < s; ++k) {
          tmp.col(j) += src.col(j + k * width);
        }
      }
      // apply fftshift along dim
      fftshift(dst, tmp, dim);
    } else {
      const int s = ncols / width;
      assert(dst.rows() == nrows);
      assert(dst.cols() == width);
      dst.setZero();
      for (int j = 0; j < (int)width; ++j) {
        for (int k = 0; k < s; ++k) {
          dst.col(j) += src.col(j + k * width);
        }
      }
    }
  }
}

/**
 * @brief unfold operation.
 *
 * unfold scales src by 1/k where k := width/size(src,dim) and repeats it
 * along dimension dim such that size(src_unfolded, dim) = width.
 * If dim == 0, the rows of src_unfolded are shifted such that the middle row of
 * src
 * becomes the middle row of src_unfolded, where the middle is determined
 * according to the centered-zero-frequency convention. In this case, this
 * function is mathematically equivalent to
 *   temp = zeros(width, size(src,2));
 *   temp(1:width/size(src,1):end, :) = ift(src);
 *   dst = ft(temp);
 * Likewise properties hold for dim == 1.
 *
 *
 * @param[out] dst  (resized)
 *   dim == 0: dst = width x m
 *   dim == 1: dst = n x width
 * @param[in] src of size n x m
 * @param[in] width
 * @param[in] dim
 * @param[in] scale
 */
template <typename DERIVED1, typename DERIVED2>
void
unfold(Eigen::DenseBase<DERIVED1> &dst,
       Eigen::DenseBase<DERIVED2> &src,
       unsigned int width,
       int dim,
       bool scale = false)
{
  typedef typename DERIVED1::Scalar numeric_t;
  typedef Eigen::Array<numeric_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> local_array_t;

  thread_local static ArrayBuffer<> buf1;
  thread_local static ArrayBuffer<> buf2;

  assert(dim == 0 || dim == 1);
  const int nrows = src.rows();
  const int ncols = src.cols();

  if (dim == 0) {
    const int s = width / nrows;
    assert(s > 0);
    if ((width / nrows) % 2 == 0) {
      auto tmp = buf1.get<local_array_t>(src.rows(), src.cols());
      ifftshift(tmp, src, 0);
      auto tmp2 = buf2.get<local_array_t>(width, src.cols());
      tmp2 = tmp.replicate(s, 1);
      assert(dst.rows() == width);
      fftshift(dst, tmp2, 0);
    } else {
      dst.derived() = src.replicate(s, 1);
    }
    if (scale) dst *= src.rows() / double(width);
  } else if (dim == 1) {
    const int s = width / ncols;
    assert(s > 0);
    if (s % 2 == 0) {
      auto tmp = buf1.get<local_array_t>(src.rows(), src.cols());
      ifftshift(tmp, src, 1);
      auto tmp2 = buf2.get<local_array_t>(src.rows(), width);
      tmp2 = tmp.replicate(1, s);
      assert(dst.cols() == width);
      fftshift(dst, tmp2, 1);
    } else {
      dst = src.replicate(1, s);
    }
    if (scale) dst *= src.cols() / double(width);
  }
}


//
// Created by philipp on 26.12.2019.
//

#ifndef FUNNELS_CPP_DISTANCES_HH
#define FUNNELS_CPP_DISTANCES_HH

#include <cassert>
#include <vector>
#include <Eigen/Core>
#include <cmath>

#ifndef M_PI
  #define M_PI 3.14159265358979323846
#endif

using namespace std;
using namespace Eigen;

double wrap_so2(double x){
  return std::fmod(x, M_PI);
}

class partial_so2_dist_t{
public:
  partial_so2_dist_t(vector<size_t> so2_dims=vector<size_t>()):
      _so2_dims(move(so2_dims)){};

//  template <class DERIVED0, class DERIVED1>
//  MatrixXd &operator()(const MatrixBase<DERIVED0> &x0,
//      const MatrixBase<DERIVED1> &x1){
//    // Multiple possibilities here
//    // so we have to do proper broadcasting
//    assert(x0.rows()==x1.rows());
//
//    if(x0.cols()==1 && x1.cols()==1){
//      // two vectors
//      _tmpMat = x0-x1;
//    }else if(x0.cols()==1 || x1.cols()==1){
//      if (x0.cols()==1){
//        _tmpMat = (-x1).colwise() + x0;
//      }else{
//        _tmpMat = x1.colwise()-x0;
//      }
//    }else if(x0.cols()==x1.cols()){
//      _tmpMat = x0-x1;
//    }
//

//    return _tmpMat;
//  }

  const MatrixXd & cp_vv(const VectorXd & v0, const VectorXd & v1) const {
    _tmpMat = v0-v1;
    do_so2_wrap();
    return _tmpMat;
  }

  const MatrixXd & cp_vM(const VectorXd & v0, const MatrixXd & m1) const {
    _tmpMat = (-m1).colwise() + v0;
    do_so2_wrap();
    return _tmpMat;
  }

  const MatrixXd & cp_Mv(const MatrixXd & m0, const VectorXd & v1) const {
    _tmpMat = m0.colwise() - v1;
    do_so2_wrap();
    return _tmpMat;
  }

  const MatrixXd &cp_MM(const MatrixXd & m0, const MatrixXd & m1) const {
    _tmpMat = m0 - m1;
    do_so2_wrap();
    return _tmpMat;
  }

protected:

  inline void do_so2_wrap()const{
    // Take into account the angles
    for (size_t idx : _so2_dims){
      _tmpMat.col(idx).unaryExpr(&wrap_so2);
    }
  }

  const vector<size_t> _so2_dims;
  mutable MatrixXd _tmpMat;
};

#endif //FUNNELS_CPP_DISTANCES_HH


#include "custom.h"
#include<QtWidgets>
#include<qdebug.h>
#include<math.h>
#include <armadillo>

Custom::Custom( QWidget * parent)
{
    //initArr();
    initTableWidget();

    calculationBtn = new QPushButton(QStringLiteral("重新计算"),this);
    connect(calculationBtn,&QPushButton::clicked,this,&Custom::calculation);
    QVBoxLayout * Vlayout = new QVBoxLayout(this);
    Vlayout->addWidget(calculationBtn);
    Vlayout->addWidget(tableWidget);
    setLayout(Vlayout);

    QString str = QString::fromStdString(arma::arma_version::as_string());
    qDebug()<<str;

}

void Custom::initTableWidget(){

    tableWidget = new QTableWidget(6,7,this);

    QStringList HHeadList;
    HHeadList<<"A(%)"<<"B(%)"<<"C(%)"<<"D(%)"<<"E(%)"<<"F(%)"<<QStringLiteral("重量(g)");
    tableWidget->setHorizontalHeaderLabels(HHeadList);

    QStringList VHeadList;
    VHeadList<<"1#"<<"2#"<<"3#"<<"4#"<<"5#"<<QStringLiteral("混合后元素含量");
    tableWidget->setVerticalHeaderLabels(VHeadList);

    for( int i = 0 ; i < tableWidget->rowCount() ; i ++){

        for( int j = 0 ; j < tableWidget->columnCount() ;j++){

            tableWidget->setItem(i,j,new QTableWidgetItem("0"));

        }

    }

    tableWidget->setItem(tableWidget->rowCount()-1,tableWidget->columnCount()-1,new QTableWidgetItem("1000"));
    tableWidget->show();
    connect(tableWidget,&QTableWidget::itemChanged,this,&Custom::tableItemChanged);

}

int Custom::calculation(){

    arma::mat A1(4,3,arma::fill::zeros);
    arma::vec B1(4,arma::fill::zeros);
    arma::vec X1;

    A1 = {
            {5,4,-4},
            {-4,-3,5},
            {-1,-1,-1},
            {-8,-7,1}
         };
    B1 = {4,-5,1,-1};
    solve(X1, A1, B1);
    A1.print("A1:");
    B1.print("B1:");
    X1.print("X1:");

    return 0;

    qDebug()<<QStringLiteral("点击重新计算按钮");

    arma::mat A(tableWidget->columnCount()-1,tableWidget->rowCount()-1,arma::fill::zeros);
    arma::vec B(tableWidget->columnCount()-1,arma::fill::zeros);
    arma::vec X(tableWidget->rowCount()-1,arma::fill::zeros);

    for( int i = 0 ; i < tableWidget->columnCount()-1; i ++){ //按列顺序优先遍历

        for( int j = 0 ; j < tableWidget->rowCount()-1 ;j++){

            if(tableWidget->item(j,i)!=nullptr){

                QString str = tableWidget->item(j,i)->text();
                double w = str.toDouble();
                A(i,j) = w;

            }

        }

    }

    double a1[10] = {0};

    for(int i = 0 ;  i < tableWidget->columnCount()-1; i++){

        if(tableWidget->item(tableWidget->rowCount()-1,i)!=nullptr){

            QString str = tableWidget->item(tableWidget->rowCount()-1,i)->text();
            double w = str.toDouble();
            B[i] = 1000*w;

        }

    }

    bool status = solve(X, A, B);
    A.print("A:");
    B.print("B:");
    if( status ){

        for(int i = 0 ; i < tableWidget->columnCount()-1;i++){
            qDebug()<<X[i];
        }

    }else{
        QMessageBox::information(NULL, QStringLiteral("提示"), QStringLiteral("无解"),
                                 QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
    }

    /*int a1[10] = {0};

    for(int i = 0 ;  i < tableWidget->columnCount()-1; i++){

        if(tableWidget->item(tableWidget->rowCount()-1,i)!=nullptr){

            QString str = tableWidget->item(tableWidget->rowCount()-1,i)->text();
            int w = str.toInt();
            a1[i] = (1000*w)/100;

        }

    }

    for( int i = 0 ; i < tableWidget->columnCount()-1; i ++){ //按列顺序优先遍历

        for( int j = 0 ; j < tableWidget->rowCount()-1 ;j++){

            if(tableWidget->item(j,i)!=nullptr){

//                if(j==tableWidget->columnCount()-1){

//                    a[i][j] = a1[i];

//                }else{

//                    QString str = tableWidget->item(i,j)->text();
//                    int w = str.toInt();
//                    a[i][j] = w;

//                }
                QString str = tableWidget->item(j,i)->text();
                int w = str.toInt();
                a[i][j] = w;

                //qDebug()<<a[i][j];
            }

        }

    }

    for( int i = 0 ; i < 10 ; i ++){

        int len = tableWidget->columnCount()-1;
        a[len][i] = a1[i];

    }

    for( int i = 0 ; i < 10 ; i ++){

        for( int j = 0; j < 10 ; j++){
            qDebug()<<a[i][j];
        }

    }

    int var = tableWidget->rowCount()-1; //未知数个数
    int free_num = Gauss(tableWidget->columnCount()-1,var);
    if (free_num == -1) {

        QMessageBox::information(NULL, QStringLiteral("提示"), QStringLiteral("无解"),
                                 QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);

    }
    else if (free_num == -2){

        QMessageBox::information(NULL, QStringLiteral("提示"), QStringLiteral("有浮点解,无整数解"),
                                 QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);

    }
    else if (free_num > 0)
    {
//        printf("无穷多解! 自由变元个数为%d\n", free_num);
//        for (i = 0; i < var; i++)
//        {
//            if (free_x[i]) printf("x%d 是不确定的\n", i + 1);
//            else printf("x%d: %d\n", i + 1, x[i]);
//        }
        QMessageBox::information(NULL, QStringLiteral("提示"), QStringLiteral("无穷多解！"),
                                 QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
    }
    else
    {
        for (int i = 0; i < var; i++)
        {
            printf("x%d: %d\n", i + 1, x[i]);
        }
    }*/

    return -1;
}

void Custom::tableItemChanged( QTableWidgetItem * item){

    qDebug()<<QStringLiteral("单元格改变");

}

int Custom::initArr(){

    for( int i = 0 ;  i < 10 ; i ++){
        for( int j = 0;j<10;j++){
            a[i][j] = 0;
        }
    }
    return 0;
}

int Custom::gcd(int a,int b)
{
    int t;
    while(b!=0)
    {
        t=b;
        b=a%b;
        a=t;
    }
    return a;
}

int Custom::lcm(int a,int b)
{
    return a/gcd(a,b)*b;//先除后乘防溢出
}

// 高斯消元法解方程组(Gauss-Jordan elimination).(-2表示有浮点数解，但无整数解，
//-1表示无解，0表示唯一解，大于0表示无穷解，并返回自由变元的个数)
//有equ个方程，var个变元。增广矩阵行数为equ,分别为0到equ-1,列数为var+1,分别为0到var.
int Custom::Gauss(int equ,int var)
{
    int i,j,k;
    int max_r;// 当前这列绝对值最大的行.
    int col;//当前处理的列
    int ta,tb;
    int LCM;
    int temp;
    int free_x_num;
    int free_index;

    for(int i=0; i<=var; i++)
    {
        x[i]=0;
        free_x[i]=true;
    }

    //转换为阶梯阵.
    col=0; // 当前处理的列
    for(k = 0; k < equ && col < var; k++,col++)
    {
        // 枚举当前处理的行.
// 找到该col列元素绝对值最大的那行与第k行交换.(为了在除法时减小误差)
        max_r=k;
        for(i=k+1; i<equ; i++)
        {
            if(abs(a[i][col])>abs(a[max_r][col])) max_r=i;
        }
        if(max_r!=k)
        {
            // 与第k行交换.
            for(j=k; j<var+1; j++) std::swap(a[k][j],a[max_r][j]);
        }
        if(a[k][col]==0)
        {
            // 说明该col列第k行以下全是0了，则处理当前行的下一列.
            k--;
            continue;
        }
        for(i=k+1; i<equ; i++)
        {
            // 枚举要删去的行.
            if(a[i][col]!=0)
            {
                LCM = lcm(abs(a[i][col]),abs(a[k][col]));
                ta = LCM/abs(a[i][col]);
                tb = LCM/abs(a[k][col]);
                if(a[i][col]*a[k][col]<0)tb=-tb;//异号的情况是相加
                for(j=col; j<var+1; j++)
                {
                    a[i][j] = a[i][j]*ta-a[k][j]*tb;
                }
            }
        }
    }

    //  Debug();

    // 1. 无解的情况: 化简的增广阵中存在(0, 0, ..., a)这样的行(a != 0).
    for (i = k; i < equ; i++)
    {
        // 对于无穷解来说，如果要判断哪些是自由变元，那么初等行变换中的交换就会影响，则要记录交换.
        if (a[i][col] != 0) return -1;
    }
    // 2. 无穷解的情况: 在var * (var + 1)的增广阵中出现(0, 0, ..., 0)这样的行，即说明没有形成严格的上三角阵.
    // 且出现的行数即为自由变元的个数.
    if (k < var)
    {
        // 首先，自由变元有var - k个，即不确定的变元至少有var - k个.
        for (i = k - 1; i >= 0; i--)
        {
            // 第i行一定不会是(0, 0, ..., 0)的情况，因为这样的行是在第k行到第equ行.
            // 同样，第i行一定不会是(0, 0, ..., a), a != 0的情况，这样的无解的.
            free_x_num = 0; // 用于判断该行中的不确定的变元的个数，如果超过1个，则无法求解，它们仍然为不确定的变元.
            for (j = 0; j < var; j++)
            {
                if (a[i][j] != 0 && free_x[j]) free_x_num++, free_index = j;
            }
            if (free_x_num > 1) continue; // 无法求解出确定的变元.
            // 说明就只有一个不确定的变元free_index，那么可以求解出该变元，且该变元是确定的.
            temp = a[i][var];
            for (j = 0; j < var; j++)
            {
                if (a[i][j] != 0 && j != free_index) temp -= a[i][j] * x[j];
            }
            x[free_index] = temp / a[i][free_index]; // 求出该变元.
            free_x[free_index] = 0; // 该变元是确定的.
        }
        return var - k; // 自由变元有var - k个.
    }
    // 3. 唯一解的情况: 在var * (var + 1)的增广阵中形成严格的上三角阵.
    // 计算出Xn-1, Xn-2 ... X0.
    for (i = var - 1; i >= 0; i--)
    {
        temp = a[i][var];
        for (j = i + 1; j < var; j++)
        {
            if (a[i][j] != 0) temp -= a[i][j] * x[j];
        }
        if (temp % a[i][i] != 0) return -2; // 说明有浮点数解，但无整数解.
        x[i] = temp / a[i][i];
    }
    return 0;
}


#include "common.h"
#include <limits>
#include <cmath>
#include <ctime>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_01.hpp>

boost::random::mt19937 rng(time(0));
boost::random::uniform_01<elem_t> dist01;
std::ostream* logOut = &std::cout;
LogLevel currentLogLevel = Error;

Color::Modifier red(Color::FG_RED);
Color::Modifier def(Color::FG_DEFAULT);
Color::Modifier green(Color::FG_GREEN);
Color::Modifier yellow(Color::FG_YELLOW);

long double logint[MAX_COUNT + 1];
long double logfactorial[MAX_COUNT + 1];
double Pi = acos(-1.0);
intN bionomial[MAX_N + 1][MAX_N + 1];

std::ostream& operator<< (std::ostream& out, LogLevel level) {
    if (level == Info) {
        out << "Info:";
    } else if (level == Warning) {
        out << "Warning:";
    } else if (level == Error) {
        out << "Error:";
    } else {
        // do nothing.
    }
}

long double LogInt(int n)
{
	if (n == 0) {
		std::cout << "error! log(0)" << std::endl;
		return std::numeric_limits<long double>::max();
	}
	if (n == 1) return 0.0;

	if (logint[n] == 0.0) {
		logint[n] = logl(n);
	}

	return logint[n];
}

long double LogFactorial(int n)
{
	if (n == 1 || n == 0) return 0.0;

	if (logfactorial[n] == 0.0) {
		for (int i = 2; i <= n; i++) {
			logfactorial[n] += LogInt(i);
		}
	}

	return logfactorial[n];
}

elem_t random(elem_t min, elem_t max)
{
    return min + (max - min) * dist01(rng);
}

i64 random64(i64 max)
{
    boost::random::uniform_int_distribution<i64> dist(0, max);
    return dist(rng);
}

u128 random128(u128 max)
{
    boost::random::uniform_int_distribution<u64> high(0, (u64)(max >> 64));
    boost::random::uniform_int_distribution<u64> low(0, (u64)(max & std::numeric_limits<u64>::max()));

    return (((u128)high(rng)) << 64) | low(rng);
}

var_t randomComplex(elem_t minR, elem_t maxR) {
    elem_t r;
    while(true) {
        r = random(minR, maxR);
        if (random(0.0, maxR * maxR - minR * minR) < (r * r - minR * minR)) {
            break;
        }
    }
    elem_t phi = random(0.0, 2 * Pi);
    return var_t(r * cos(phi), r * sin(phi));
}

double EuclideanDist(double x1, double y1, double x2, double y2)
{
	return sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
}

double EuclideanDistSquare(double x1, double y1, double x2, double y2)
{
	return (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);
}

intN BioCoeff(int n, int k)
{
	if (k > n) return 0;
	if (k == 0 || k == n) return 1;
	intN& ret = bionomial[n][k];
	if (ret == 0) {
		ret = BioCoeff(n - 1, k) + BioCoeff(n - 1, k - 1);
	}

	return ret;
}

bool isSingular(var_t u, elem_t eps) {
    return std::abs(u.real()) < eps && (std::abs(u.imag() + 0.5) < eps || std::abs(u.imag() - 0.5) < eps);
}

var_t e2ip(var_t u, elem_t eps) {
    if (isSingular(u)) {
        if (u.imag() > (elem_t)0.0) {
            return (elem_t)1.0;
        } else {
            return (elem_t)-1.0;
        }
    }
    return (u + var_t(0.0, 0.5))/(u - var_t(0.0, 0.5));
/*    if (abs(u.real()) > EPS) {
        return (u + var_t(0.0, 0.5))/(u - var_t(0.0, 0.5));
    } else {
        if (abs(u.imag() + 0.5) <= EPS) {
            return var_t(0, -EPS);
        } else if (abs(u.imag() - 0.5) <= EPS) {
            return var_t(0, 1/EPS);
        } else {
            return (u + var_t(0.0, 0.5))/(u - var_t(0.0, 0.5));
        }
    }*/
}

var_t e2ip(const Vector &u, elem_t eps) {
    var_t ret((elem_t)1.0);
    for (int i = 0; i < u.size(); i++) {
        ret *= e2ip(u[i], eps);
    }
    return ret;
}

elem_t momentum(Vector u, elem_t eps) {
    var_t ret = e2ip(u, eps);
//    return log(ret) * var_t(0.0, -1.0);
    return std::arg(ret);
}

var_t sMatrix(var_t u1, var_t u2) {
    return (u1 - u2 - var_t(0, 1.0))/(u1 - u2 + var_t(0, 1.0));
}

elem_t floatMod(elem_t num1, elem_t num2) {
    return num1 - floor((num1 + num2 * 0.5)/num2) * num2;
}

bool isZero(const var_t &val) {
    return abs(val.real()) < EPS && abs(val.imag()) < EPS;
}

var_t chop(var_t v, elem_t eps) {
    elem_t re = v.real();
    elem_t im = v.imag();
    if (std::abs(re) < eps) re = 0.0L;
    if (std::abs(im) < eps) im = 0.0L;
    return var_t(re, im);
}

void chop(Vector &v, elem_t eps) {
    for (int i = 0; i < v.size(); i++) {
        v[i] = chop(v[i], eps);
    }
}

Stopwatch::Stopwatch()
{
    start = clock();
}

void Stopwatch::Restart()
{
        start = clock();
}

std::string Stopwatch::Log(bool restart)
{
    double ret = (clock() - start) / (double)CLOCKS_PER_SEC;
    std::string text = ToString(ret) + " seconds elapsed, current time " + Now();

    if (restart) {
        Restart();
    }

    return text;
}

std::string Stopwatch::Now()
{
    time_t t = time(0);   // get time now
    struct tm * now = localtime(&t);
    char buf[80];
    strftime(buf, sizeof(buf), "%Y-%m-%d.%X", now);
    return buf;
}

/// return how much time in second since last Start() function call
double Stopwatch::Elapsed(bool restart)
{
        double ret = (clock() - start) / (double)CLOCKS_PER_SEC;
        if (restart) Restart();
        return ret;
}


// Copyright John Maddock 2006.
// 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 "mp_t.hpp"
#include <boost/math/tools/test_data.hpp>
#include <boost/test/included/prg_exec_monitor.hpp>
#include <boost/math/special_functions/ellint_rj.hpp>
#include <boost/math/special_functions/ellint_rd.hpp>
#include <fstream>
#include <boost/math/tools/test_data.hpp>
#include <boost/random.hpp>

float extern_val;
// confuse the compilers optimiser, and force a truncation to float precision:
float truncate_to_float(float const * pf)
{
   extern_val = *pf;
   return *pf;
}

//
// Archived here is the original implementation of this
// function by Xiaogang Zhang, we can use this to
// generate special test cases for the new version:
//
template <typename T, typename Policy>
T ellint_rj_old(T x, T y, T z, T p, const Policy& pol)
{
   T value, u, lambda, alpha, beta, sigma, factor, tolerance;
   T X, Y, Z, P, EA, EB, EC, E2, E3, S1, S2, S3;
   unsigned long k;

   BOOST_MATH_STD_USING
      using namespace boost::math;

   static const char* function = "boost::math::ellint_rj<%1%>(%1%,%1%,%1%)";

   if(x < 0)
   {
      return policies::raise_domain_error<T>(function,
         "Argument x must be non-negative, but got x = %1%", x, pol);
   }
   if(y < 0)
   {
      return policies::raise_domain_error<T>(function,
         "Argument y must be non-negative, but got y = %1%", y, pol);
   }
   if(z < 0)
   {
      return policies::raise_domain_error<T>(function,
         "Argument z must be non-negative, but got z = %1%", z, pol);
   }
   if(p == 0)
   {
      return policies::raise_domain_error<T>(function,
         "Argument p must not be zero, but got p = %1%", p, pol);
   }
   if(x + y == 0 || y + z == 0 || z + x == 0)
   {
      return policies::raise_domain_error<T>(function,
         "At most one argument can be zero, "
         "only possible result is %1%.", std::numeric_limits<T>::quiet_NaN(), pol);
   }

   // error scales as the 6th power of tolerance
   tolerance = pow(T(1) * tools::epsilon<T>() / 3, T(1) / 6);

   // for p < 0, the integral is singular, return Cauchy principal value
   if(p < 0)
   {
      //
      // We must ensure that (z - y) * (y - x) is positive.
      // Since the integral is symmetrical in x, y and z
      // we can just permute the values:
      //
      if(x > y)
         std::swap(x, y);
      if(y > z)
         std::swap(y, z);
      if(x > y)
         std::swap(x, y);

      T q = -p;
      T pmy = (z - y) * (y - x) / (y + q);  // p - y

      BOOST_ASSERT(pmy >= 0);

      p = pmy + y;
      value = ellint_rj_old(x, y, z, p, pol);
      value *= pmy;
      value -= 3 * boost::math::ellint_rf(x, y, z, pol);
      value += 3 * sqrt((x * y * z) / (x * z + p * q)) * boost::math::ellint_rc(x * z + p * q, p * q, pol);
      value /= (y + q);
      return value;
   }

   // duplication
   sigma = 0;
   factor = 1;
   k = 1;
   do
   {
      u = (x + y + z + p + p) / 5;
      X = (u - x) / u;
      Y = (u - y) / u;
      Z = (u - z) / u;
      P = (u - p) / u;

      if((tools::max)(abs(X), abs(Y), abs(Z), abs(P)) < tolerance)
         break;

      T sx = sqrt(x);
      T sy = sqrt(y);
      T sz = sqrt(z);

      lambda = sy * (sx + sz) + sz * sx;
      alpha = p * (sx + sy + sz) + sx * sy * sz;
      alpha *= alpha;
      beta = p * (p + lambda) * (p + lambda);
      sigma += factor * boost::math::ellint_rc(alpha, beta, pol);
      factor /= 4;
      x = (x + lambda) / 4;
      y = (y + lambda) / 4;
      z = (z + lambda) / 4;
      p = (p + lambda) / 4;
      ++k;
   } while(k < policies::get_max_series_iterations<Policy>());

   // Check to see if we gave up too soon:
   policies::check_series_iterations<T>(function, k, pol);

   // Taylor series expansion to the 5th order
   EA = X * Y + Y * Z + Z * X;
   EB = X * Y * Z;
   EC = P * P;
   E2 = EA - 3 * EC;
   E3 = EB + 2 * P * (EA - EC);
   S1 = 1 + E2 * (E2 * T(9) / 88 - E3 * T(9) / 52 - T(3) / 14);
   S2 = EB * (T(1) / 6 + P * (T(-6) / 22 + P * T(3) / 26));
   S3 = P * ((EA - EC) / 3 - P * EA * T(3) / 22);
   value = 3 * sigma + factor * (S1 + S2 + S3) / (u * sqrt(u));

   return value;
}

template <typename T, typename Policy>
T ellint_rd_imp_old(T x, T y, T z, const Policy& pol)
{
   T value, u, lambda, sigma, factor, tolerance;
   T X, Y, Z, EA, EB, EC, ED, EE, S1, S2;
   unsigned long k;

   BOOST_MATH_STD_USING
   using namespace boost::math;

   static const char* function = "boost::math::ellint_rd<%1%>(%1%,%1%,%1%)";

   if(x < 0)
   {
      return policies::raise_domain_error<T>(function,
         "Argument x must be >= 0, but got %1%", x, pol);
   }
   if(y < 0)
   {
      return policies::raise_domain_error<T>(function,
         "Argument y must be >= 0, but got %1%", y, pol);
   }
   if(z <= 0)
   {
      return policies::raise_domain_error<T>(function,
         "Argument z must be > 0, but got %1%", z, pol);
   }
   if(x + y == 0)
   {
      return policies::raise_domain_error<T>(function,
         "At most one argument can be zero, but got, x + y = %1%", x + y, pol);
   }

   // error scales as the 6th power of tolerance
   tolerance = pow(tools::epsilon<T>() / 3, T(1) / 6);

   // duplication
   sigma = 0;
   factor = 1;
   k = 1;
   do
   {
      u = (x + y + z + z + z) / 5;
      X = (u - x) / u;
      Y = (u - y) / u;
      Z = (u - z) / u;
      if((tools::max)(abs(X), abs(Y), abs(Z)) < tolerance)
         break;
      T sx = sqrt(x);
      T sy = sqrt(y);
      T sz = sqrt(z);
      lambda = sy * (sx + sz) + sz * sx; //sqrt(x * y) + sqrt(y * z) + sqrt(z * x);
      sigma += factor / (sz * (z + lambda));
      factor /= 4;
      x = (x + lambda) / 4;
      y = (y + lambda) / 4;
      z = (z + lambda) / 4;
      ++k;
   } while(k < policies::get_max_series_iterations<Policy>());

   // Check to see if we gave up too soon:
   policies::check_series_iterations<T>(function, k, pol);

   // Taylor series expansion to the 5th order
   EA = X * Y;
   EB = Z * Z;
   EC = EA - EB;
   ED = EA - 6 * EB;
   EE = ED + EC + EC;
   S1 = ED * (ED * T(9) / 88 - Z * EE * T(9) / 52 - T(3) / 14);
   S2 = Z * (EE / 6 + Z * (-EC * T(9) / 22 + Z * EA * T(3) / 26));
   value = 3 * sigma + factor * (1 + S1 + S2) / (u * sqrt(u));

   return value;
}

template <typename T, typename Policy>
T ellint_rf_imp_old(T x, T y, T z, const Policy& pol)
{
   T value, X, Y, Z, E2, E3, u, lambda, tolerance;
   unsigned long k;
   BOOST_MATH_STD_USING
   using namespace boost::math;
   static const char* function = "boost::math::ellint_rf<%1%>(%1%,%1%,%1%)";
   if(x < 0 || y < 0 || z < 0)
   {
      return policies::raise_domain_error<T>(function,
         "domain error, all arguments must be non-negative, "
         "only sensible result is %1%.",
         std::numeric_limits<T>::quiet_NaN(), pol);
   }
   if(x + y == 0 || y + z == 0 || z + x == 0)
   {
      return policies::raise_domain_error<T>(function,
         "domain error, at most one argument can be zero, "
         "only sensible result is %1%.",
         std::numeric_limits<T>::quiet_NaN(), pol);
   }
   // Carlson scales error as the 6th power of tolerance,
   // but this seems not to work for types larger than
   // 80-bit reals, this heuristic seems to work OK:
   if(policies::digits<T, Policy>() > 64)
   {
      tolerance = pow(tools::epsilon<T>(), T(1) / 4.25f);
      BOOST_MATH_INSTRUMENT_VARIABLE(tolerance);
   }
   else
   {
      tolerance = pow(4 * tools::epsilon<T>(), T(1) / 6);
      BOOST_MATH_INSTRUMENT_VARIABLE(tolerance);
   }
   // duplication
   k = 1;
   do
   {
      u = (x + y + z) / 3;
      X = (u - x) / u;
      Y = (u - y) / u;
      Z = (u - z) / u;
      // Termination condition:
      if((tools::max)(abs(X), abs(Y), abs(Z)) < tolerance)
         break;
      T sx = sqrt(x);
      T sy = sqrt(y);
      T sz = sqrt(z);
      lambda = sy * (sx + sz) + sz * sx;
      x = (x + lambda) / 4;
      y = (y + lambda) / 4;
      z = (z + lambda) / 4;
      ++k;
   } while(k < policies::get_max_series_iterations<Policy>());
   // Check to see if we gave up too soon:
   policies::check_series_iterations<T>(function, k, pol);
   BOOST_MATH_INSTRUMENT_VARIABLE(k);
   // Taylor series expansion to the 5th order
   E2 = X * Y - Z * Z;
   E3 = X * Y * Z;
   value = (1 + E2*(E2 / 24 - E3*T(3) / 44 - T(0.1)) + E3 / 14) / sqrt(u);
   BOOST_MATH_INSTRUMENT_VARIABLE(value);
   return value;
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rj_data_4e(mp_t n)
{
   mp_t result = ellint_rj_old(n, n, n, n, boost::math::policies::policy<>());
   return boost::math::make_tuple(n, n, n, result);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t, mp_t> generate_rj_data_3e(mp_t x, mp_t p)
{
   mp_t r = ellint_rj_old(x, x, x, p, boost::math::policies::policy<>());
   return boost::math::make_tuple(x, x, x, p, r);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t, mp_t> generate_rj_data_2e_1(mp_t x, mp_t y, mp_t p)
{
   mp_t r = ellint_rj_old(x, x, y, p, boost::math::policies::policy<>());
   return boost::math::make_tuple(x, x, y, p, r);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t, mp_t> generate_rj_data_2e_2(mp_t x, mp_t y, mp_t p)
{
   mp_t r = ellint_rj_old(x, y, x, p, boost::math::policies::policy<>());
   return boost::math::make_tuple(x, y, x, p, r);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t, mp_t> generate_rj_data_2e_3(mp_t x, mp_t y, mp_t p)
{
   mp_t r = ellint_rj_old(y, x, x, p, boost::math::policies::policy<>());
   return boost::math::make_tuple(y, x, x, p, r);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t, mp_t> generate_rj_data_2e_4(mp_t x, mp_t y, mp_t p)
{
   mp_t r = ellint_rj_old(x, y, p, p, boost::math::policies::policy<>());
   return boost::math::make_tuple(x, y, p, p, r);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rd_data_2e_1(mp_t x, mp_t y)
{
   mp_t r = ellint_rd_imp_old(x, y, y, boost::math::policies::policy<>());
   return boost::math::make_tuple(x, y, y, r);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rd_data_2e_2(mp_t x, mp_t y)
{
   mp_t r = ellint_rd_imp_old(x, x, y, boost::math::policies::policy<>());
   return boost::math::make_tuple(x, x, y, r);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rd_data_2e_3(mp_t x)
{
   mp_t r = ellint_rd_imp_old(mp_t(0), x, x, boost::math::policies::policy<>());
   return boost::math::make_tuple(0, x, x, r);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rd_data_3e(mp_t x)
{
   mp_t r = ellint_rd_imp_old(x, x, x, boost::math::policies::policy<>());
   return boost::math::make_tuple(x, x, x, r);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rd_data_0xy(mp_t x, mp_t y)
{
   mp_t r = ellint_rd_imp_old(mp_t(0), x, y, boost::math::policies::policy<>());
   return boost::math::make_tuple(mp_t(0), x, y, r);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rf_data_xxx(mp_t x)
{
   mp_t r = ellint_rf_imp_old(x, x, x, boost::math::policies::policy<>());
   return boost::math::make_tuple(x, x, x, r);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rf_data_xyy(mp_t x, mp_t y)
{
   mp_t r = ellint_rf_imp_old(x, y, y, boost::math::policies::policy<>());
   return boost::math::make_tuple(x, y, y, r);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rf_data_xxy(mp_t x, mp_t y)
{
   mp_t r = ellint_rf_imp_old(x, x, y, boost::math::policies::policy<>());
   return boost::math::make_tuple(x, x, y, r);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rf_data_xyx(mp_t x, mp_t y)
{
   mp_t r = ellint_rf_imp_old(x, y, x, boost::math::policies::policy<>());
   return boost::math::make_tuple(x, y, x, r);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rf_data_0yy(mp_t y)
{
   mp_t r = ellint_rf_imp_old(mp_t(0), y, y, boost::math::policies::policy<>());
   return boost::math::make_tuple(mp_t(0), y, y, r);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rf_data_xy0(mp_t x, mp_t y)
{
   mp_t r = ellint_rf_imp_old(x, y, mp_t(0), boost::math::policies::policy<>());
   return boost::math::make_tuple(x, y, mp_t(0), r);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rf_data(mp_t n)
{
   static boost::mt19937 r;
   boost::uniform_real<float> ur(0, 1);
   boost::uniform_int<int> ui(-100, 100);
   float x = ur(r);
   x = ldexp(x, ui(r));
   mp_t xr(truncate_to_float(&x));
   float y = ur(r);
   y = ldexp(y, ui(r));
   mp_t yr(truncate_to_float(&y));
   float z = ur(r);
   z = ldexp(z, ui(r));
   mp_t zr(truncate_to_float(&z));

   mp_t result = boost::math::ellint_rf(xr, yr, zr);
   return boost::math::make_tuple(xr, yr, zr, result);
}

boost::math::tuple<mp_t, mp_t, mp_t> generate_rc_data(mp_t n)
{
   static boost::mt19937 r;
   boost::uniform_real<float> ur(0, 1);
   boost::uniform_int<int> ui(-100, 100);
   float x = ur(r);
   x = ldexp(x, ui(r));
   mp_t xr(truncate_to_float(&x));
   float y = ur(r);
   y = ldexp(y, ui(r));
   mp_t yr(truncate_to_float(&y));

   mp_t result = boost::math::ellint_rc(xr, yr);
   return boost::math::make_tuple(xr, yr, result);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t, mp_t> generate_rj_data(mp_t n)
{
   static boost::mt19937 r;
   boost::uniform_real<float> ur(0, 1);
   boost::uniform_real<float> nur(-1, 1);
   boost::uniform_int<int> ui(-100, 100);
   float x = ur(r);
   x = ldexp(x, ui(r));
   mp_t xr(truncate_to_float(&x));
   float y = ur(r);
   y = ldexp(y, ui(r));
   mp_t yr(truncate_to_float(&y));
   float z = ur(r);
   z = ldexp(z, ui(r));
   mp_t zr(truncate_to_float(&z));
   float p = nur(r);
   p = ldexp(p, ui(r));
   mp_t pr(truncate_to_float(&p));

   boost::math::ellint_rj(x, y, z, p);

   mp_t result = boost::math::ellint_rj(xr, yr, zr, pr);
   return boost::math::make_tuple(xr, yr, zr, pr, result);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rd_data(mp_t n)
{
   static boost::mt19937 r;
   boost::uniform_real<float> ur(0, 1);
   boost::uniform_int<int> ui(-100, 100);
   float x = ur(r);
   x = ldexp(x, ui(r));
   mp_t xr(truncate_to_float(&x));
   float y = ur(r);
   y = ldexp(y, ui(r));
   mp_t yr(truncate_to_float(&y));
   float z = ur(r);
   z = ldexp(z, ui(r));
   mp_t zr(truncate_to_float(&z));

   mp_t result = boost::math::ellint_rd(xr, yr, zr);
   return boost::math::make_tuple(xr, yr, zr, result);
}

mp_t rg_imp(mp_t x, mp_t y, mp_t z)
{
   using std::swap;
   // If z is zero permute so the call to RD is valid:
   if(z == 0)
      swap(x, z);
   return (z * ellint_rf_imp_old(x, y, z, boost::math::policies::policy<>())
      - (x - z) * (y - z) * ellint_rd_imp_old(x, y, z, boost::math::policies::policy<>()) / 3
      + sqrt(x * y / z)) / 2;
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rg_data(mp_t n)
{
   static boost::mt19937 r;
   boost::uniform_real<float> ur(0, 1);
   boost::uniform_int<int> ui(-100, 100);
   float x = ur(r);
   x = ldexp(x, ui(r));
   mp_t xr(truncate_to_float(&x));
   float y = ur(r);
   y = ldexp(y, ui(r));
   mp_t yr(truncate_to_float(&y));
   float z = ur(r);
   z = ldexp(z, ui(r));
   mp_t zr(truncate_to_float(&z));

   mp_t result = rg_imp(xr, yr, zr);
   return boost::math::make_tuple(xr, yr, zr, result);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rg_xxx(mp_t x)
{
   mp_t result = rg_imp(x, x, x);
   return boost::math::make_tuple(x, x, x, result);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rg_xyy(mp_t x, mp_t y)
{
   mp_t result = rg_imp(x, y, y);
   return boost::math::make_tuple(x, y, y, result);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rg_xxy(mp_t x, mp_t y)
{
   mp_t result = rg_imp(x, x, y);
   return boost::math::make_tuple(x, x, y, result);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rg_xyx(mp_t x, mp_t y)
{
   mp_t result = rg_imp(x, y, x);
   return boost::math::make_tuple(x, y, x, result);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rg_0xx(mp_t x)
{
   mp_t result = rg_imp(mp_t(0), x, x);
   return boost::math::make_tuple(mp_t(0), x, x, result);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rg_x0x(mp_t x)
{
   mp_t result = rg_imp(x, mp_t(0), x);
   return boost::math::make_tuple(x, mp_t(0), x, result);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rg_xx0(mp_t x)
{
   mp_t result = rg_imp(x, x, mp_t(0));
   return boost::math::make_tuple(x, x, mp_t(0), result);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rg_00x(mp_t x)
{
   mp_t result = sqrt(x) / 2;
   return boost::math::make_tuple(mp_t(0), mp_t(0), x, result);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rg_0x0(mp_t x)
{
   mp_t result = sqrt(x) / 2;
   return boost::math::make_tuple(mp_t(0), x, mp_t(0), result);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rg_x00(mp_t x)
{
   mp_t result = sqrt(x) / 2;
   return boost::math::make_tuple(x, mp_t(0), mp_t(0), result);
}

boost::math::tuple<mp_t, mp_t, mp_t, mp_t> generate_rg_xy0(mp_t x, mp_t y)
{
   mp_t result = rg_imp(x, y, mp_t(0));
   return boost::math::make_tuple(x, y, mp_t(0), result);
}

int cpp_main(int argc, char*argv[])
{
   using namespace boost::math::tools;

   parameter_info<mp_t> arg1, arg2, arg3;
   test_data<mp_t> data;

   bool cont;
   std::string line;

   if(argc < 1)
      return 1;

   do{
#if 0
      int count;
      std::cout << "Number of points: ";
      std::cin >> count;

      arg1 = make_periodic_param(mp_t(0), mp_t(1), count);
      arg1.type |= dummy_param;

      //
      // Change this next line to get the R variant you want:
      //
      data.insert(&generate_rd_data, arg1);

      std::cout << "Any more data [y/n]?";
      std::getline(std::cin, line);
      boost::algorithm::trim(line);
      cont = (line == "y");
#else
      get_user_parameter_info(arg1, "x");
      get_user_parameter_info(arg2, "y");
      //get_user_parameter_info(arg3, "p");
      arg1.type |= dummy_param;
      arg2.type |= dummy_param;
      //arg3.type |= dummy_param;
      data.insert(generate_rd_data_0xy, arg1, arg2);

      std::cout << "Any more data [y/n]?";
      std::getline(std::cin, line);
      boost::algorithm::trim(line);
      cont = (line == "y");
#endif
   }while(cont);

   std::cout << "Enter name of test data file [default=ellint_rf_data.ipp]";
   std::getline(std::cin, line);
   boost::algorithm::trim(line);
   if(line == "")
      line = "ellint_rf_data.ipp";
   std::ofstream ofs(line.c_str());
   line.erase(line.find('.'));
   ofs << std::scientific << std::setprecision(40);
   write_code(ofs, data, line.c_str());

   return 0;
}


#include "glutil.h"
#include <boost/filesystem.hpp>
#include <cmath>

namespace glow {

Eigen::Matrix4f glTranslate(float x, float y, float z) {
  Eigen::Matrix4f m;
  m << 1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1;
  return m;
}

Eigen::Matrix4f glScale(float x, float y, float z) {
  Eigen::Matrix4f m;
  m << x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1;
  return m;
}

Eigen::Matrix4f glRotateX(float angle) {
  float sin_t = std::sin(angle);
  float cos_t = std::cos(angle);

  Eigen::Matrix4f m;
  m << 1, 0, 0, 0, 0, cos_t, -sin_t, 0, 0, sin_t, cos_t, 0, 0, 0, 0, 1;

  return m;
}

Eigen::Matrix4f glRotateY(float angle) {
  float sin_t = std::sin(angle);
  float cos_t = std::cos(angle);
  Eigen::Matrix4f m;
  m << cos_t, 0, sin_t, 0, 0, 1, 0, 0, -sin_t, 0, cos_t, 0, 0, 0, 0, 1;

  return m;
}

Eigen::Matrix4f glRotateZ(float angle) {
  float sin_t = std::sin(angle);
  float cos_t = std::cos(angle);
  Eigen::Matrix4f m;
  m << cos_t, -sin_t, 0, 0, sin_t, cos_t, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1;
  return m;
}

Eigen::Matrix4f glRotateAxis(float angle, float x, float y, float z) {
  Eigen::Matrix4f m;

  float s = std::sin(angle);
  float c = std::cos(angle);

  m(0, 0) = x * x * (1 - c) + c;
  m(0, 1) = x * y * (1.0f - c) - z * s;
  m(0, 2) = x * z * (1.0f - c) + y * s;
  m(0, 3) = 0.0f;

  m(1, 0) = x * y * (1.0f - c) + z * s;
  m(1, 1) = c + (1.0f - c) * y * y;
  m(1, 2) = y * z * (1.0f - c) - x * s;
  m(1, 3) = 0.0f;

  m(2, 0) = x * z * (1.0f - c) - y * s;
  m(2, 1) = y * z * (1.0f - c) + x * s;
  m(2, 2) = z * z + (1.0f - z * z) * c;
  m(2, 3) = 0.0f;

  m(3, 0) = 0.0f;
  m(3, 1) = 0.0f;
  m(3, 2) = 0.0f;
  m(3, 3) = 1.0f;

  return m;
}

Eigen::Matrix4f glPerspective(float fov, float aspect, float znear, float zfar) {
  // https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml
  assert(znear > 0.0f);
  Eigen::Matrix4f M = Eigen::Matrix4f::Zero();

  // Copied from gluPerspective
  float f = 1.0f / std::tan(0.5f * fov);

  M(0, 0) = f / aspect;
  M(1, 1) = f;
  M(2, 2) = (znear + zfar) / (znear - zfar);
  M(2, 3) = (2.0f * zfar * znear) / (znear - zfar);
  M(3, 2) = -1.0f;

  return M;
}

Eigen::Matrix4f glOrthographic(float left, float right, float bottom, float top, float znear, float zfar) {

  // copied from https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glOrtho.xml

  Eigen::Matrix4f M = Eigen::Matrix4f::Zero();

  M(0, 0) = 2.0f / (right - left);
  M(1, 1) = 2.0f / (top - bottom);
  M(2, 2) = -2.0f / (zfar - znear);

  M(0, 3) = -(right + left) / (right - left);
  M(1, 3) = -(top + bottom) / (top - bottom);
  M(2, 3) = -(zfar + znear) / (zfar - znear);
  M(3, 3) = 1.0f;

  return M;
}

// clang-format off
Eigen::Matrix4f initializeMatrix(float t00, float t01, float t02, float t03,
                                 float t10, float t11, float t12, float t13,
                                 float t20, float t21, float t22, float t23,
                                 float t30, float t31, float t32, float t33) {
  Eigen::Matrix4f m;
  m << t00, t01, t02, t03, t10, t11, t12, t13, t20, t21, t22, t23, t30, t31, t32, t33;
  return m;
}

Eigen::Matrix4f RoSe2GL::matrix = initializeMatrix(0, -1, 0, 0,
                                                   0,  0, 1, 0,
                                                  -1,  0, 0, 0,
                                                   0,  0, 0, 1);

Eigen::Matrix4f GL2RoSe::matrix = initializeMatrix(0, 0, -1, 0,
                                                  -1, 0,  0, 0,
                                                   0, 1,  0, 0,
                                                   0, 0,  0, 1);
// clang-format on

float rgb2float(float r, float g, float b) {
  int32_t rgb = int32_t(round(r * 255.0f));
  rgb = (rgb << 8) + int32_t(round(g * 255.0f));
  rgb = (rgb << 8) + int32_t(round(b * 255.0f));

  return float(rgb);
}

std::string extension(const std::string& path, int32_t level) {
  std::string filename = boost::filesystem::path(path).filename().string();
  if (filename == "" || filename == "." || filename == "..") return "";

  std::string ext;
  while (level-- > 0) {
    std::string::size_type idx = filename.rfind(".");
    if (idx == std::string::npos) break;
    ext.insert(0, filename.substr(idx));
    filename.resize(idx);
  }

  return ext;
}
}

std::ostream& operator<<(std::ostream& stream, glow::vec2& vec) {
  stream << "(" << vec.x << ", " << vec.y << ")";

  return stream;
}

std::ostream& operator<<(std::ostream& stream, glow::vec3& vec) {
  stream << "(" << vec.x << ", " << vec.y << ", " << vec.z << ")";

  return stream;
}

std::ostream& operator<<(std::ostream& stream, glow::vec4& vec) {
  stream << "(" << vec.x << ", " << vec.y << ", " << vec.z << ", " << vec.w << ")";

  return stream;
}

std::ostream& operator<<(std::ostream& stream, const glow::vec2& vec) {
  stream << "(" << vec.x << ", " << vec.y << ")";

  return stream;
}

std::ostream& operator<<(std::ostream& stream, const glow::vec3& vec) {
  stream << "(" << vec.x << ", " << vec.y << ", " << vec.z << ")";

  return stream;
}

std::ostream& operator<<(std::ostream& stream, const glow::vec4& vec) {
  stream << "(" << vec.x << ", " << vec.y << ", " << vec.z << ", " << vec.w << ")";

  return stream;
}