// Copyright Paul A. Bristow 2006, 2017.
// 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)

// test_students_t.cpp

// http://en.wikipedia.org/wiki/Student%27s_t_distribution
// http://www.itl.nist.gov/div898/handbook/eda/section3/eda3664.htm

// Basic sanity test for Student's t probability (quantile) (0. < p < 1).
// and Student's t probability Quantile (0. < p < 1).

#ifdef _MSC_VER
#  pragma warning (disable :4127) // conditional expression is constant.
#endif

#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp> // Boost.Test
#include <boost/test/floating_point_comparison.hpp>

#include <boost/math/concepts/real_concept.hpp> // for real_concept
#include <boost/math/tools/test.hpp> // for real_concept
#include "test_out_of_range.hpp"
#include <boost/math/distributions/students_t.hpp>
    using boost::math::students_t_distribution;

#include <iostream>
   using std::cout;
   using std::endl;
   using std::setprecision;
#include <limits>
  using std::numeric_limits;

template <class RealType>
RealType naive_pdf(RealType v, RealType t)
{
   // Calculate the pdf of the students t in a deliberately
   // naive way, using equation (5) from
   // http://mathworld.wolfram.com/Studentst-Distribution.html
   // This is equivalent to, but a different method
   // to the one in the actual implementation, so can be used as
   // a very basic sanity check.  However some published values
   // would be nice....

   using namespace std;  // for ADL
   using boost::math::beta;

   //return pow(v / (v + t*t), (1+v) / 2) / (sqrt(v) * beta(v/2, RealType(0.5f)));
   RealType result = boost::math::tgamma_ratio((v+1)/2, v/2);
   result /= sqrt(v * boost::math::constants::pi<RealType>());
   result /= pow(1 + t*t/v, (v+1)/2);
   return result;
}

template <class RealType>
void test_spots(RealType)
{
  // Basic sanity checks

   RealType tolerance = static_cast<RealType>(1e-4); // 1e-6 (as %)
   // Some tests only pass at 1e-5 because probability value is less accurate,
   // a digit in 6th decimal place, although calculated using
   // a t-distribution generator (claimed 6 decimal digits) at
  // http://faculty.vassar.edu/lowry/VassarStats.html
   // http://faculty.vassar.edu/lowry/tsamp.html
   // df = 5, +/-t = 2.0, 1-tailed = 0.050970, 2-tailed = 0.101939

   cout << "Tolerance for type " << typeid(RealType).name()  << " is " << tolerance << " %" << endl;

   // http://en.wikipedia.org/wiki/Student%27s_t_distribution#Table_of_selected_values
  // Using tabulated value of t = 3.182 for 0.975, 3 df, one-sided.

   // http://www.mth.kcl.ac.uk/~shaww/web_page/papers/Tdistribution06.pdf refers to:

   // A lookup table of quantiles of the RealType distribution
  // for 1 to 25 in steps of 0.1 is provided in CSV form at:
  // www.mth.kcl.ac.uk/~shaww/web_page/papers/Tsupp/tquantiles.csv
   // gives accurate t of -3.1824463052837 and 3 degrees of freedom.
   // Values below are from this source, saved as tquantiles.xls.
   // DF are across the columns, probabilities down the rows
   // and the t- values (quantiles) are shown.
   // These values are probably accurate to nearly 64-bit double
  // (perhaps 14 decimal digits).

   BOOST_CHECK_CLOSE(
      ::boost::math::cdf(
         students_t_distribution<RealType>(2),       // degrees_of_freedom
         static_cast<RealType>(-6.96455673428326)),  // t
         static_cast<RealType>(0.01),                // probability.
         tolerance); // %

   BOOST_CHECK_CLOSE(
      ::boost::math::cdf(
         students_t_distribution<RealType>(5),       // degrees_of_freedom
         static_cast<RealType>(-3.36492999890721)),  // t
         static_cast<RealType>(0.01),                // probability.
         tolerance);

   BOOST_CHECK_CLOSE(
      ::boost::math::cdf(
         students_t_distribution<RealType>(1),      // degrees_of_freedom
         static_cast<RealType>(-31830.988607907)),  // t
         static_cast<RealType>(0.00001),            // probability.
         tolerance);

   BOOST_CHECK_CLOSE(
      ::boost::math::cdf(
         students_t_distribution<RealType>(25.),    // degrees_of_freedom
         static_cast<RealType>(-5.2410429995425)),  // t
         static_cast<RealType>(0.00001),            // probability.
         tolerance);

   BOOST_CHECK_CLOSE(
      ::boost::math::cdf(
         students_t_distribution<RealType>(1),   // degrees_of_freedom
         static_cast<RealType>(-63661.97723)),   // t
         static_cast<RealType>(0.000005),        // probability.
         tolerance);

    BOOST_CHECK_CLOSE(
      ::boost::math::cdf(
         students_t_distribution<RealType>(5.),  // degrees_of_freedom
         static_cast<RealType>(-17.89686614)),   // t
         static_cast<RealType>(0.000005),        // probability.
         tolerance);

    BOOST_CHECK_CLOSE(
      ::boost::math::cdf(
         students_t_distribution<RealType>(25.),  // degrees_of_freedom
         static_cast<RealType>(-5.510848412)),    // t
         static_cast<RealType>(0.000005),         // probability.
         tolerance);

  BOOST_CHECK_CLOSE(
      ::boost::math::cdf(
         students_t_distribution<RealType>(10.),  // degrees_of_freedom
         static_cast<RealType>(-1.812461123)),    // t
         static_cast<RealType>(0.05),             // probability.
         tolerance);

  BOOST_CHECK_CLOSE(
      ::boost::math::cdf(
         students_t_distribution<RealType>(10),  // degrees_of_freedom
         static_cast<RealType>(1.812461123)),    // t
         static_cast<RealType>(0.95),            // probability.
         tolerance);

  BOOST_CHECK_CLOSE(
      ::boost::math::cdf(
         complement(
            students_t_distribution<RealType>(10),  // degrees_of_freedom
            static_cast<RealType>(1.812461123))),    // t
         static_cast<RealType>(0.05),            // probability.
         tolerance);

  BOOST_CHECK_CLOSE(
      ::boost::math::cdf(
         students_t_distribution<RealType>(10),  // degrees_of_freedom
         static_cast<RealType>(9.751995491)),    // t
         static_cast<RealType>(0.999999),        // probability.
         tolerance);

  BOOST_CHECK_CLOSE(
      ::boost::math::cdf(
         students_t_distribution<RealType>(10.),  // degrees_of_freedom - for ALL degrees_of_freedom!
         static_cast<RealType>(0.)),              // t
         static_cast<RealType>(0.5),              // probability.
         tolerance);

   // Student's t Inverse function tests.
  // Special cases

  BOOST_MATH_CHECK_THROW(boost::math::quantile(
         students_t_distribution<RealType>(1.),  // degrees_of_freedom (ignored).
         static_cast<RealType>(0)), std::overflow_error); // t == -infinity.

  BOOST_MATH_CHECK_THROW(boost::math::quantile(
         students_t_distribution<RealType>(1.),  // degrees_of_freedom (ignored).
         static_cast<RealType>(1)), std::overflow_error); // t == +infinity.

  BOOST_CHECK_EQUAL(boost::math::quantile(
         students_t_distribution<RealType>(1.),  // degrees_of_freedom (ignored).
         static_cast<RealType>(0.5)),  //  probability == half - special case.
         static_cast<RealType>(0)); // t == zero.

  BOOST_CHECK_EQUAL(boost::math::quantile(
         complement(
            students_t_distribution<RealType>(1.),  // degrees_of_freedom (ignored).
            static_cast<RealType>(0.5))),  //  probability == half - special case.
         static_cast<RealType>(0)); // t == zero.

  BOOST_CHECK_CLOSE(boost::math::quantile(
         students_t_distribution<RealType>(1.),  // degrees_of_freedom (ignored).
         static_cast<RealType>(0.5)),  //  probability == half - special case.
         static_cast<RealType>(0), // t == zero.
         tolerance);

   BOOST_CHECK_CLOSE( // Tests of p middling.
      ::boost::math::cdf(
         students_t_distribution<RealType>(5.),  // degrees_of_freedom
         static_cast<RealType>(-0.559429644)),  // t
         static_cast<RealType>(0.3), // probability.
         tolerance);
   BOOST_CHECK_CLOSE(
      ::boost::math::quantile(
         students_t_distribution<RealType>(5.),  // degrees_of_freedom
         static_cast<RealType>(0.3)),  // probability.
         static_cast<RealType>(-0.559429644), // t
         tolerance);

   BOOST_CHECK_CLOSE(
      ::boost::math::quantile(
         complement(
            students_t_distribution<RealType>(5.),  // degrees_of_freedom
            static_cast<RealType>(0.7))),  // probability.
         static_cast<RealType>(-0.559429644), // t
         tolerance);

   BOOST_CHECK_CLOSE( // Tests of p high.
      ::boost::math::cdf(
         students_t_distribution<RealType>(5.),  // degrees_of_freedom
         static_cast<RealType>(1.475884049)),  // t
         static_cast<RealType>(0.9), // probability.
         tolerance);
   BOOST_CHECK_CLOSE(
      ::boost::math::quantile(
         students_t_distribution<RealType>(5.),  // degrees_of_freedom
         static_cast<RealType>(0.9)),  // probability.
         static_cast<RealType>(1.475884049), // t
         tolerance);

   BOOST_CHECK_CLOSE( // Tests of p low.
      ::boost::math::cdf(
         students_t_distribution<RealType>(5.),  // degrees_of_freedom
         static_cast<RealType>(-1.475884049)),  // t
         static_cast<RealType>(0.1), // probability.
         tolerance);
   BOOST_CHECK_CLOSE(
      ::boost::math::quantile(
         students_t_distribution<RealType>(5.),  // degrees_of_freedom
         static_cast<RealType>(0.1)),  // probability.
         static_cast<RealType>(-1.475884049), // t
         tolerance);

   BOOST_CHECK_CLOSE(
      ::boost::math::cdf(
         students_t_distribution<RealType>(2.),  // degrees_of_freedom
         static_cast<RealType>(-6.96455673428326)),  // t
         static_cast<RealType>(0.01), // probability.
         tolerance);

   BOOST_CHECK_CLOSE(
      ::boost::math::quantile(
         students_t_distribution<RealType>(2.),  // degrees_of_freedom
         static_cast<RealType>(0.01)),  // probability.
         static_cast<RealType>(-6.96455673428326), // t
         tolerance);

      //
      // Some special tests to exercise the double-precision approximations
      // to the quantile:
      //
      // tolerance is 50 eps expressed as a persent:
      //
      tolerance = boost::math::tools::epsilon<RealType>() * 5000;
      BOOST_CHECK_CLOSE(boost::math::quantile(
         students_t_distribution<RealType>(2.00390625L),                     // degrees_of_freedom.
         static_cast<RealType>(0.5625L)),                                    //  probability.
         static_cast<RealType>(0.178133131573788108465134803511798566L),     // t.
         tolerance);
      BOOST_CHECK_CLOSE(boost::math::quantile(
         students_t_distribution<RealType>(1L),                              // degrees_of_freedom.
         static_cast<RealType>(0.03125L)),                                   //  probability.
         static_cast<RealType>(-10.1531703876088604621071476634194722L),     // t.
         tolerance);
      BOOST_CHECK_CLOSE(boost::math::quantile(
         students_t_distribution<RealType>(1L),                            // degrees_of_freedom.
         static_cast<RealType>(0.875L)),                                   //  probability.
         static_cast<RealType>(2.41421356237309504880168872421390942L),    // t.
         tolerance);
      BOOST_CHECK_CLOSE(boost::math::quantile(
         students_t_distribution<RealType>(2L),                              // degrees_of_freedom.
         static_cast<RealType>(0.03125L)),                                   //  probability.
         static_cast<RealType>(-3.81000381000571500952501666878143315L),     // t.
         tolerance);
      BOOST_CHECK_CLOSE(boost::math::quantile(
         students_t_distribution<RealType>(2L),                            // degrees_of_freedom.
         static_cast<RealType>(0.875L)),                                   //  probability.
         static_cast<RealType>(1.60356745147454630810732088527854144L),    // t.
         tolerance);
      BOOST_CHECK_CLOSE(boost::math::quantile(
         students_t_distribution<RealType>(4L),                              // degrees_of_freedom.
         static_cast<RealType>(0.03125L)),                                   //  probability.
         static_cast<RealType>(-2.56208431914409044861223047927635034L),     // t.
         tolerance);
      BOOST_CHECK_CLOSE(boost::math::quantile(
         students_t_distribution<RealType>(4L),                            // degrees_of_freedom.
         static_cast<RealType>(0.875L)),                                   //  probability.
         static_cast<RealType>(1.34439755550909142430681981315923574L),    // t.
         tolerance);
      BOOST_CHECK_CLOSE(boost::math::quantile(
         students_t_distribution<RealType>(6L),                              // degrees_of_freedom.
         static_cast<RealType>(0.03125L)),                                   //  probability.
         static_cast<RealType>(-2.28348667906973065861212495010082952L),     // t.
         tolerance);
      BOOST_CHECK_CLOSE(boost::math::quantile(
         students_t_distribution<RealType>(6L),                            // degrees_of_freedom.
         static_cast<RealType>(0.875L)),                                   //  probability.
         static_cast<RealType>(1.27334930914664286821103236660071906L),    // t.
         tolerance);
      BOOST_CHECK_CLOSE(boost::math::quantile(
         students_t_distribution<RealType>(8L),                              // degrees_of_freedom.
         static_cast<RealType>(0.03125L)),                                   //  probability.
         static_cast<RealType>(-2.16296475406014719458642055768894376L),     // t.
         tolerance);
      BOOST_CHECK_CLOSE(boost::math::quantile(
         students_t_distribution<RealType>(8L),                            // degrees_of_freedom.
         static_cast<RealType>(0.875L)),                                   //  probability.
         static_cast<RealType>(1.24031826078267310637634677726479038L),    // t.
         tolerance);
      BOOST_CHECK_CLOSE(boost::math::quantile(
         students_t_distribution<RealType>(10L),                             // degrees_of_freedom.
         static_cast<RealType>(0.03125L)),                                   //  probability.
         static_cast<RealType>(-2.09596136475109350926340169211429572L),     // t.
         tolerance);
      BOOST_CHECK_CLOSE(boost::math::quantile(
         students_t_distribution<RealType>(10L),                         // degrees_of_freedom.
         static_cast<RealType>(0.875L)),                                 //  probability.
         static_cast<RealType>(1.2212553950039221407185188573696834L),   // t.
         tolerance);
      BOOST_CHECK_CLOSE(boost::math::quantile(
         students_t_distribution<RealType>(2.125L),                          // degrees_of_freedom.
         static_cast<RealType>(0.03125L)),                                   //  probability.
         static_cast<RealType>(-3.62246031671091980110493455859296532L),     // t.
         tolerance);
      BOOST_CHECK_CLOSE(boost::math::quantile(
         students_t_distribution<RealType>(2.125L),                        // degrees_of_freedom.
         static_cast<RealType>(0.875L)),                                   //  probability.
         static_cast<RealType>(1.56905270993307293450392958697861969L),    // t.
         tolerance);
      BOOST_CHECK_CLOSE(boost::math::quantile(
         students_t_distribution<RealType>(3L),                              // degrees_of_freedom.
         static_cast<RealType>(0.03125L)),                                   //  probability.
         static_cast<RealType>(-2.90004411882995814036141778367917946L),     // t.
         tolerance);
      BOOST_CHECK_CLOSE(boost::math::quantile(
         students_t_distribution<RealType>(3L),                            // degrees_of_freedom.
         static_cast<RealType>(0.875L)),                                   //  probability.
         static_cast<RealType>(1.42262528146180931868169289781115099L),    // t.
         tolerance);

      if(boost::is_floating_point<RealType>::value)
      {
         BOOST_CHECK_CLOSE(boost::math::cdf(
            students_t_distribution<RealType>(1e30f),
               boost::math::quantile(
                  students_t_distribution<RealType>(1e30f), static_cast<RealType>(0.25f))),
            static_cast<RealType>(0.25f), tolerance);
         BOOST_CHECK_CLOSE(boost::math::cdf(
            students_t_distribution<RealType>(1e20f),
               boost::math::quantile(
                  students_t_distribution<RealType>(1e20f), static_cast<RealType>(0.25f))),
            static_cast<RealType>(0.25f), tolerance);
         BOOST_CHECK_CLOSE(boost::math::cdf(
            students_t_distribution<RealType>(static_cast<RealType>(0x7FFFFFFF)),
               boost::math::quantile(
                  students_t_distribution<RealType>(static_cast<RealType>(0x7FFFFFFF)), static_cast<RealType>(0.25f))),
            static_cast<RealType>(0.25f), tolerance);
         BOOST_CHECK_CLOSE(boost::math::cdf(
            students_t_distribution<RealType>(static_cast<RealType>(0x10000000)),
               boost::math::quantile(
                  students_t_distribution<RealType>(static_cast<RealType>(0x10000000)), static_cast<RealType>(0.25f))),
            static_cast<RealType>(0.25f), tolerance);
         BOOST_CHECK_CLOSE(boost::math::cdf(
            students_t_distribution<RealType>(static_cast<RealType>(0x0fffffff)),
               boost::math::quantile(
                  students_t_distribution<RealType>(static_cast<RealType>(0x0fffffff)), static_cast<RealType>(0.25f))),
            static_cast<RealType>(0.25f), tolerance);
      }

  // Student's t pdf tests.
  // for PDF checks, use 100 eps tolerance expressed as a percent:
   tolerance = boost::math::tools::epsilon<RealType>() * 10000;

   for(unsigned i = 1; i < 20; i += 3)
   {
      for(RealType r = -10; r < 10; r += 0.125)
      {
         //std::cout << "df=" << i << " t=" << r << std::endl;
         BOOST_CHECK_CLOSE(
            boost::math::pdf(
               students_t_distribution<RealType>(static_cast<RealType>(i)),
               r),
            naive_pdf<RealType>(static_cast<RealType>(i), r),
            tolerance);
      }
   }

    RealType tol2 = boost::math::tools::epsilon<RealType>() * 5;
    students_t_distribution<RealType> dist(8);
    RealType x = static_cast<RealType>(0.125);
    using namespace std; // ADL of std names.
    // mean:
    BOOST_CHECK_CLOSE(
       mean(dist)
       , static_cast<RealType>(0), tol2);
    // variance:
 //   BOOST_CHECK_CLOSE(
 //      variance(dist)
 //      , static_cast<RealType>(13.0L / 6.0L), tol2);
 //// was     , static_cast<RealType>(8.0L / 6.0L), tol2);
    // std deviation:
    BOOST_CHECK_CLOSE(
       standard_deviation(dist)
       , static_cast<RealType>(sqrt(8.0L / 6.0L)), tol2);
    // hazard:
    BOOST_CHECK_CLOSE(
       hazard(dist, x)
       , pdf(dist, x) / cdf(complement(dist, x)), tol2);
    // cumulative hazard:
    BOOST_CHECK_CLOSE(
       chf(dist, x)
       , -log(cdf(complement(dist, x))), tol2);
    // coefficient_of_variation:
    BOOST_MATH_CHECK_THROW(
       coefficient_of_variation(dist),
       std::overflow_error);
    // mode:
    BOOST_CHECK_CLOSE(
       mean(dist)
       , static_cast<RealType>(0), tol2);
    // median:
    BOOST_CHECK_CLOSE(
       median(dist)
       , static_cast<RealType>(0), tol2);
    // skewness:
    BOOST_CHECK_CLOSE(
       skewness(dist)
       , static_cast<RealType>(0), tol2);
    // kurtosis:
    BOOST_CHECK_CLOSE(
       kurtosis(dist)
       , static_cast<RealType>(4.5), tol2);
    // kurtosis excess:
    BOOST_CHECK_CLOSE(
       kurtosis_excess(dist)
       , static_cast<RealType>(1.5), tol2);

    // Parameter estimation. These results are close to but
    // not identical to those reported on the NIST website at
    // http://www.itl.nist.gov/div898/handbook/prc/section2/prc222.htm
    // the NIST results appear to be calculated using a normal
    // approximation, which slightly under-estimates the degrees of
    // freedom required, particularly when the result is small.
    //
    BOOST_CHECK_EQUAL(
       ceil(students_t_distribution<RealType>::find_degrees_of_freedom(
         static_cast<RealType>(0.5),
         static_cast<RealType>(0.005),
         static_cast<RealType>(0.01),
         static_cast<RealType>(1.0))),
         99);
    BOOST_CHECK_EQUAL(
       ceil(students_t_distribution<RealType>::find_degrees_of_freedom(
         static_cast<RealType>(1.5),
         static_cast<RealType>(0.005),
         static_cast<RealType>(0.01),
         static_cast<RealType>(1.0))),
         14);
    BOOST_CHECK_EQUAL(
       ceil(students_t_distribution<RealType>::find_degrees_of_freedom(
         static_cast<RealType>(0.5),
         static_cast<RealType>(0.025),
         static_cast<RealType>(0.01),
         static_cast<RealType>(1.0))),
         76);
    BOOST_CHECK_EQUAL(
       ceil(students_t_distribution<RealType>::find_degrees_of_freedom(
         static_cast<RealType>(1.5),
         static_cast<RealType>(0.025),
         static_cast<RealType>(0.01),
         static_cast<RealType>(1.0))),
         11);
    BOOST_CHECK_EQUAL(
       ceil(students_t_distribution<RealType>::find_degrees_of_freedom(
         static_cast<RealType>(0.5),
         static_cast<RealType>(0.05),
         static_cast<RealType>(0.01),
         static_cast<RealType>(1.0))),
         65);
    BOOST_CHECK_EQUAL(
       ceil(students_t_distribution<RealType>::find_degrees_of_freedom(
         static_cast<RealType>(1.5),
         static_cast<RealType>(0.05),
         static_cast<RealType>(0.01),
         static_cast<RealType>(1.0))),
         9);

    // Test for large degrees of freedom when should be same as normal.
    RealType inf = std::numeric_limits<RealType>::infinity();
    RealType nan = std::numeric_limits<RealType>::quiet_NaN();

    std::string type = typeid(RealType).name();
//    if (type != "class boost::math::concepts::real_concept") fails for gcc
    if (typeid(RealType) != typeid(boost::math::concepts::real_concept))
    { // Ordinary floats only.
      RealType limit = 1/ boost::math::tools::epsilon<RealType>();
      // Default policy to get full accuracy.
      // std::cout << "Switch over to normal if df > " << limit << std::endl;
      // float Switch over to normal if df > 8.38861e+006
      // double Switch over to normal if df > 4.5036e+015
      // Can't test real_concept - doesn't converge.

      boost::math::normal_distribution<RealType> n(0, 1); //
      students_t_distribution<RealType> st(boost::math::tools::max_value<RealType>()); // Well over the switchover point,
      // PDF
      BOOST_CHECK_EQUAL(pdf(st, 0), pdf(n, 0.)); // Should be exactly equal.

      students_t_distribution<RealType> st2(limit /5 ); // Just below the switchover point,
      BOOST_CHECK_CLOSE_FRACTION(pdf(st2, 0), pdf(n, 0.), tolerance); // Should be very close to normal.
      // CDF
      BOOST_CHECK_EQUAL(cdf(st, 0), cdf(n, 0.)); // Should be exactly equal.
      BOOST_CHECK_CLOSE_FRACTION(cdf(st2, 0), cdf(n, 0.), tolerance); // Should be very close to normal.

      // Tests for df = infinity.
      students_t_distribution<RealType> infdf(inf);
      BOOST_CHECK_EQUAL(infdf.degrees_of_freedom(), inf);
      BOOST_CHECK_EQUAL(mean(infdf), 0); // OK.
#ifndef BOOST_NO_EXCEPTIONS
      BOOST_MATH_CHECK_THROW(students_t_distribution<RealType> minfdf(-inf), std::domain_error);
      BOOST_MATH_CHECK_THROW(students_t_distribution<RealType> minfdf(nan), std::domain_error);
      BOOST_MATH_CHECK_THROW(students_t_distribution<RealType> minfdf(-nan), std::domain_error);
#endif
      BOOST_CHECK_EQUAL(pdf(infdf, -inf), 0);
      BOOST_CHECK_EQUAL(pdf(infdf, +inf), 0);
      BOOST_CHECK_EQUAL(cdf(infdf, -inf), 0);
      BOOST_CHECK_EQUAL(cdf(infdf, +inf), 1);

     // BOOST_CHECK_CLOSE_FRACTION(pdf(infdf, 0), static_cast<RealType>(0.3989422804014326779399460599343818684759L), tolerance);
      BOOST_CHECK_CLOSE_FRACTION(pdf(infdf, 0),boost::math::constants::one_div_root_two_pi<RealType>() , tolerance);
      BOOST_CHECK_CLOSE_FRACTION(cdf(infdf, 0),boost::math::constants::half<RealType>() , tolerance);

    // Checks added for Trac #7717 report by Thomas Mang.

    BOOST_MATH_CHECK_THROW(quantile(dist, -1), std::domain_error);
    BOOST_MATH_CHECK_THROW(quantile(dist, 2), std::domain_error);
    BOOST_MATH_CHECK_THROW(pdf(students_t_distribution<RealType>(0), 0), std::domain_error);
    BOOST_MATH_CHECK_THROW(pdf(students_t_distribution<RealType>(-1), 0), std::domain_error);

    // Check on df for mean (moment k = 1)
    BOOST_MATH_CHECK_THROW(mean(students_t_distribution<RealType>(nan)), std::domain_error);
//    BOOST_MATH_CHECK_THROW(mean(students_t_distribution<RealType>(inf)), std::domain_error); inf is now OK
    BOOST_MATH_CHECK_THROW(mean(students_t_distribution<RealType>(-1)), std::domain_error);
    BOOST_MATH_CHECK_THROW(mean(students_t_distribution<RealType>(0)), std::domain_error);
    BOOST_MATH_CHECK_THROW(mean(students_t_distribution<RealType>(1)), std::domain_error); // df == k
    BOOST_CHECK_EQUAL(mean(students_t_distribution<RealType>(2)), 0); // OK.
    BOOST_CHECK_EQUAL(mean(students_t_distribution<RealType>(inf)), 0); // OK.

    // Check on df for variance (moment 2)
    BOOST_MATH_CHECK_THROW(variance(students_t_distribution<RealType>(nan)), std::domain_error);
//    BOOST_MATH_CHECK_THROW(variance(students_t_distribution<RealType>(inf)), std::domain_error); // inf is now OK.
    BOOST_MATH_CHECK_THROW(variance(students_t_distribution<RealType>(-1)), std::domain_error);
    BOOST_MATH_CHECK_THROW(variance(students_t_distribution<RealType>(0)), std::domain_error);
    BOOST_MATH_CHECK_THROW(variance(students_t_distribution<RealType>(1)), std::domain_error);
    BOOST_MATH_CHECK_THROW(variance(students_t_distribution<RealType>(static_cast<RealType>(1.99999L))), std::domain_error);
    BOOST_MATH_CHECK_THROW(variance(students_t_distribution<RealType>(static_cast<RealType>(1.99999L))), std::domain_error);
    BOOST_MATH_CHECK_THROW(variance(students_t_distribution<RealType>(2)), std::domain_error); // df ==
    BOOST_CHECK_EQUAL(variance(students_t_distribution<RealType>(2.5)), 5); // OK.
    BOOST_CHECK_EQUAL(variance(students_t_distribution<RealType>(3)), 3); // OK.
    BOOST_CHECK_EQUAL(variance(students_t_distribution<RealType>(inf)), 1); // OK.

    // Check on df for skewness (moment 3)
    BOOST_MATH_CHECK_THROW(skewness(students_t_distribution<RealType>(nan)), std::domain_error);
    BOOST_MATH_CHECK_THROW(skewness(students_t_distribution<RealType>(-1)), std::domain_error);
    BOOST_MATH_CHECK_THROW(skewness(students_t_distribution<RealType>(0)), std::domain_error);
    BOOST_MATH_CHECK_THROW(skewness(students_t_distribution<RealType>(1)), std::domain_error);
    BOOST_MATH_CHECK_THROW(skewness(students_t_distribution<RealType>(1.5L)), std::domain_error);
    BOOST_MATH_CHECK_THROW(skewness(students_t_distribution<RealType>(2)), std::domain_error);
    BOOST_MATH_CHECK_THROW(skewness(students_t_distribution<RealType>(3)), std::domain_error); // df == k
    BOOST_CHECK_EQUAL(skewness(students_t_distribution<RealType>(3.5)), 0); // OK.
    BOOST_CHECK_EQUAL(skewness(students_t_distribution<RealType>(4)), 0); // OK.
    BOOST_CHECK_EQUAL(skewness(students_t_distribution<RealType>(inf)), 0); // OK.

    // Check on df for kurtosis_excess (moment 4)
    BOOST_MATH_CHECK_THROW(kurtosis_excess(students_t_distribution<RealType>(nan)), std::domain_error);
    BOOST_MATH_CHECK_THROW(kurtosis_excess(students_t_distribution<RealType>(-1)), std::domain_error);
    BOOST_MATH_CHECK_THROW(kurtosis_excess(students_t_distribution<RealType>(0)), std::domain_error);
    BOOST_MATH_CHECK_THROW(kurtosis_excess(students_t_distribution<RealType>(1)), std::domain_error);
    BOOST_MATH_CHECK_THROW(kurtosis_excess(students_t_distribution<RealType>(1.5L)), std::domain_error);
    BOOST_MATH_CHECK_THROW(kurtosis_excess(students_t_distribution<RealType>(2)), std::domain_error);
    BOOST_MATH_CHECK_THROW(kurtosis(students_t_distribution<RealType>(static_cast<RealType>(2.1))), std::domain_error);
    BOOST_MATH_CHECK_THROW(kurtosis_excess(students_t_distribution<RealType>(3)), std::domain_error);
    BOOST_MATH_CHECK_THROW(kurtosis_excess(students_t_distribution<RealType>(4)), std::domain_error); // df == k
    BOOST_CHECK_EQUAL(kurtosis_excess(students_t_distribution<RealType>(5)), 6); // OK.
    BOOST_CHECK_EQUAL(kurtosis_excess(students_t_distribution<RealType>(inf)), 0); // OK.

    // Check on df for kurtosis (moment 4)
    BOOST_MATH_CHECK_THROW(kurtosis(students_t_distribution<RealType>(nan)), std::domain_error);
    BOOST_MATH_CHECK_THROW(kurtosis(students_t_distribution<RealType>(-1)), std::domain_error);
    BOOST_MATH_CHECK_THROW(kurtosis(students_t_distribution<RealType>(0)), std::domain_error);
    BOOST_MATH_CHECK_THROW(kurtosis(students_t_distribution<RealType>(1)), std::domain_error);
    BOOST_MATH_CHECK_THROW(kurtosis(students_t_distribution<RealType>(2)), std::domain_error);
    BOOST_MATH_CHECK_THROW(kurtosis(students_t_distribution<RealType>(static_cast<RealType>(2.0001L))), std::domain_error);
    BOOST_MATH_CHECK_THROW(kurtosis(students_t_distribution<RealType>(3)), std::domain_error);
    BOOST_MATH_CHECK_THROW(kurtosis(students_t_distribution<RealType>(4)), std::domain_error); // df == k
    BOOST_CHECK_EQUAL(kurtosis(students_t_distribution<RealType>(5)), 9); // OK.
    BOOST_CHECK_EQUAL(kurtosis(students_t_distribution<RealType>(inf)), 3); // OK.

   }

    // Use a new distribution ignore_error_students_t with a custom policy to ignore all errors,
    // and check returned values are as expected.

    /*
     Sandia-darwin-intel-12.0 - math - test_students_t / intel-darwin-12.0
    ../libs/math/test/test_students_t.cpp(544): error: "domain_error" has already been declared in the current scope
    using boost::math::policies::domain_error;

../libs/math/test/test_students_t.cpp(552): error: "pole_error" has already been declared in the current scope
    using boost::math::policies::pole_error;

    Unclear where previous declaration is.
    Does not seem to be in student_t.hpp or any included files???

    So to avoid this perceived problem by this compiler,
    the ignore policy below uses fully specified names.
    */

    using boost::math::policies::policy;
  // Types of error whose action can be altered by policies:.
  //using boost::math::policies::evaluation_error;
  //using boost::math::policies::domain_error;
  //using boost::math::policies::overflow_error;
  //using boost::math::policies::underflow_error;
  //using boost::math::policies::domain_error;
  //using boost::math::policies::pole_error;

  //// Actions on error (in enum error_policy_type):
  //using boost::math::policies::errno_on_error;
  //using boost::math::policies::ignore_error;
  //using boost::math::policies::throw_on_error;
  //using boost::math::policies::denorm_error;
  //using boost::math::policies::pole_error;
  //using boost::math::policies::user_error;

  typedef policy<
    boost::math::policies::domain_error<boost::math::policies::ignore_error>,
    boost::math::policies::overflow_error<boost::math::policies::ignore_error>,
    boost::math::policies::underflow_error<boost::math::policies::ignore_error>,
    boost::math::policies::denorm_error<boost::math::policies::ignore_error>,
    boost::math::policies::pole_error<boost::math::policies::ignore_error>,
    boost::math::policies::evaluation_error<boost::math::policies::ignore_error>
              > my_ignore_policy;

  typedef students_t_distribution<RealType, my_ignore_policy> ignore_error_students_t;

  // Only test NaN and infinity if type has these features (realconcept returns zero).
  // Integers are always converted to RealType,
  // others requires static cast to RealType from long double.

  if(std::numeric_limits<RealType>::has_quiet_NaN)
  {
  // Mean
    BOOST_CHECK((boost::math::isnan)(mean(ignore_error_students_t(-1))));
    BOOST_CHECK((boost::math::isnan)(mean(ignore_error_students_t(0))));
    BOOST_CHECK((boost::math::isnan)(mean(ignore_error_students_t(1))));

    // Variance
    BOOST_CHECK((boost::math::isnan)(variance(ignore_error_students_t(std::numeric_limits<RealType>::quiet_NaN()))));
    BOOST_CHECK((boost::math::isnan)(variance(ignore_error_students_t(-1))));
    BOOST_CHECK((boost::math::isnan)(variance(ignore_error_students_t(0))));
    BOOST_CHECK((boost::math::isnan)(variance(ignore_error_students_t(1))));
    BOOST_CHECK((boost::math::isnan)(variance(ignore_error_students_t(static_cast<RealType>(1.7L)))));
    BOOST_CHECK((boost::math::isnan)(variance(ignore_error_students_t(2))));

  // Skewness
    BOOST_CHECK((boost::math::isnan)(skewness(ignore_error_students_t(std::numeric_limits<RealType>::quiet_NaN()))));
    BOOST_CHECK((boost::math::isnan)(skewness(ignore_error_students_t(-1))));
    BOOST_CHECK((boost::math::isnan)(skewness(ignore_error_students_t(0))));
    BOOST_CHECK((boost::math::isnan)(skewness(ignore_error_students_t(1))));
    BOOST_CHECK((boost::math::isnan)(skewness(ignore_error_students_t(2))));
    BOOST_CHECK((boost::math::isnan)(skewness(ignore_error_students_t(3))));

  // Kurtosis
    BOOST_CHECK((boost::math::isnan)(kurtosis(ignore_error_students_t(std::numeric_limits<RealType>::quiet_NaN()))));
    BOOST_CHECK((boost::math::isnan)(kurtosis(ignore_error_students_t(-1))));
    BOOST_CHECK((boost::math::isnan)(kurtosis(ignore_error_students_t(0))));
    BOOST_CHECK((boost::math::isnan)(kurtosis(ignore_error_students_t(1))));
    BOOST_CHECK((boost::math::isnan)(kurtosis(ignore_error_students_t(2))));
    BOOST_CHECK((boost::math::isnan)(kurtosis(ignore_error_students_t(static_cast<RealType>(2.0001L)))));
    BOOST_CHECK((boost::math::isnan)(kurtosis(ignore_error_students_t(3))));
    BOOST_CHECK((boost::math::isnan)(kurtosis(ignore_error_students_t(4))));

    // Kurtosis excess
    BOOST_CHECK((boost::math::isnan)(kurtosis_excess(ignore_error_students_t(std::numeric_limits<RealType>::quiet_NaN()))));
    BOOST_CHECK((boost::math::isnan)(kurtosis_excess(ignore_error_students_t(-1))));
    BOOST_CHECK((boost::math::isnan)(kurtosis_excess(ignore_error_students_t(0))));
    BOOST_CHECK((boost::math::isnan)(kurtosis_excess(ignore_error_students_t(1))));
    BOOST_CHECK((boost::math::isnan)(kurtosis_excess(ignore_error_students_t(2))));
    BOOST_CHECK((boost::math::isnan)(kurtosis_excess(ignore_error_students_t(static_cast<RealType>(2.0001L)))));
    BOOST_CHECK((boost::math::isnan)(kurtosis_excess(ignore_error_students_t(3))));
    BOOST_CHECK((boost::math::isnan)(kurtosis_excess(ignore_error_students_t(4))));
  } // has_quiet_NaN

  BOOST_CHECK(boost::math::isfinite(mean(ignore_error_students_t(1 + std::numeric_limits<RealType>::epsilon()))));
  BOOST_CHECK(boost::math::isfinite(variance(ignore_error_students_t(2 + 2 * std::numeric_limits<RealType>::epsilon()))));
  BOOST_CHECK(boost::math::isfinite(variance(ignore_error_students_t(static_cast<RealType>(2.0001L)))));
  BOOST_CHECK(boost::math::isfinite(variance(ignore_error_students_t(2 + 2 * std::numeric_limits<RealType>::epsilon()))));
  BOOST_CHECK(boost::math::isfinite(skewness(ignore_error_students_t(3 + 3 * std::numeric_limits<RealType>::epsilon()))));
  BOOST_CHECK(boost::math::isfinite(kurtosis(ignore_error_students_t(4 + 4 * std::numeric_limits<RealType>::epsilon()))));
  BOOST_CHECK(boost::math::isfinite(kurtosis(ignore_error_students_t(static_cast<RealType>(4.0001L)))));

  // check_out_of_range<students_t_distribution<RealType> >(1);
  // Cannot be used because fails "exception std::domain_error is expected but not raised"
  // if df = +infinity is allowed, must use new version that allows skipping infinity tests.
  // Infinite == true

  check_support<students_t_distribution<RealType> >(students_t_distribution<RealType>(1), true);

} // template <class RealType>void test_spots(RealType)

BOOST_AUTO_TEST_CASE( test_main )
{
  // Check that can construct students_t distribution using the two convenience methods:
  using namespace boost::math;
  students_t myst1(2); // Using typedef
  students_t_distribution<> myst2(2); // Using default RealType double.
   //students_t_distribution<double> myst3(2); // Using explicit RealType double.

   // Basic sanity-check spot values.
   // (Parameter value, arbitrarily zero, only communicates the floating point type).
  test_spots(0.0F); // Test float. OK at decdigits = 0 tolerance = 0.0001 %
  test_spots(0.0); // Test double. OK at decdigits 7, tolerance = 1e07 %
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
  test_spots(0.0L); // Test long double.
#if !BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582))
  test_spots(boost::math::concepts::real_concept(0.)); // Test real concept.
#endif
#else
   std::cout << "<note>The long double tests have been disabled on this platform "
      "either because the long double overloads of the usual math functions are "
      "not available at all, or because they are too inaccurate for these tests "
      "to pass.</note>" << std::endl;
#endif

} // BOOST_AUTO_TEST_CASE( test_main )

/*

Autorun "i:\boost-06-05-03-1300\libs\math\test\Math_test\debug\test_students_t.exe"
Running 1 test case...
Tolerance for type float is 0.0001 %
Tolerance for type double is 0.0001 %
Tolerance for type long double is 0.0001 %
Tolerance for type class boost::math::concepts::real_concept is 0.0001 %
*** No errors detected

*/


#include "problemes.h"
#include "arithmetique.h"

#include <fstream>

#include <boost/range/adaptor/reversed.hpp>
#include <boost/math/constants/constants.hpp>

typedef unsigned long long nombre;
typedef std::vector<nombre> vecteur;

namespace {
    const long double epsilon = 1e-9L;

    struct comparedouble {
        bool operator()(const long double &lhs, const long double &rhs) const {
            return std::abs(lhs - rhs) < epsilon ? false : lhs < rhs;
        }
    };
}

ENREGISTRER_PROBLEME(177, "Integer angled Quadrilaterals") {
    // Let ABCD be a convex quadrilateral, with diagonals AC and BD. At each vertex the diagonal makes an angle with
    // each of the two sides, creating eight corner angles.
    //
    // For example, at vertex A, the two angles are CAD, CAB.
    //
    // We call such a quadrilateral for which all eight corner angles have integer values when measured in degrees an
    // "integer angled quadrilateral". An example of an integer angled quadrilateral is a square, where all eight corner
    // angles are 45°. Another example is given by DAC = 20°, BAC = 60°, ABD = 50°, CBD = 30°, BCA = 40°, DCA = 30°,
    // CDB = 80°, ADB = 50°.
    //
    // What is the total number of non-similar integer angled quadrilaterals?
    //
    // Note: In your calculations you may assume that a calculated angle is integral if it is within a tolerance of 10-9
    // of an integer value.
    std::vector<long double> sinus(180, 0.0L);
    std::vector<long double> cosinus(180, 0.0L);
    std::vector<long double> rad(180, 0.0L);
    std::vector<bool> prevalue(10000 + 1, false);

    std::vector<nombre> solution(8 + 1, 0);

    std::set<long double, comparedouble> fs;
    for (nombre i = 1; i < 180; i++) {
        rad[i] = static_cast<long double>(i) * M_PIl / 180.0L;
        sinus[i] = std::sin(rad[i]);
        cosinus[i] = std::cos(rad[i]);
        auto j = static_cast<nombre>(sinus[i] * 10000);
        prevalue[j] = true;
        fs.insert(sinus[i]);
    }
    prevalue[10000] = true; // sin90
    prevalue[10000 - 1] = true; // sin90
    prevalue[5000] = true; // sin30
    prevalue[5000 - 1] = true; //sin30

    for (nombre a = 1; a <= 45; a++)
        for (nombre b = a; a + b < 180 - 1; b++)
            for (nombre c = 1; a + b + c < 180; c++) {
                nombre d = 180 - a - b - c;
                for (nombre e = 1; e < b + d; e++) {
                    nombre f = b + d - e;
                    long double m = (sinus[b] * sinus[c] * sinus[f]) / (sinus[a] * sinus[e] * sinus[d]);
                    long double n = cosinus[a + c];
                    long double siny = std::sqrt((1 - n * n) / (m * m + 2 * m * n + 1));
                    auto j = static_cast<nombre>(siny * 10000);
                    if (!prevalue[j]) continue;
                    if (fs.find(siny) != fs.end()) {
                        if (siny > 1) siny = 1;

                        auto y = static_cast<nombre>(std::asin(siny) * 180 / M_PIl + 0.01L);
                        long double sinx = m * siny;
                        long double xangle = (std::abs(sinx - 1) < epsilon) ? 90 : std::asin(sinx) * 180 / M_PIl;
                        auto x = static_cast<nombre>(xangle + 0.01L);
                        if (xangle < x + epsilon && xangle > x - epsilon) {
                            x = (180 - x + y == a + c) ? 180 - x : x;
                            y = (180 + x - y == a + c) ? 180 - y : y;

                            std::vector<vecteur> quadrilateres
                                    {
                                            {a, b, d, y, x, f, e, c},
                                            {d, y, x, f, e, c, a, b},
                                            {x, f, e, c, a, b, d, y},
                                            {e, c, a, b, d, y, x, f},
                                            {b, a, c, e, f, x, y, d},
                                            {c, e, f, x, y, d, b, a},
                                            {f, x, y, d, b, a, c, e},
                                            {y, d, b, a, c, e, f, x}
                                    };

                            std::set<vecteur> s;
                            for (auto quadrilatere: quadrilateres) {
                                if (quadrilatere.front() <= quadrilatere.at(1) && quadrilatere.front() <= 45)
                                    s.insert(quadrilatere);
                            }

                            solution[s.size()]++;
                        }
                    }
                }
            }

    nombre resultat = 0;
    for (nombre i = 1; i <= 8; i++)
        resultat += solution[i] / i;

    return std::to_string(resultat);
}


//---------------------------------------------------------------------------

#pragma hdrstop

#include <System.SysUtils.hpp>

#include <random>
#include <future>
#include <vector>
#include <numeric>
#include <iterator>
#include <utility>

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/geometries.hpp>

#include "IModel.h"

#include "StochasticMTAreaCalc.h"

using std::mt19937;
using std::uniform_real_distribution;
using std::accumulate;
using std::begin;
using std::end;

using boost::geometry::envelope;

using boost::geometry::model::box;
using boost::geometry::area;

//---------------------------------------------------------------------------

#pragma package(smart_init)

namespace AreaPrj {

String StochasticMTAreaCalc::DoGetDescription() const
{
    return Format(
        _D( "\'%s\' con %.0n di punti casuali suddiviso in %u task" ),
        ARRAYOFCONST((
            GetName(),
            static_cast<long double>( pointCount_ ),
            taskCount_
        ))
    );
}

double StochasticMTAreaCalc::DoCompute( IModel const & Model ) const
{
    box<IModel::PointType> BoundingBox;
    auto const & Polygons = Model.GetPolygons();
    envelope( Polygons, BoundingBox );
    std::vector<std::future<size_t>> Tasks;
    for ( size_t n = 0 ; n < taskCount_ ; ++n ) {
        Tasks.push_back(
            std::async(
                std::launch::async,
                [&]( std::random_device::result_type seed, size_t PtCnt ) -> size_t {
                    auto const & Polygons = Model.GetPolygons();
                    mt19937 Generator( seed );
                    uniform_real_distribution<> DisX(
                        BoundingBox.min_corner().x(),
                        BoundingBox.max_corner().x()
                    );

                    uniform_real_distribution<> DisY(
                        BoundingBox.min_corner().y(),
                        BoundingBox.max_corner().y()
                    );

                    size_t HitCnt {};

                    for ( size_t n = 0 ; n < PtCnt ; ++n ) {
                        if ( Model.HitTest( DisX( Generator ), DisY( Generator ) ) ) {
                            ++HitCnt;
                        }
                    }
                    return HitCnt;
                },
                rd_(),
                pointCount_ / taskCount_
            )
        );
    }
    return
        static_cast<double>(
            accumulate<decltype( begin( Tasks ) ), size_t>(
                begin( Tasks ), end( Tasks ), {},
                []( size_t Val, auto& fut ) {
                    return Val + fut.get();
                }
            )
        ) /
        pointCount_ * area( BoundingBox );
}

} // End of namespace AreaPrj


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

#include "dotproduct.h"
#include "common/pixel_benchmark.h"
#include "common/pixel_fast_rng.h"

#include <opencv2/opencv.hpp>
#include <opencv2/core/simd_intrinsics.hpp>

#include <Eigen/Dense>

#include <iostream>
using namespace std;

static void dotproduct_f32_test();
static float dotproduct_f32_opencv1(float* a, float* b, uint32_t len);
static float dotproduct_f32_opencv2(float* a, float* b, uint32_t len);
static float dotproduct_f32_eigen(float* a, float* b, uint32_t len);

static void dotproduct_u8_test();
static uint64_t dotproduct_u8_opencv1(uint8_t* a, uint8_t* b, uint32_t len);
static uint64_t dotproduct_u8_opencv2(uint8_t* a, uint8_t* b, uint32_t len);
static uint64_t dotproduct_u8_eigen(uint8_t* a, uint8_t* b, uint32_t len);
static uint64_t dotproduct_u8_eigen2(uint8_t* a, uint8_t* b, uint32_t len);

static void arm_marco_determine();

//----------------------------------------------------------------------

float dotproduct_f32_opencv1(float* a, float* b, uint32_t len)
{
    cv::Mat matA(1, len, CV_32FC1, a);
    cv::Mat matB(1, len, CV_32FC1, b);
    float res = matA.dot(matB);
    return res;
}

float dotproduct_f32_opencv2(float* a, float* b, uint32_t len)
{
    size_t step = sizeof(cv::v_float32)/sizeof(float);
    cv::v_float32 v_sum = cv::vx_setzero_f32();
    size_t vec_size = len - len % step;
    for (size_t i=0; i< vec_size; i+=step)
    {
        cv::v_float32 v1 = cv::vx_load(a+i);
        cv::v_float32 v2 = cv::vx_load(b+i);
        v_sum += v1 * v2;
    }

    float sum = cv::v_reduce_sum(v_sum);

    for (size_t i= vec_size; i<len; i++) {
        sum += a[i] * b[i];
    }

    return sum;
}

float dotproduct_f32_eigen(float* a, float* b, uint32_t len) {
    Eigen::Map<Eigen::Matrix<float, 1, Eigen::Dynamic, Eigen::RowMajor>> va(a, len);
    Eigen::Map<Eigen::Matrix<float, 1, Eigen::Dynamic, Eigen::RowMajor>> vb(b, len);
    float res = va.dot(vb);
    return res;
}

uint64_t dotproduct_u8_opencv1(uint8_t* a, uint8_t* b, uint32_t len)
{
    cv::Mat matA(1, len, CV_8UC1, a);
    cv::Mat matB(1, len, CV_8UC1, b);
    uint64_t res = matA.dot(matB);
    return res;
}

uint64_t dotproduct_u8_opencv2(uint8_t* a, uint8_t* b, uint32_t len)
{
    // 实现起来可能略繁琐，并且和neon实现类似，暂时不写
    return 0;
}

uint64_t dotproduct_u8_eigen(uint8_t* a, uint8_t* b, uint32_t len)
{
    // 这里的实现有问题：如果是u8的Eigen::Matrix做dotproduct，结果不对
    // 如果转为u64或float的Eigen::Matrix，结果对，但是比naive实现慢好多倍
    Eigen::Map<Eigen::Matrix<uint8_t, 1, Eigen::Dynamic, Eigen::RowMajor>> va(a, len);
    Eigen::Map<Eigen::Matrix<uint8_t, 1, Eigen::Dynamic, Eigen::RowMajor>> vb(b, len);

    Eigen::Matrix<uint64_t, 1, Eigen::Dynamic, Eigen::RowMajor> fa = va.cast<uint64_t>();
    Eigen::Matrix<uint64_t, 1, Eigen::Dynamic, Eigen::RowMajor> fb = vb.cast<uint64_t>();

    return fa.dot(fb);
}

uint64_t dotproduct_u8_eigen2(uint8_t* a, uint8_t* b, uint32_t len)
{
    // 这里的实现有问题：如果是u8的Eigen::Matrix做dotproduct，结果不对
    // 如果转为u64或float的Eigen::Matrix，结果对，但是比naive实现慢好多倍
    Eigen::Map<Eigen::Matrix<uint8_t, 1, Eigen::Dynamic, Eigen::RowMajor>> va(a, len);
    Eigen::Map<Eigen::Matrix<uint8_t, 1, Eigen::Dynamic, Eigen::RowMajor>> vb(b, len);
    uint64_t res = va.cast<uint64_t>().dot(vb.cast<uint64_t>());
    return res;
}

void dotproduct_f32_test()
{
    uint32_t len = 200000000; //200M
    float* a = (float*)malloc(sizeof(float)*len);
    float* b = (float*)malloc(sizeof(float)*len);

    float res_naive;
    float res_opencv1;
    float res_opencv2;
    float res_eigen;
    float res_asimd;
    float res_asimd2;
    float res_asimd3;

    double t_start, t_cost;

    g_state.a = 7767517;
    t_start = pixel_get_current_time();
    for(uint32_t i=0; i<len; i++) {
        a[i] = pixel_fast_random_float(-1.2, 1.2);
        b[i] = pixel_fast_random_float(-1.2, 1.2);
    }
    t_cost = pixel_get_current_time() - t_start;
    printf("generate random numbers, time cost %.4lf ms\n", t_cost);

    // naive
    t_start = pixel_get_current_time();
    res_naive = dotproduct_f32_naive(a, b, len);
    t_cost = pixel_get_current_time() - t_start;
    printf("dotproduct_f32, naive, time cost %.4lf ms\n", t_cost);

    // opencv, method1
    t_start = pixel_get_current_time();
    res_opencv1 = dotproduct_f32_opencv1(a, b, len);
    t_cost = pixel_get_current_time() - t_start;
    printf("dotproduct_f32, opencv method1, time cost %.4lf ms\n", t_cost);

    // opencv, method2
    t_start = pixel_get_current_time();
    res_opencv2 = dotproduct_f32_opencv2(a, b, len);
    t_cost = pixel_get_current_time() - t_start;
    printf("dotproduct_f32, opencv method2, time cost %.4lf ms\n", t_cost);

    // eigen
    t_start = pixel_get_current_time();
    res_eigen = dotproduct_f32_eigen(a, b, len);
    t_cost = pixel_get_current_time() - t_start;
    printf("dotproduct_f32, eigen, time cost %.4lf ms\n", t_cost);

    // asimd
    t_start = pixel_get_current_time();
    res_asimd = dotproduct_f32_asimd(a, b, len);
    t_cost = pixel_get_current_time() - t_start;
    printf("dotproduct_f32, asimd, time cost %.4lf ms\n", t_cost);

    // asimd2
    t_start = pixel_get_current_time();
    res_asimd2 = dotproduct_f32_asimd2(a, b, len);
    t_cost = pixel_get_current_time() - t_start;
    printf("dotproduct_f32, asimd2, time cost %.4lf ms\n", t_cost);

    // asimd3
    t_start = pixel_get_current_time();
    res_asimd3 = dotproduct_f32_asimd3(a, b, len);
    t_cost = pixel_get_current_time() - t_start;
    printf("dotproduct_f32, asimd3, time cost %.4lf ms\n", t_cost);

    // validate
    int mis_opencv1=0;
    int mis_opencv2=0;
    int mis_eigen=0;
    int mis_asimd=0;
    int mis_asimd2=0;
    if (res_naive!=res_opencv1) {
        mis_opencv1++;
    }
    if (res_naive!=res_opencv2) {
        mis_opencv2++;
    }
    if (res_naive!=res_eigen) {
        mis_eigen++;
    }
    if (res_naive!=res_asimd) {
        mis_asimd++;
    }
    if (res_naive!=res_asimd2) {
        mis_asimd2++;
    }
    printf("mis_opencv1=%d, mis_opencv2=%d, mis_eigen=%d, mis_asimd=%d, mis_asimd2=%d\n",
        mis_opencv1, mis_opencv2, mis_eigen, mis_asimd, mis_asimd2);
    printf("res_naive=%f, res_opencv1=%f, res_opencv2=%f, res_eigen=%f, res_asimd=%f, res_asimd2=%f, res_asimd3=%f\n",
        res_naive, res_opencv1, res_opencv2, res_eigen, res_asimd, res_asimd2, res_asimd3
    );
}

void dotproduct_u8_test()
{
    uint32_t len = 200000000; //200M
    uint8_t* a = (uint8_t*)malloc(sizeof(uint8_t)*len);
    uint8_t* b = (uint8_t*)malloc(sizeof(uint8_t)*len);

    uint64_t res_naive;
    uint64_t res_opencv1;
    uint64_t res_opencv2;
    uint64_t res_eigen;
    uint64_t res_asimd;
    uint64_t res_asimd2;
    uint64_t res_asimd3;
    uint64_t res_asimd4;

    double t_start, t_cost;

    g_state.a = 7767517;
    t_start = pixel_get_current_time();
    for(uint32_t i=0; i<len; i++) {
        a[i] = (uint8_t)(pixel_fast_random_float(0, 255));
        b[i] = (uint8_t)(pixel_fast_random_float(0, 255));
    }
    t_cost = pixel_get_current_time() - t_start;
    printf("generate random numbers, time cost %.4lf ms\n", t_cost);

    // naive
    t_start = pixel_get_current_time();
    res_naive = dotproduct_u8_naive(a, b, len);
    t_cost = pixel_get_current_time() - t_start;
    printf("dotproduct_u8, naive, time cost %.4lf ms\n", t_cost);

    // opencv, method1
    t_start = pixel_get_current_time();
    res_opencv1 = dotproduct_u8_opencv1(a, b, len);
    t_cost = pixel_get_current_time() - t_start;
    printf("dotproduct_u8, opencv method1, time cost %.4lf ms\n", t_cost);

    // opencv, method2
    t_start = pixel_get_current_time();
    res_opencv2 = dotproduct_u8_opencv2(a, b, len);
    t_cost = pixel_get_current_time() - t_start;
    printf("dotproduct_u8, opencv method2, time cost %.4lf ms\n", t_cost);

    // eigen
    t_start = pixel_get_current_time();
    res_eigen = dotproduct_u8_eigen(a, b, len);
    t_cost = pixel_get_current_time() - t_start;
    printf("dotproduct_u8, eigen, time cost %.4lf ms\n", t_cost);

    // eigen2
    t_start = pixel_get_current_time();
    res_eigen = dotproduct_u8_eigen2(a, b, len);
    t_cost = pixel_get_current_time() - t_start;
    printf("dotproduct_u8, eigen2, time cost %.4lf ms\n", t_cost);

    // asimd
    t_start = pixel_get_current_time();
    res_asimd = dotproduct_u8_asimd(a, b, len);
    t_cost = pixel_get_current_time() - t_start;
    printf("dotproduct_u8, asimd, time cost %.4lf ms\n", t_cost);

    // asimd2
    t_start = pixel_get_current_time();
    res_asimd2 = dotproduct_u8_asimd2(a, b, len);
    t_cost = pixel_get_current_time() - t_start;
    printf("dotproduct_u8, asimd2, time cost %.4lf ms\n", t_cost);

    // asimd3
    t_start = pixel_get_current_time();
    res_asimd3 = dotproduct_u8_asimd3(a, b, len);
    t_cost = pixel_get_current_time() - t_start;
    printf("dotproduct_u8, asimd3, time cost %.4lf ms\n", t_cost);

    // asimd4
    t_start = pixel_get_current_time();
    res_asimd4 = dotproduct_u8_asimd4(a, b, len);
    t_cost = pixel_get_current_time() - t_start;
    printf("dotproduct_u8, asimd4, time cost %.4lf ms\n", t_cost);

    // validate
    int mis_opencv1=0;
    int mis_opencv2=0;
    int mis_eigen=0;
    int mis_asimd=0;
    int mis_asimd2=0;
    int mis_asimd3=0;
    int mis_asimd4=0;
    if (res_naive!=res_opencv1) {
        mis_opencv1++;
    }
    if (res_naive!=res_opencv2) {
        mis_opencv2++;
    }
    if (res_naive!=res_eigen) {
        mis_eigen++;
    }
    if (res_naive!=res_asimd) {
        mis_asimd++;
    }
    if (res_naive!=res_asimd2) {
        mis_asimd2++;
    }
    if (res_naive!=res_asimd3) {
        mis_asimd3++;
    }
    if (res_naive!=res_asimd4) {
        mis_asimd4++;
    }
    printf("mis_opencv1=%d, mis_opencv2=%d, mis_eigen=%d, mis_asimd=%d, mis_asimd2=%d, mis_asimd3=%d, mis_asimd4=%d\n",
        mis_opencv1, mis_opencv2, mis_eigen, mis_asimd, mis_asimd2, mis_asimd3, mis_asimd4);
    // printf("res_naive=%ul, res_opencv1=%ul, res_opencv2=%ul, res_eigen=%ul, res_asimd=%ul, res_asimd2=%ul, res_asimd3=%ul\n",
    //     res_naive, res_opencv1, res_opencv2, res_eigen, res_asimd, res_asimd2, res_asimd3
    // );
    cout << "res_naive=" << res_naive << endl;
    cout << "res_opencv1=" << res_opencv1 << endl;
    cout << "res_eigen=" << res_eigen << endl;
    cout << "res_asimd=" << res_asimd << endl;
    cout << "res_asimd2=" << res_asimd2 << endl;
    cout << "res_asimd3=" << res_asimd3 << endl;
    cout << "res_asimd4=" << res_asimd4 << endl;
}

void arm_marco_determine()
{
#if __ARM_NEON
    printf("__ARM_NEON\n");
    #if __aarch64__
        printf("__aarch64__\n");
        #if __ARM_FEATURE_FP16_VECTOR_ARITHMETIC
            printf("arm8.2 fp16\n");
        #else
            printf("not arm8.2 fp16\n");
        #endif

        #if __ARM_FEATURE_DOTPROD
            printf("arm8.2 dotproduct\n");
        #else
            printf("not arm8.2 dotproduct\n");
        #endif
    #else
        printf("not __aarch64__\n");
    #endif // __aarch64__
#else
    printf("not __ARM_NEON\n");
#endif // __ARM_NEON
}

static void sdot_example() {
#if __ARM_FEATURE_DOTPROD
    //uint32x2_t vdot_u32 (uint32x2_t r, uint8x8_t a, uint8x8_t b)
    uint8_t a[8] = {0, 1, 2, 3, 4, 5, 6, 7};
    uint8_t b[8] = {0, 1, 2, 3, 4, 5, 6, 7};
    uint8x8_t va = vld1_u8(a);
    uint8x8_t vb = vld1_u8(b);
    uint32x2_t vr = vdup_n_u32(0);
    vr = vdot_u32(vr, va, vb);

    uint32_t res[2];
    vst1_u32(res, vr);
    printf("res[0]=%d, res[1]=%d\n", res[0], res[1]);
#endif
}

int main() {
    arm_marco_determine();
    //dotproduct_f32_test();
    dotproduct_u8_test();

    return 0;
}


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

///
/// \file self_adjoint_coneigensolver.hpp
///
#ifndef MXPFIT_SELF_ADJOINT_CONEIGENSOLVER_HPP
#define MXPFIT_SELF_ADJOINT_CONEIGENSOLVER_HPP

#include <cassert>
#include <type_traits>

#include <Eigen/Core>
#include <Eigen/QR>

#include <mxpfit/jacobi_svd.hpp>

namespace mxpfit
{
///
/// ### SelfAdjointConeigenSolver
///
/// \brief Compute con-eigenvalue decomposition of self-adjoint matrix having a
///        rank-revealing decomposition
///
/// \tparam T  Scalar type of matrix to be decomposed.
///
/// Let \f$A\f$ be an \f$n \times n\f$ self-adjoint matrix having a rank
/// revealing decomposition of the form
///
/// \f[ A = X D^2 X^{\ast},\f]
///
/// where \f$X\f$ is a \f$n \times k \, (n \geq k)\f$ matrix and \f$D\f$ is a
/// \f$k \times k\f$ diagonal matrix with non-negative entries. This class
/// computes a con-eigenvalue decomposition of matrix \f$A\f$ defined as
///
/// \f[
///   A = \overline{U} \Sigma U^{\ast}
/// \f]
///
/// where \f$U\f$ is a \f$n \times k\f$ matrix satisfying \f$U^{-1}=U^{T},\f$
/// and overline denotes the element-wise complex conjugate of a matrix.
/// \f$\Sigma=\mathrm{diag}(\sigma_{1},\dots,\sigma_{k})\f$ is a \f$k \times
/// k\f$ diagonal matrix with \f$\sigma_{1}\geq\sigma_{2}\geq \cdots \geq
/// \sigma_{k} > 0.\f$ The con-eigenvalue decomposition defined above is a
/// special case of singular value decomposition (SVD) \f$A=W\Sigma V^{\ast}\f$,
/// where \f$ W=\overline{U} \f$ and \f$V=U.\f$
///
/// The con-eigenvalue decomposition is computed in high-relative accuracy using
/// the algorithm developed by Haut and Beylkin. The algorithm can also be
/// regarded as modification of Demmel's algorithm for high accuracy SVD of a
/// matrix with rank-revealing decomposition. See the references listed below
/// for the details.
///
///
/// #### References
///
/// 1. T. S. Haut and G. Beylkin, "FAST AND ACCURATE CON-EIGENVALUE ALGORITHM
///    FOR OPTIMAL RATIONAL APPROXIMATIONS", SIAM J. Matrix Anal. Appl. **33**
///    (2012) 1101-1125. [DOI: https://doi.org/10.1137/110821901]
/// 2. J. Demmel, "ACCURATE SINGULAR VALUE DECOMPOSITIONS OF STRUCTURED
///    MATRICES", SIAM J. Matrix Anal. Appl. **21** (1999) 562-580.
///    [DOI: https://doi.org/10.1137/S0895479897328716]
/// 3. J. Demmel, M. Gu, S. Eisenstat, I. Slapnicar, K. Veselic, and Z. Drmac,
///    "ACCURATE SINGULAR VALUE DECOMPOSITIONS OF STRUCTURED MATRICES", SIAM J.
///    Linear Algebra Appl. **299** (1999) 21-80. [DOI:
///    https://doi.org/10.1016/S0024-3795(99)00134-2]
///

enum DecompositionOption
{
    ConeigenvaluesOnly,
    ComputeConeigenvectors
};

template <typename T>
class SelfAdjointConeigenSolver
{
public:
    using Index         = Eigen::Index;
    using Scalar        = T;
    using RealScalar    = typename Eigen::NumTraits<Scalar>::Real;
    using ComplexScalar = std::complex<RealScalar>;

    using MatrixType = Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>;
    using VectorType = Eigen::Matrix<Scalar, Eigen::Dynamic, 1>;
    using RealVectorType = Eigen::Matrix<RealScalar, Eigen::Dynamic, 1>;

    using ConeigenvalueType = Eigen::Matrix<RealScalar, Eigen::Dynamic, 1>;

protected:
    enum
    {
        IsComplex  = Eigen::NumTraits<Scalar>::IsComplex,
        PacketSize = Eigen::internal::packet_traits<Scalar>::size,
        Alignment  = Eigen::internal::traits<MatrixType>::Alignment,
        // Alignment  = Eigen::internal::unpacket_traits<PacketType>::alignment
    };

    using MappedMatrix = Eigen::Map<MatrixType, Alignment>;
    using PacketType   = typename Eigen::internal::packet_traits<Scalar>::type;

    MatrixType m_ceigvecs;     // m x n (m >= n)
    RealVectorType m_ceigvals; // n
    MatrixType m_mat_work1;    // n x n
    MatrixType m_mat_work2;    // n x n

public:
    SelfAdjointConeigenSolver()                                 = default;
    SelfAdjointConeigenSolver(const SelfAdjointConeigenSolver&) = default;

    explicit SelfAdjointConeigenSolver(Index size, Index rank)
        : m_ceigvecs(size, rank),
          m_ceigvals(rank),
          m_mat_work1(rank, rank),
          m_mat_work2(rank, rank)
    {
        assert(size >= rank);
    }

    ~SelfAdjointConeigenSolver() = default;

    template <typename InputMatrix, typename InputVector>
    void compute(const Eigen::MatrixBase<InputMatrix>& matX,
                 const Eigen::MatrixBase<InputVector>& vecD,
                 DecompositionOption option = ComputeConeigenvectors);

    const MatrixType& coneigenvectors() const
    {
        return m_ceigvecs;
    }

    const RealVectorType& coneigenvalues() const
    {
        return m_ceigvals;
    }

protected:
    void resize(Index m, Index n)
    {
        m_ceigvecs.resize(m, n);
        m_ceigvals.resize(n);
        m_mat_work1.resize(n, n);
        m_mat_work2.resize(n, n);
    }
};

template <typename T>
template <typename InputMatrix, typename InputVector>
void SelfAdjointConeigenSolver<T>::compute(
    const Eigen::MatrixBase<InputMatrix>& matX,
    const Eigen::MatrixBase<InputVector>& vecD, DecompositionOption option)
{
    EIGEN_STATIC_ASSERT_VECTOR_ONLY(InputVector);

    assert(matX.rows() >= matX.cols());
    assert(vecD.size() == matX.cols());

    const Index m = matX.rows();
    const Index n = matX.cols();
    resize(m, n);

    MappedMatrix matG(m_ceigvecs.data(), n, n);

    //
    // Form G = D * (X.st() * X) * D
    //
    matG.noalias() = matX.transpose() * matX;
    for (Index j = 0; j < n; ++j)
    {
        for (Index i = 0; i < n; ++i)
        {
            matG(i, j) *= vecD(i) * vecD(j);
        }
    }
    //
    // Compute G = Q * R by Householder QR factorization. G is overwritten by QR
    // factors.
    //
    Eigen::HouseholderQR<Eigen::Ref<MatrixType>> qr(matG);

    //
    // Compute SVD of `R = U S V^H` with high relative accuracy using the
    // one-sided Jacobi SVD algorithm. Only the singular values and left
    // singular vectors U are computed. The obtained singular values coincide
    // with coneigenvalues of input matrix
    //
    // Applying the one-sided Jacobi SVD to the matrix X = R^H is much faster
    // than applying the algorithm to R directly. This is because `R R^H` is
    // more diagonal than `R^H R`. Thus, `U` is computed as right singular
    // vectors of R^H.
    //
    MatrixType& matRt = m_mat_work1;
    matRt.setZero();
    matRt            = matG.template triangularView<Eigen::Upper>().adjoint();
    MatrixType& matU = m_mat_work2;
    const RealScalar tol_svd = Eigen::NumTraits<RealScalar>::epsilon() *
                               Eigen::numext::sqrt(RealScalar(n));
    one_sided_jacobi_svd(matRt, m_ceigvals, matU, tol_svd);

    //
    // Quick return if no con-eigenvector is required.
    //
    if (option == ConeigenvaluesOnly)
    {
        return;
    }

    //-------------------------------------------------------------------------
    //
    // The eigenvectors of A * conj(A) are given as
    //
    //   conj(X') = X * D * V * S^{-1/2}.
    //
    // However, direct evaluation of eigenvectors with this formula might be
    // inaccurate since D is ill-conditioned. The following formula are used
    // instead.
    //
    //   conj(X') = X * D * R.inv() * U * S^{1/2}
    //            = X * (D.inv() * R * D.inv()).inv() * (D.inv() * U *
    //            S^{1/2})
    //            = X * R1.inv() * X1
    //
    //-------------------------------------------------------------------------
    // Matrix R is stored on upper triangular part of G
    auto matR1 = matG.template triangularView<Eigen::Upper>();
    //
    // Compute R1 = D^(-1) * R * D^(-1). The upper triangular part of `matG` is
    // overwritten by R1.
    //
    for (Index j = 0; j < n; ++j)
    {
        const auto dj = vecD(j);
        for (Index i = 0; i <= j; ++i)
        {
            const auto di = vecD(i);
            matR1(i, j)   = matR1(i, j) / (di * dj);
        }
    }
    //
    // Compute X1 = D^{-1} * U * S^{1/2}, and then solve R1 * Y1 = X1 in-place.
    // Matrix `matU` is overwritten by X1 and then overwritten by Y1.
    //
    MatrixType& matY1 = matU;
    for (Index j = 0; j < n; ++j)
    {
        const auto sj = Eigen::numext::sqrt(m_ceigvals(j));
        for (Index i = 0; i < n; ++i)
        {
            matY1(i, j) *= sj / vecD(i);
        }
    }

    matR1.solveInPlace(matY1);
    //
    // Compute con-eigenvectors U = conj(X) * conj(Y).
    //
    m_ceigvecs.noalias() = matX.conjugate() * matY1.conjugate();

    if (IsComplex)
    {
        //
        // Adjust phase factor of each con-eigenvectors, so that U^{T} * U = I
        //
        for (Index j = 0; j < n; ++j)
        {
            auto xj          = m_ceigvecs.col(j);
            const auto t     = (xj.transpose() * xj).value();
            const auto phase = t / std::abs(t);
            const auto scale = std::sqrt(Eigen::numext::conj(phase));
            xj *= scale;
        }
    }
}

} // namespace: mxpfit

#endif /* MXPFIT_SELF_ADJOINT_CONEIGENSOLVER_HPP */


// Equality comparision of floating point values.
//
// Copyright (c) Eric Nodwell
// See LICENSE for details.
//
// Since floating point values are approximate, equality comparison should be
// done to within some limited precision.  In some cases, the best measure of
// precision is a certain number of "ulps" ("Units in Last Place"), where ulps
// essentially is a count of how many representable floating point numbers
// separate two given values.  This is what these functions do.
//
// It is worth emphasizing that a maximum difference of ulps is often not
// the best equality comparison.  However you typically require specific
// domain knowledge of expected precision or error in order to do better.  In
// cases where you don't know or don't care particularly about the expected
// precision, ULPs is typically the best default fall-back.
//
// Basic usage:
//
//   Step 1. (Optional but highly recommended.)
//   Call bonelabMisc::SanityCheck() somewhere in your code.
//
//   Step 2.
//   Compare floating point values (floats and/or doubles), like this:
//
//     if (bonelabMisc::EssentiallyEqual(x,y))
//
//   or
//
//     if (bonelabMisc::ApproximatelyEqual(x,y))
//
//  The former checks that two floating point numbers are within 1 ULP
//  of each other, while the latter has a looser tolerance equal to
//  APPROXIMATELY_EQUAL_ULPS .  I have set APPROXIMATELY_EQUAL_ULPS to 16,
//  but you may tweak it if you want something else.
//
//  As an additional note, one should never use the == operator to compare
//  a float with a double.  The comparsion mostly returns false because
//  following the standard rules of C++, the float is promoted to a double
//  before == is applied.  Thus, you are effectively trying to compare more
//  digits than the number actually has.  The templated comparison functions
//  here by contrast, will demote the double to float if passed both a float
//  and a double.  This is what you want: a meaningful comparison can only be
//  made with the precision of the least precise input.
//
//  This algorithm is a modified verion of the one from:
//    http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
//
//  Boost also has a floating-point comparison module.  It is more appropriate
//  in cases where one can make an estimate for the uncertainty epsilon:
//    http://www.boost.org/doc/libs/1_34_0/libs/test/doc/components/test_tools/floating_point_comparison.html
//
//  For even more information on floating point numbers, refer to
//    http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.22.6768

#include <boost/static_assert.hpp>
#include <boost/math/special_functions/fpclassify.hpp>  // for isnan
#include <boost/cstdint.hpp>
#include <limits>
#include <cstdlib>

// Uncomment this line to get very verbose reporting of comparison.
//#define TRACE_ALMOSTEQUAL2SCOMPLEMENT

namespace bonelabMisc
{

using boost::int64_t;

// Change this value to
const int APPROXIMATELY_EQUAL_ULPS = 16;

// Checks we can do at compile time.
// Please ensure that you also call SanityCheck() in your code.
//
// Note: Hypothetically, one could write the functions below as macros
//       which could then be called as static asserts, thus permitting
//       the whole of SanityCheck to be implemented as static asserts.
//       Then, since inline functions are much preferable to macros
//       for the user, the functions would all wrap the corresponding macro.
//       For now, this is too much work.
BOOST_STATIC_ASSERT(sizeof(int) == sizeof(float));
BOOST_STATIC_ASSERT(sizeof(int64_t) == sizeof(double));

// Description:
// Explicit reinterpretation of float as a 32 bit integer.
// Key point: The value of the int is meaningless, but order is preserved.
inline int FloatTwosComplementUlps (float x)
{
  int xInt = *(int*)&x;
  // Make aInt lexicographically ordered as a twos-complement int
  if (xInt < 0)
    xInt = 0x80000000 - xInt;
  return xInt;
}

// Description:
// Explicit reinterpretation of double as a 64 bit integer
// Key point: The value of the int is meaningless, but order is preserved.
inline int64_t DoubleTwosComplementUlps (double x)
{
  int64_t xInt = *(int64_t*)&x;
  // Make aInt lexicographically ordered as a twos-complement int
  if (xInt < 0)
    xInt = 0x8000000000000000 - xInt;
  return xInt;
}

// Description:
// Given a certain specified precision, return the corresponding number of ulps.
// Note that the correspondence only holds approximately, and is wildly
// inaccurate for denormalized numbers.
inline int FloatUlpsFromPrecision (float p)
{
  float x = 1.0f;
  int ulps = FloatTwosComplementUlps(x+p) - FloatTwosComplementUlps(x);
  return ulps;
}

// Description:
// Given a certain specified precision, return the corresponding number of ulps.
// Note that the correspondence only holds approximately, and is wildly
// inaccurate for denormalized numbers.
inline int64_t DoubleUlpsFromPrecision (double p)
{
  double x = 1.0;
  int64_t ulps = DoubleTwosComplementUlps(x+p) - DoubleTwosComplementUlps(x);
  return ulps;
}

// Description:
// Given the specified number of ulps, return the approximate corresponding
// relative precision.
// Note: Not valid for denormalized numbers.
inline float PrecisionOfFloatUlps (int ulps=1)
{
  float x = 1.0f;
  int xInt = FloatTwosComplementUlps(x);
  int yInt = xInt + ulps;
  float y = *(float*)&yInt;
  return y-x;
}

// Description:
// Given the specified number of ulps, return the approximate corresponding
// relative precision.
// Note: Not valid for denormalized numbers.
inline double PrecisionOfDoubleUlps (int ulps=1)
{
  double x = 1.0f;
  int64_t xInt = DoubleTwosComplementUlps(x);
  int64_t yInt = xInt + ulps;
  double y = *(double*)&yInt;
  return y-x;
}

// Description:
// Compare two floats and return true if they are within a certain number of
// ULPS of each other.
inline bool FloatAlmostEqual2sComplement (float a, float b, int maxUlps=1)
{
    int aInt = FloatTwosComplementUlps(a);
    int bInt = FloatTwosComplementUlps(b);
    int intDiff = std::abs(aInt - bInt);
#ifdef TRACE_ALMOSTEQUAL2SCOMPLEMENT
    cout << "\n";
    cout << std::dec << "a = " << a << "\n";
    cout << std::dec << "b = " << b << "\n";
    cout << std::dec << "*(int*)&a = " << *(int*)&a << " " << std::hex << *(int*)&a << "\n";
    cout << std::dec << "*(int*)&b = " << *(int*)&b << " " << std::hex << *(int*)&b << "\n";
    cout << std::dec << "aInt = " << aInt << " " << std::hex << aInt << "\n";
    cout << std::dec << "bInt = " << bInt << " " << std::hex << bInt << "\n";
    cout << std::dec << "abs(aInt - BInt) = " << intDiff << " " << std::hex << intDiff << "\n";
    cout << std::dec << "maxUlps = " << maxUlps << "\n";
#endif
    // Note that the correct answer to NaN == NaN is false; hence the special case.
    if (intDiff <= maxUlps && !(boost::math::isnan)(a))
        return true;
    return false;
}

// Description:
// Compare two doubles and return true if they are within a certain number of
// ULPS of each other.
inline bool DoubleAlmostEqual2sComplement (double a, double b, int64_t maxUlps=1)
{
    int64_t aInt = DoubleTwosComplementUlps(a);
    int64_t bInt = DoubleTwosComplementUlps(b);
#ifdef WIN32
    // 64 bit Windows has no abs function for 64 bit integers.
    int64_t signedIntDiff = aInt - bInt;
    int64_t intDiff = (signedIntDiff >= 0) ? signedIntDiff : -signedIntDiff;
#else
    int64_t intDiff = std::abs(aInt - bInt);
#endif
#ifdef TRACE_ALMOSTEQUAL2SCOMPLEMENT
    cout << "\n";
    cout << std::dec << "a = " << a << "\n";
    cout << std::dec << "b = " << b << "\n";
    cout << std::dec << "*(int64_t*)&a = " << *(int64_t*)&a << " " << std::hex << *(int64_t*)&a << "\n";
    cout << std::dec << "*(int64_t*)&b = " << *(int64_t*)&b << " " << std::hex << *(int64_t*)&b << "\n";
    cout << std::dec << "aInt = " << aInt << " " << std::hex << aInt << "\n";
    cout << std::dec << "bInt = " << bInt << " " << std::hex << bInt << "\n";
    cout << std::dec << "abs(aInt - BInt) = " << intDiff << " " << std::hex << intDiff << "\n";
    cout << std::dec << "maxUlps = " << maxUlps << "\n";
#endif
    // Note that the correct answer to NaN == NaN is false; hence the special case.
    if (intDiff <= maxUlps && !(boost::math::isnan)(a))
        return true;
    return false;
}

// --------------------------------------------------------------------------
// Templated versions of comparison functions
//
// Note: Comparison of mixed float and double will default to the float
//       versions of the comparison functions.  This is generally sensible.

template <typename T1, typename T2> inline bool AlmostEqual2sComplement (T1 a, T2 b, int maxUlps=1);

template <> inline bool AlmostEqual2sComplement<float, float> (float a, float b, int maxUlps)
{return FloatAlmostEqual2sComplement(a,b,maxUlps);}

template <> inline bool AlmostEqual2sComplement<double, double> (double a, double b, int maxUlps)
{return DoubleAlmostEqual2sComplement(a,b,maxUlps);}

template <> inline bool AlmostEqual2sComplement<float, double> (float a, double b, int maxUlps)
{return FloatAlmostEqual2sComplement(a,(float)b,maxUlps);}

template <> inline bool AlmostEqual2sComplement<double, float> (double a, float b, int maxUlps)
{return FloatAlmostEqual2sComplement((float)a,b,maxUlps);}

// Description:
// Returns true if the arguments are within 1 ULP of each other.
template <typename T1, typename T2> inline bool EssentiallyEqual (T1 a, T2 b)
{return AlmostEqual2sComplement<T1,T2>(a,b,1);}

// Description:
// Returns true if the arguments are within APPROXIMATELY_EQUAL_ULPS of each other.
template <typename T1, typename T2> inline bool ApproximatelyEqual (T1 a, T2 b)
{return AlmostEqual2sComplement<T1,T2>(a,b,APPROXIMATELY_EQUAL_ULPS);}

// Description:
// Checks that the comparison functions work as expected.
// I STRONGLY recommend that you call this somewhere in your code before using
// and of the comparison functions.
void SanityCheck ()
{
  // --------------------------------------------------------------------------
  // Floats

  // Trival check
  assert(!FloatAlmostEqual2sComplement(1.0f,-1.0f,256));
  assert(!FloatAlmostEqual2sComplement(-1.0f,1.0f,256));

  // Positive and Negative zero have different representations, but should
  // compare as equal.
  assert(FloatAlmostEqual2sComplement(0.0f,-0.0f));
  assert(FloatAlmostEqual2sComplement(-0.0f,0.0f));

  // Denormalized minimum value of float should equal 1 when reinterpreted
  // as an int.
  assert(FloatAlmostEqual2sComplement(0.0f,std::numeric_limits<float>::denorm_min()));
  assert(FloatAlmostEqual2sComplement(std::numeric_limits<float>::denorm_min(),0.0f));
  assert(FloatAlmostEqual2sComplement(-0.0f,std::numeric_limits<float>::denorm_min()));
  assert(FloatAlmostEqual2sComplement(std::numeric_limits<float>::denorm_min(),-0.0f));
  assert(FloatAlmostEqual2sComplement(0.0f,-std::numeric_limits<float>::denorm_min()));
  assert(FloatAlmostEqual2sComplement(-std::numeric_limits<float>::denorm_min(),0.0f));
  assert(FloatAlmostEqual2sComplement(-0.0f,-std::numeric_limits<float>::denorm_min()));
  assert(FloatAlmostEqual2sComplement(-std::numeric_limits<float>::denorm_min(),-0.0f));

  // Normalized minimum value of float should be a rather large number when
  // reinterpreted as an int.
  assert(!FloatAlmostEqual2sComplement(0.0f,std::numeric_limits<float>::min()));
  assert(!FloatAlmostEqual2sComplement(std::numeric_limits<float>::min(),0.0f));
  assert(!FloatAlmostEqual2sComplement(-0.0f,std::numeric_limits<float>::min()));
  assert(!FloatAlmostEqual2sComplement(std::numeric_limits<float>::min(),-0.0f));
  assert(!FloatAlmostEqual2sComplement(0.0f,-std::numeric_limits<float>::min()));
  assert(!FloatAlmostEqual2sComplement(-std::numeric_limits<float>::min(),0.0f));
  assert(!FloatAlmostEqual2sComplement(-0.0f,-std::numeric_limits<float>::min()));
  assert(!FloatAlmostEqual2sComplement(-std::numeric_limits<float>::min(),-0.0f));

  // Expected accuracies.
  float lessThanFloatPrecision = PrecisionOfFloatUlps() / 3;
  float moreThanFloatPrecision = PrecisionOfFloatUlps() * 2;
  assert(FloatAlmostEqual2sComplement(1.0f,1.0f+lessThanFloatPrecision));
  assert(FloatAlmostEqual2sComplement(1.0f+lessThanFloatPrecision,1.0f));
  assert(!FloatAlmostEqual2sComplement(1.0f,1.0f+moreThanFloatPrecision));
  assert(!FloatAlmostEqual2sComplement(1.0f+moreThanFloatPrecision,1.0f));
  assert(FloatAlmostEqual2sComplement(-1.0f,-1.0f+lessThanFloatPrecision));
  assert(FloatAlmostEqual2sComplement(-1.0f+lessThanFloatPrecision,-1.0f));
  assert(!FloatAlmostEqual2sComplement(-1.0f,-1.0f+moreThanFloatPrecision));
  assert(!FloatAlmostEqual2sComplement(-1.0f+moreThanFloatPrecision,-1.0f));

  // --------------------------------------------------------------------------
  // Doubles

  // Trival check
  assert(!DoubleAlmostEqual2sComplement(1.0,-1.0,256));
  assert(!DoubleAlmostEqual2sComplement(-1.0,1.0,256));

  // Positive and Negative zero have different representations, but should
  // compare as equal.
  assert(DoubleAlmostEqual2sComplement(0.0,-0.0));
  assert(DoubleAlmostEqual2sComplement(-0.0,0.0));

  // Denormalized minimum value of double should equal 1 when reinterpreted
  // as an int.
  assert(DoubleAlmostEqual2sComplement(0.0,std::numeric_limits<double>::denorm_min()));
  assert(DoubleAlmostEqual2sComplement(std::numeric_limits<double>::denorm_min(),0.0));
  assert(DoubleAlmostEqual2sComplement(-0.0,std::numeric_limits<double>::denorm_min()));
  assert(DoubleAlmostEqual2sComplement(std::numeric_limits<double>::denorm_min(),-0.0));
  assert(DoubleAlmostEqual2sComplement(0.0,-std::numeric_limits<double>::denorm_min()));
  assert(DoubleAlmostEqual2sComplement(-std::numeric_limits<double>::denorm_min(),0.0));
  assert(DoubleAlmostEqual2sComplement(-0.0,-std::numeric_limits<double>::denorm_min()));
  assert(DoubleAlmostEqual2sComplement(-std::numeric_limits<double>::denorm_min(),-0.0));

  // Normalized minimum value of double should be a rather large number when
  // reinterpreted as an int.
  assert(!DoubleAlmostEqual2sComplement(0.0,std::numeric_limits<double>::min()));
  assert(!DoubleAlmostEqual2sComplement(std::numeric_limits<double>::min(),0.0));
  assert(!DoubleAlmostEqual2sComplement(-0.0,std::numeric_limits<double>::min()));
  assert(!DoubleAlmostEqual2sComplement(std::numeric_limits<double>::min(),-0.0));
  assert(!DoubleAlmostEqual2sComplement(0.0,-std::numeric_limits<double>::min()));
  assert(!DoubleAlmostEqual2sComplement(-std::numeric_limits<double>::min(),0.0));
  assert(!DoubleAlmostEqual2sComplement(-0.0,-std::numeric_limits<double>::min()));
  assert(!DoubleAlmostEqual2sComplement(-std::numeric_limits<double>::min(),-0.0));

  // Expected accuracies.
  double lessThanDoublePrecision = PrecisionOfDoubleUlps() / 3;
  double moreThanDoublePrecision = PrecisionOfDoubleUlps() * 2;
  assert(DoubleAlmostEqual2sComplement(1.0,1.0+lessThanDoublePrecision));
  assert(DoubleAlmostEqual2sComplement(1.0+lessThanDoublePrecision,1.0));
  assert(!DoubleAlmostEqual2sComplement(1.0,1.0+moreThanDoublePrecision));
  assert(!DoubleAlmostEqual2sComplement(1.0+moreThanDoublePrecision,1.0));
  assert(DoubleAlmostEqual2sComplement(-1.0,-1.0+lessThanDoublePrecision));
  assert(DoubleAlmostEqual2sComplement(-1.0+lessThanDoublePrecision,-1.0));
  assert(!DoubleAlmostEqual2sComplement(-1.0,-1.0+moreThanDoublePrecision));
  assert(!DoubleAlmostEqual2sComplement(-1.0+moreThanDoublePrecision,-1.0));

  // -------------------------------------------------------------------------
  // EssentiallyEqual and Mixed Floats and Doubles

  // Mixes Zeros (positive and negative)
  assert(EssentiallyEqual(0.0f,0.0));
  assert(EssentiallyEqual(0.0,0.0f));
  assert(EssentiallyEqual(-0.0f,0.0));
  assert(EssentiallyEqual(0.0,-0.0f));
  assert(EssentiallyEqual(0.0f,-0.0));
  assert(EssentiallyEqual(-0.0,0.0f));
  assert(EssentiallyEqual(-0.0f,-0.0));
  assert(EssentiallyEqual(-0.0,-0.0f));

  // Mixed Ones
  assert(EssentiallyEqual(1.0f,1.0));
  assert(EssentiallyEqual(1.0,1.0f));
  assert(EssentiallyEqual(-1.0f,-1.0));
  assert(EssentiallyEqual(-1.0,-1.0f));

  // Expected accuracies should be the same as float, not double
  assert(EssentiallyEqual(1.0f,1.0+lessThanFloatPrecision));
  assert(EssentiallyEqual(1.0+lessThanFloatPrecision,1.0f));
  assert(!EssentiallyEqual(1.0f,1.0+moreThanFloatPrecision));
  assert(!EssentiallyEqual(1.0+moreThanFloatPrecision,1.0f));

  // Finally verify that EssentiallyEqual does give us double accuracies
  // for pure double inputs.
  assert(!EssentiallyEqual(1.0,1.0+moreThanDoublePrecision));
  assert(!EssentiallyEqual(1.0+moreThanDoublePrecision,1.0));

  // -------------------------------------------------------------------------
  // ApproximatelyEqual

  // Float - More tolerant than EssentiallyEqual
  assert(ApproximatelyEqual(1.0f,1.0f+moreThanFloatPrecision));
  assert(ApproximatelyEqual(1.0f+moreThanFloatPrecision,1.0f));

  // Float - Outside specified ULP range for ApproximatelyEqual
  assert(!ApproximatelyEqual(1.0f,1.0f+APPROXIMATELY_EQUAL_ULPS*moreThanFloatPrecision));
  assert(!ApproximatelyEqual(1.0f+APPROXIMATELY_EQUAL_ULPS*moreThanFloatPrecision,1.0f));

  // Double - More tolerant than EssentiallyEqual
  assert(ApproximatelyEqual(1.0,1.0+moreThanDoublePrecision));
  assert(ApproximatelyEqual(1.0+moreThanDoublePrecision,1.0));

  // Double - Outside specified ULP range for ApproximatelyEqual
  assert(!ApproximatelyEqual(1.0,1.0+APPROXIMATELY_EQUAL_ULPS*moreThanDoublePrecision));
  assert(!ApproximatelyEqual(1.0+APPROXIMATELY_EQUAL_ULPS*moreThanDoublePrecision,1.0));

  // -------------------------------------------------------------------------
  // Infinities and NaNs

  // Float infinities
  if (std::numeric_limits<float>::has_infinity)
    {
    assert(!FloatAlmostEqual2sComplement(0.0f,std::numeric_limits<float>::infinity()));
    assert(!FloatAlmostEqual2sComplement(std::numeric_limits<float>::infinity(), 0.0f));
    assert(!FloatAlmostEqual2sComplement(0.0f,-std::numeric_limits<float>::infinity()));
    assert(!FloatAlmostEqual2sComplement(-std::numeric_limits<float>::infinity(), 0.0f));
    assert(!FloatAlmostEqual2sComplement(std::numeric_limits<float>::infinity(),-std::numeric_limits<float>::infinity()));
    assert(!FloatAlmostEqual2sComplement(-std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity()));
    assert(FloatAlmostEqual2sComplement(std::numeric_limits<float>::infinity(),std::numeric_limits<float>::infinity()));
    assert(FloatAlmostEqual2sComplement(-std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity()));
    }

  // Double infinities
  if (std::numeric_limits<double>::has_infinity)
    {
    assert(!DoubleAlmostEqual2sComplement(0.0,std::numeric_limits<double>::infinity()));
    assert(!DoubleAlmostEqual2sComplement(std::numeric_limits<double>::infinity(), 0.0));
    assert(!DoubleAlmostEqual2sComplement(0.0,-std::numeric_limits<double>::infinity()));
    assert(!DoubleAlmostEqual2sComplement(-std::numeric_limits<double>::infinity(), 0.0));
    assert(!DoubleAlmostEqual2sComplement(std::numeric_limits<double>::infinity(),-std::numeric_limits<double>::infinity()));
    assert(!DoubleAlmostEqual2sComplement(-std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity()));
    assert(DoubleAlmostEqual2sComplement(std::numeric_limits<double>::infinity(),std::numeric_limits<double>::infinity()));
    assert(DoubleAlmostEqual2sComplement(-std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity()));
    }

  // float NaNs
  if (std::numeric_limits<float>::has_quiet_NaN)
    {
    assert(!FloatAlmostEqual2sComplement(0.0f,std::numeric_limits<float>::quiet_NaN()));
    assert(!FloatAlmostEqual2sComplement(std::numeric_limits<float>::quiet_NaN(),0.0f));
    assert(!FloatAlmostEqual2sComplement(1.0f,std::numeric_limits<float>::quiet_NaN()));
    assert(!FloatAlmostEqual2sComplement(std::numeric_limits<float>::quiet_NaN(),1.0f));
    assert(!FloatAlmostEqual2sComplement(std::numeric_limits<float>::infinity(),std::numeric_limits<float>::quiet_NaN()));
    assert(!FloatAlmostEqual2sComplement(std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::infinity()));
    assert(!FloatAlmostEqual2sComplement(-std::numeric_limits<float>::infinity(),std::numeric_limits<float>::quiet_NaN()));
    assert(!FloatAlmostEqual2sComplement(std::numeric_limits<float>::quiet_NaN(),-std::numeric_limits<float>::infinity()));
    // Note that according to the standard the correct answer to NaN == NaN is false
    assert(!FloatAlmostEqual2sComplement(std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN()));
    }

  // Double NaNs
  if (std::numeric_limits<double>::has_quiet_NaN)
    {
    assert(!DoubleAlmostEqual2sComplement(0.0,std::numeric_limits<double>::quiet_NaN()));
    assert(!DoubleAlmostEqual2sComplement(std::numeric_limits<double>::quiet_NaN(),0.0));
    assert(!DoubleAlmostEqual2sComplement(1.0,std::numeric_limits<double>::quiet_NaN()));
    assert(!DoubleAlmostEqual2sComplement(std::numeric_limits<double>::quiet_NaN(),1.0));
    assert(!DoubleAlmostEqual2sComplement(std::numeric_limits<double>::infinity(),std::numeric_limits<double>::quiet_NaN()));
    assert(!DoubleAlmostEqual2sComplement(std::numeric_limits<double>::quiet_NaN(),std::numeric_limits<double>::infinity()));
    assert(!DoubleAlmostEqual2sComplement(-std::numeric_limits<double>::infinity(),std::numeric_limits<double>::quiet_NaN()));
    assert(!DoubleAlmostEqual2sComplement(std::numeric_limits<double>::quiet_NaN(),-std::numeric_limits<double>::infinity()));
    // Note that according to the standard the correct answer to NaN == NaN is false
    assert(!DoubleAlmostEqual2sComplement(std::numeric_limits<double>::quiet_NaN(),std::numeric_limits<double>::quiet_NaN()));
    }

}

}  // namespace


// Created by Siddhant Gangapurwala

#ifndef ACTIVATION_HPP
#define ACTIVATION_HPP

#include <Eigen/Dense>

class Activation {
public:
    virtual Eigen::MatrixXd forward(const Eigen::MatrixXd &input);

    virtual Eigen::MatrixXd gradient(const Eigen::MatrixXd &input);
};

class ReLU : public Activation {
public:
    Eigen::MatrixXd forward(const Eigen::MatrixXd &input) override;

    Eigen::MatrixXd gradient(const Eigen::MatrixXd &input) override;
};

class TanH : public Activation {
public:
    Eigen::MatrixXd forward(const Eigen::MatrixXd &input) override;

    Eigen::MatrixXd gradient(const Eigen::MatrixXd &input) override;
};

class SoftSign : public Activation {
public:
    Eigen::MatrixXd forward(const Eigen::MatrixXd &input) override;

    Eigen::MatrixXd gradient(const Eigen::MatrixXd &input) override;
};

class Sigmoid : public Activation {
public:
    Eigen::MatrixXd forward(const Eigen::MatrixXd &input) override;

    Eigen::MatrixXd gradient(const Eigen::MatrixXd &input) override;
};

class LeakyReLU : public Activation {
public:
    Eigen::MatrixXd forward(const Eigen::MatrixXd &input) override;

    Eigen::MatrixXd gradient(const Eigen::MatrixXd &input) override;
};

extern struct ActivationHandler {
    ReLU relu;
    TanH tanh;
    SoftSign softsign;
    Sigmoid sigmoid;
    LeakyReLU leakyReLu;
} activation;

#endif // ACTIVATION_HPP


// dot.cpp: example program contrasting a BLAS L1 dot routine between FLOAT and POSIT
//
// Copyright (C) 2017-2019 Stillwater Supercomputing, Inc.
//
// This file is part of the HPR-BLAS project, which is released under an MIT Open Source license.

#include <ratio>
#include <chrono>
#include <iostream>
#include <ctime>

////////////////////////////////////////////////////////////////////////////////////////
///  BEHAVIORAL COMPILATION SWITCHES for posit library configuration

////////////////////////////////////////////////////////////////////////////////////////
// enable/disable special posit format I/O
// POSIT_ROUNDING_ERROR_FREE_IO_FORMAT
// default is to print (long double) values
// #define POSIT_ROUNDING_ERROR_FREE_IO_FORMAT 0

////////////////////////////////////////////////////////////////////////////////////////
// enable/disable the ability to use literals in binary logic and arithmetic operators
// POSIT_ENABLE_LITERALS)
// default is to enable them
// #define POSIT_ENABLE_LITERALS 1

////////////////////////////////////////////////////////////////////////////////////////
// enable throwing specific exceptions for posit arithmetic errors
// left to application to enable
// POSIT_THROW_ARITHMETIC_EXCEPTION
// default is to use NaR as a signalling error
// #define POSIT_THROW_ARITHMETIC_EXCEPTION 0

////////////////////////////////////////////////////////////////////////////////////////
/// INCLUDE FILES posit library
#include <universal/number/posit/posit.hpp>

///////////////////////////////////////////////////////////////////////////////////////
/// useful mathematical property functions
//#include <universal/functions/functions.hpp>

///////////////////////////////////////////////////////////////////////////////////////
/// the underlying matrix/vector machinery
#ifndef MTL_WITH_INITLIST
#define MTL_WITH_INITLIST
#endif
#include <boost/numeric/mtl/mtl.hpp>

///////////////////////////////////////////////////////////////////////////////////////
/// the High-Performance Reproducible Basic Linear Algebra Subroutines
/// L1, L2, and L3 matrix/vector operations
#include <hprblas.hpp>
/// norms (l1, l2, linf, Frobenius) using HPR methods
//#include <norms.hpp>

#include <utils/matvec.hpp>

using namespace sw::universal;

int main(int argc, char** argv)
try {
	using namespace std;
	using namespace sw::hprblas;
	// configure the posit environment
	const size_t nbits = 32;
	const size_t es = 2;
	const size_t vecSize = 1024;

	int nrOfFailedTestCases = 0;
	// steady_clock example
	using namespace std::chrono;

	cout << "DOT product examples" << endl;
	vector<float> x(vecSize), y(vecSize);
	float fresult;

	randomVectorFillAroundOneEPS(vecSize, x);  //	sampleVector("x", x);
	randomVectorFillAroundOneEPS(vecSize, y);  // 	sampleVector("y", y);
	fresult = sw::hprblas::dot(vecSize, x, 1, y, 1);
	cout << "DOT product is " << setprecision(20) << fresult << endl;
#ifdef LATER
	using Posit = sw::universal::posit<nbits, es>;
	vector<Posit> px(vecSize), py(vecSize);
	Posit presult;
	randomVectorFillAroundOneEPS(vecSize, px);  //	sampleVector("px", px);
	randomVectorFillAroundOneEPS(vecSize, py);  // 	sampleVector("py", py);

	steady_clock::time_point t1 = steady_clock::now();
	presult = sw::hprblas::dot(vecSize, px, 1, py, 1);
	steady_clock::time_point t2 = steady_clock::now();
	double ops = vecSize * 2.0; // dot product is vecSize products and vecSize adds
	cout << "DOT product is " << setprecision(20) << presult << endl;
	//sampleVector("px", px);  // <-- currently shows bad conversions....

	duration<double> time_span = duration_cast<duration<double>>(t2 - t1);
	double elapsed = time_span.count();
	std::cout << "It took " << elapsed << " seconds." << std::endl;
	std::cout << "Performance " << (uint32_t) (ops / (1000*elapsed)) << " KOPS" << std::endl;
	std::cout << std::endl;
#endif
	return (nrOfFailedTestCases > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
catch (char const* msg) {
	std::cerr << msg << std::endl;
	return EXIT_FAILURE;
}
catch (const posit_arithmetic_exception& err) {
	std::cerr << "Uncaught posit arithmetic exception: " << err.what() << std::endl;
	return EXIT_FAILURE;
}
catch (const quire_exception& err) {
	std::cerr << "Uncaught quire exception: " << err.what() << std::endl;
	return EXIT_FAILURE;
}
catch (const posit_internal_exception& err) {
	std::cerr << "Uncaught posit internal exception: " << err.what() << std::endl;
	return EXIT_FAILURE;
}
catch (std::runtime_error& err) {
	std::cerr << err.what() << std::endl;
	return EXIT_FAILURE;
}
catch (...) {
	std::cerr << "Caught unknown exception" << std::endl;
	return EXIT_FAILURE;
}


//
// Created by xiang on 12/21/17.
//

#include <Eigen/Core>
#include <Eigen/Dense>

using namespace Eigen;

#include <vector>
#include <fstream>
#include <iostream>
#include <iomanip>

#include "sophus/se3.h"

using namespace std;

typedef vector<Vector3d, Eigen::aligned_allocator<Vector3d>> VecVector3d;
typedef vector<Vector2d, Eigen::aligned_allocator<Vector3d>> VecVector2d;
typedef Matrix<double, 6, 1> Vector6d;

string p3d_file = "./p3d.txt";
string p2d_file = "./p2d.txt";

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

    VecVector2d p2d;
    VecVector3d p3d;
    Matrix3d K;
    double fx = 520.9, fy = 521.0, cx = 325.1, cy = 249.7;
    K << fx, 0, cx, 0, fy, cy, 0, 0, 1;

    // load points in to p3d and p2d
    // START YOUR CODE HERE

    // END YOUR CODE HERE
    assert(p3d.size() == p2d.size());

    int iterations = 100;
    double cost = 0, lastCost = 0;
    int nPoints = p3d.size();
    cout << "points: " << nPoints << endl;

    Sophus::SE3 T_esti; // estimated pose

    for (int iter = 0; iter < iterations; iter++) {

        Matrix<double, 6, 6> H = Matrix<double, 6, 6>::Zero();
        Vector6d b = Vector6d::Zero();

        cost = 0;
        // compute cost
        for (int i = 0; i < nPoints; i++) {
            // compute cost for p3d[I] and p2d[I]
            // START YOUR CODE HERE

	    // END YOUR CODE HERE

	    // compute jacobian
            Matrix<double, 2, 6> J;
            // START YOUR CODE HERE

	    // END YOUR CODE HERE

            H += J.transpose() * J;
            b += -J.transpose() * e;
        }

	// solve dx
        Vector6d dx;

        // START YOUR CODE HERE

        // END YOUR CODE HERE

        if (isnan(dx[0])) {
            cout << "result is nan!" << endl;
            break;
        }

        if (iter > 0 && cost >= lastCost) {
            // cost increase, update is not good
            cout << "cost: " << cost << ", last cost: " << lastCost << endl;
            break;
        }

        // update your estimation
        // START YOUR CODE HERE

        // END YOUR CODE HERE

        lastCost = cost;

        cout << "iteration " << iter << " cost=" << cout.precision(12) << cost << endl;
    }

    cout << "estimated pose: \n" << T_esti.matrix() << endl;
    return 0;
}


#include <blitz/array.h>
#include <blitz/array/convolve.h>

using namespace blitz;

int main()
{
    Array<float,1> B(Range(-2,+2));
    Array<float,1> C(Range(10,15));

    B = 1, 0, 2, 5, 3;
    C = 10, 2, 4, 1, 7, 2;

    Array<float,1> A = convolve(B,C);

    cout << "A has domain " << A.lbound(0) << "..." << A.ubound(0) << endl
         << A << endl;

    return 0;
}