#include <cstdlib>
#include <sstream>
#include <iostream>
#include <fstream>
#include <vector>

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

#include "filelib.h"
#include "weights.h"
#include "sparse_vector.h"
#include "optimize.h"

using namespace std;
namespace po = boost::program_options;

// since this is a ranking model, there should be equal numbers of
// positive and negative examples, so the bias should be 0
static const double MAX_BIAS = 1e-10;

void InitCommandLine(int argc, char** argv, po::variables_map* conf) {
  po::options_description opts("Configuration options");
  opts.add_options()
        ("weights,w", po::value<string>(), "Weights from previous iteration (used as initialization and interpolation")
        ("interpolation,p",po::value<double>()->default_value(0.9), "Output weights are p*w + (1-p)*w_prev")
        ("memory_buffers,m",po::value<unsigned>()->default_value(200), "Number of memory buffers (LBFGS)")
        ("sigma_squared,s",po::value<double>()->default_value(0.1), "Sigma squared for Gaussian prior")
        ("min_reg,r",po::value<double>()->default_value(1e-8), "When tuning (-T) regularization strength, minimum regularization strenght")
        ("max_reg,R",po::value<double>()->default_value(10.0), "When tuning (-T) regularization strength, maximum regularization strenght")
        ("testset,t",po::value<string>(), "Optional held-out test set")
        ("tune_regularizer,T", "Use the held out test set (-t) to tune the regularization strength")
        ("help,h", "Help");
  po::options_description dcmdline_options;
  dcmdline_options.add(opts);
  po::store(parse_command_line(argc, argv, dcmdline_options), *conf);
  if (conf->count("help")) {
    cerr << dcmdline_options << endl;
    exit(1);
  }
}

void ParseSparseVector(string& line, size_t cur, SparseVector<double>* out) {
  SparseVector<double>& x = *out;
  size_t last_start = cur;
  size_t last_comma = string::npos;
  while(cur <= line.size()) {
    if (line[cur] == ' ' || cur == line.size()) {
      if (!(cur > last_start && last_comma != string::npos && cur > last_comma)) {
        cerr << "[ERROR] " << line << endl << "  position = " << cur << endl;
        exit(1);
      }
      const int fid = FD::Convert(line.substr(last_start, last_comma - last_start));
      if (cur < line.size()) line[cur] = 0;
      const double val = strtod(&line[last_comma + 1], NULL);
      x.set_value(fid, val);

      last_comma = string::npos;
      last_start = cur+1;
    } else {
      if (line[cur] == '=')
        last_comma = cur;
    }
    ++cur;
  }
}

void ReadCorpus(istream* pin, vector<pair<bool, SparseVector<double> > >* corpus) {
  istream& in = *pin;
  corpus->clear();
  bool flag = false;
  int lc = 0;
  string line;
  SparseVector<double> x;
  while(getline(in, line)) {
    ++lc;
    if (lc % 1000 == 0) { cerr << '.'; flag = true; }
    if (lc % 40000 == 0) { cerr << " [" << lc << "]\n"; flag = false; }
    if (line.empty()) continue;
    const size_t ks = line.find("\t");
    assert(string::npos != ks);
    assert(ks == 1);
    const bool y = line[0] == '1';
    x.clear();
    ParseSparseVector(line, ks + 1, &x);
    corpus->push_back(make_pair(y, x));
  }
  if (flag) cerr << endl;
}

void GradAdd(const SparseVector<double>& v, const double scale, vector<double>* acc) {
  for (SparseVector<double>::const_iterator it = v.begin();
       it != v.end(); ++it) {
    (*acc)[it->first] += it->second * scale;
  }
}

double TrainingInference(const vector<double>& x,
                         const vector<pair<bool, SparseVector<double> > >& corpus,
                         vector<double>* g = NULL) {
  double cll = 0;
  for (int i = 0; i < corpus.size(); ++i) {
    const double dotprod = corpus[i].second.dot(x) + x[0]; // x[0] is bias
    double lp_false = dotprod;
    double lp_true = -dotprod;
    if (0 < lp_true) {
      lp_true += log1p(exp(-lp_true));
      lp_false = log1p(exp(lp_false));
    } else {
      lp_true = log1p(exp(lp_true));
      lp_false += log1p(exp(-lp_false));
    }
    lp_true*=-1;
    lp_false*=-1;
    if (corpus[i].first) {  // true label
      cll -= lp_true;
      if (g) {
        // g -= corpus[i].second * exp(lp_false);
        GradAdd(corpus[i].second, -exp(lp_false), g);
        (*g)[0] -= exp(lp_false); // bias
      }
    } else {                  // false label
      cll -= lp_false;
      if (g) {
        // g += corpus[i].second * exp(lp_true);
        GradAdd(corpus[i].second, exp(lp_true), g);
        (*g)[0] += exp(lp_true); // bias
      }
    }
  }
  return cll;
}

// return held-out log likelihood
double LearnParameters(const vector<pair<bool, SparseVector<double> > >& training,
                       const vector<pair<bool, SparseVector<double> > >& testing,
                       const double sigsq,
                       const unsigned memory_buffers,
                       vector<double>* px) {
  vector<double>& x = *px;
  vector<double> vg(FD::NumFeats(), 0.0);
  bool converged = false;
  LBFGSOptimizer opt(FD::NumFeats(), memory_buffers);
  double tppl = 0.0;
  while(!converged) {
    fill(vg.begin(), vg.end(), 0.0);
    double cll = TrainingInference(x, training, &vg);
    double ppl = cll / log(2);
    ppl /= training.size();
    ppl = pow(2.0, ppl);

    // evaluate optional held-out test set
    if (testing.size()) {
      tppl = TrainingInference(x, testing) / log(2);
      tppl /= testing.size();
      tppl = pow(2.0, tppl);
    }

    // handle regularizer
#if 1
    double norm = 0;
    for (int i = 1; i < x.size(); ++i) {
      const double mean_i = 0.0;
      const double param = (x[i] - mean_i);
      norm += param * param;
      vg[i] += param / sigsq;
    }
    const double reg = norm / (2.0 * sigsq);
#else
    double reg = 0;
#endif
    cll += reg;
    cerr << cll << " (REG=" << reg << ")\tPPL=" << ppl << "\t TEST_PPL=" << tppl << "\t";
    try {
      vector<double> old_x = x;
      do {
        opt.Optimize(cll, vg, &x);
        converged = opt.HasConverged();
      } while (!converged && x == old_x);
    } catch (...) {
      cerr << "Exception caught, assuming convergence is close enough...\n";
      converged = true;
    }
    if (fabs(x[0]) > MAX_BIAS) {
      cerr << "Biased model learned. Are your training instances wrong?\n";
      cerr << "  BIAS: " << x[0] << endl;
    }
  }
  return tppl;
}

int main(int argc, char** argv) {
  po::variables_map conf;
  InitCommandLine(argc, argv, &conf);
  string line;
  vector<pair<bool, SparseVector<double> > > training, testing;
  SparseVector<double> old_weights;
  const bool tune_regularizer = conf.count("tune_regularizer");
  if (tune_regularizer && !conf.count("testset")) {
    cerr << "--tune_regularizer requires --testset to be set\n";
    return 1;
  }
  const double min_reg = conf["min_reg"].as<double>();
  const double max_reg = conf["max_reg"].as<double>();
  double sigsq = conf["sigma_squared"].as<double>();
  assert(sigsq > 0.0);
  assert(min_reg > 0.0);
  assert(max_reg > 0.0);
  assert(max_reg > min_reg);
  const double psi = conf["interpolation"].as<double>();
  if (psi < 0.0 || psi > 1.0) { cerr << "Invalid interpolation weight: " << psi << endl; }
  if (conf.count("weights")) {
    Weights w;
    w.InitFromFile(conf["weights"].as<string>());
    w.InitSparseVector(&old_weights);
  }
  ReadCorpus(&cin, &training);
  if (conf.count("testset")) {
    ReadFile rf(conf["testset"].as<string>());
    ReadCorpus(rf.stream(), &testing);
  }
  cerr << "Number of features: " << FD::NumFeats() << endl;
  vector<double> x(FD::NumFeats(), 0.0);  // x[0] is bias
  for (SparseVector<double>::const_iterator it = old_weights.begin();
       it != old_weights.end(); ++it)
    x[it->first] = it->second;
  double tppl = 0.0;
  vector<pair<double,double> > sp;
  vector<double> smoothed;
  if (tune_regularizer) {
    cerr << "Tuning regularizer..." << endl;

    sigsq = min_reg;
    const double steps = 18;
    double sweep_factor = exp((log(max_reg) - log(min_reg)) / steps);
    cerr << "SWEEP FACTOR: " << sweep_factor << endl;
    while(sigsq < max_reg) {
      tppl = LearnParameters(training, testing, sigsq, conf["memory_buffers"].as<unsigned>(), &x);
      sp.push_back(make_pair(sigsq, tppl));
      sigsq *= sweep_factor;
    }
    smoothed.resize(sp.size(), 0);
    smoothed[0] = sp[0].second;
    smoothed.back() = sp.back().second;
    for (int i = 1; i < sp.size()-1; ++i) {
      double prev = sp[i-1].second;
      double next = sp[i+1].second;
      double cur = sp[i].second;
      smoothed[i] = (prev*0.2) + cur * 0.6 + (0.2*next);
    }
    double best_ppl = 9999999;
    unsigned best_i = 0;
    for (unsigned i = 0; i < sp.size(); ++i) {
      if (smoothed[i] < best_ppl) {
        best_ppl = smoothed[i];
        best_i = i;
      }
    }
    sigsq = sp[best_i].first;
  }
  cerr << "Learning parameters..." << endl;
  tppl = LearnParameters(training, testing, sigsq, conf["memory_buffers"].as<unsigned>(), &x);

  Weights w;
  if (conf.count("weights")) {
    cerr << "Interpolating with previous weight vectors..." << endl;
    for (int i = 1; i < x.size(); ++i)
      x[i] = (x[i] * psi) + old_weights.get(i) * (1.0 - psi);
  }
  cout.precision(15);
  cout << "# sigma^2=" << sigsq << "\theld out perplexity=";
  if (tppl) { cout << tppl << endl; } else { cout << "N/A\n"; }
  if (sp.size()) {
    cout << "# Parameter sweep:\n";
    for (int i = 0; i < sp.size(); ++i) {
      cout << "# " << sp[i].first << "\t" << sp[i].second << "\t" << smoothed[i] << endl;
    }
  }
  w.InitFromVector(x);
  w.WriteToFile("-");
  return 0;
}


//  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)

#ifndef BOOST_STATS_EXTREME_VALUE_HPP
#define BOOST_STATS_EXTREME_VALUE_HPP

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

//
// This is the maximum extreme value distribution, see
// http://www.itl.nist.gov/div898/handbook/eda/section3/eda366g.htm
// and http://mathworld.wolfram.com/ExtremeValueDistribution.html
// Also known as a Fisher-Tippett distribution, a log-Weibull
// distribution or a Gumbel distribution.

#include <utility>
#include <cmath>

#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4702) // unreachable code (return after domain_error throw).
#endif

namespace boost{ namespace math{

namespace detail{
//
// Error check:
//
template <class RealType, class Policy>
inline bool verify_scale_b(const char* function, RealType b, RealType* presult, const Policy& pol)
{
   if((b <= 0) || !(boost::math::isfinite)(b))
   {
      *presult = policies::raise_domain_error<RealType>(
         function,
         "The scale parameter \"b\" must be finite and > 0, but was: %1%.", b, pol);
      return false;
   }
   return true;
}

} // namespace detail

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

   extreme_value_distribution(RealType a = 0, RealType b = 1)
      : m_a(a), m_b(b)
   {
      RealType err;
      detail::verify_scale_b("boost::math::extreme_value_distribution<%1%>::extreme_value_distribution", b, &err, Policy());
      detail::check_finite("boost::math::extreme_value_distribution<%1%>::extreme_value_distribution", a, &err, Policy());
   } // extreme_value_distribution

   RealType location()const { return m_a; }
   RealType scale()const { return m_b; }

private:
   RealType m_a, m_b;
};

typedef extreme_value_distribution<double> extreme_value;

#ifdef __cpp_deduction_guides
template <class RealType>
extreme_value_distribution(RealType)->extreme_value_distribution<typename boost::math::tools::promote_args<RealType>::type>;
template <class RealType>
extreme_value_distribution(RealType,RealType)->extreme_value_distribution<typename boost::math::tools::promote_args<RealType>::type>;
#endif

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

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

template <class RealType, class Policy>
inline RealType pdf(const extreme_value_distribution<RealType, Policy>& dist, const RealType& x)
{
   BOOST_MATH_STD_USING // for ADL of std functions

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

   RealType a = dist.location();
   RealType b = dist.scale();
   RealType result = 0;
   if(0 == detail::verify_scale_b(function, b, &result, Policy()))
      return result;
   if(0 == detail::check_finite(function, a, &result, Policy()))
      return result;
   if((boost::math::isinf)(x))
      return 0.0f;
   if(0 == detail::check_x(function, x, &result, Policy()))
      return result;
   RealType e = (a - x) / b;
   if(e < tools::log_max_value<RealType>())
      result = exp(e) * exp(-exp(e)) / b;
   // else.... result *must* be zero since exp(e) is infinite...
   return result;
} // pdf

template <class RealType, class Policy>
inline RealType cdf(const extreme_value_distribution<RealType, Policy>& dist, const RealType& x)
{
   BOOST_MATH_STD_USING // for ADL of std functions

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

   if((boost::math::isinf)(x))
      return x < 0 ? 0.0f : 1.0f;
   RealType a = dist.location();
   RealType b = dist.scale();
   RealType result = 0;
   if(0 == detail::verify_scale_b(function, b, &result, Policy()))
      return result;
   if(0 == detail::check_finite(function, a, &result, Policy()))
      return result;
   if(0 == detail::check_finite(function, a, &result, Policy()))
      return result;
   if(0 == detail::check_x("boost::math::cdf(const extreme_value_distribution<%1%>&, %1%)", x, &result, Policy()))
      return result;

   result = exp(-exp((a-x)/b));

   return result;
} // cdf

template <class RealType, class Policy>
RealType quantile(const extreme_value_distribution<RealType, Policy>& dist, const RealType& p)
{
   BOOST_MATH_STD_USING // for ADL of std functions

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

   RealType a = dist.location();
   RealType b = dist.scale();
   RealType result = 0;
   if(0 == detail::verify_scale_b(function, b, &result, Policy()))
      return result;
   if(0 == detail::check_finite(function, a, &result, Policy()))
      return result;
   if(0 == detail::check_probability(function, p, &result, Policy()))
      return result;

   if(p == 0)
      return -policies::raise_overflow_error<RealType>(function, 0, Policy());
   if(p == 1)
      return policies::raise_overflow_error<RealType>(function, 0, Policy());

   result = a - log(-log(p)) * b;

   return result;
} // quantile

template <class RealType, class Policy>
inline RealType cdf(const complemented2_type<extreme_value_distribution<RealType, Policy>, RealType>& c)
{
   BOOST_MATH_STD_USING // for ADL of std functions

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

   if((boost::math::isinf)(c.param))
      return c.param < 0 ? 1.0f : 0.0f;
   RealType a = c.dist.location();
   RealType b = c.dist.scale();
   RealType result = 0;
   if(0 == detail::verify_scale_b(function, b, &result, Policy()))
      return result;
   if(0 == detail::check_finite(function, a, &result, Policy()))
      return result;
   if(0 == detail::check_x(function, c.param, &result, Policy()))
      return result;

   result = -boost::math::expm1(-exp((a-c.param)/b), Policy());

   return result;
}

template <class RealType, class Policy>
RealType quantile(const complemented2_type<extreme_value_distribution<RealType, Policy>, RealType>& c)
{
   BOOST_MATH_STD_USING // for ADL of std functions

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

   RealType a = c.dist.location();
   RealType b = c.dist.scale();
   RealType q = c.param;
   RealType result = 0;
   if(0 == detail::verify_scale_b(function, b, &result, Policy()))
      return result;
   if(0 == detail::check_finite(function, a, &result, Policy()))
      return result;
   if(0 == detail::check_probability(function, q, &result, Policy()))
      return result;

   if(q == 0)
      return policies::raise_overflow_error<RealType>(function, 0, Policy());
   if(q == 1)
      return -policies::raise_overflow_error<RealType>(function, 0, Policy());

   result = a - log(-boost::math::log1p(-q, Policy())) * b;

   return result;
}

template <class RealType, class Policy>
inline RealType mean(const extreme_value_distribution<RealType, Policy>& dist)
{
   RealType a = dist.location();
   RealType b = dist.scale();
   RealType result = 0;
   if(0 == detail::verify_scale_b("boost::math::mean(const extreme_value_distribution<%1%>&)", b, &result, Policy()))
      return result;
   if (0 == detail::check_finite("boost::math::mean(const extreme_value_distribution<%1%>&)", a, &result, Policy()))
      return result;
   return a + constants::euler<RealType>() * b;
}

template <class RealType, class Policy>
inline RealType standard_deviation(const extreme_value_distribution<RealType, Policy>& dist)
{
   BOOST_MATH_STD_USING // for ADL of std functions.

   RealType b = dist.scale();
   RealType result = 0;
   if(0 == detail::verify_scale_b("boost::math::standard_deviation(const extreme_value_distribution<%1%>&)", b, &result, Policy()))
      return result;
   if(0 == detail::check_finite("boost::math::standard_deviation(const extreme_value_distribution<%1%>&)", dist.location(), &result, Policy()))
      return result;
   return constants::pi<RealType>() * b / sqrt(static_cast<RealType>(6));
}

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

template <class RealType, class Policy>
inline RealType median(const extreme_value_distribution<RealType, Policy>& dist)
{
  using constants::ln_ln_two;
   return dist.location() - dist.scale() * ln_ln_two<RealType>();
}

template <class RealType, class Policy>
inline RealType skewness(const extreme_value_distribution<RealType, Policy>& /*dist*/)
{
   //
   // This is 12 * sqrt(6) * zeta(3) / pi^3:
   // See http://mathworld.wolfram.com/ExtremeValueDistribution.html
   //
   return static_cast<RealType>(1.1395470994046486574927930193898461120875997958366L);
}

template <class RealType, class Policy>
inline RealType kurtosis(const extreme_value_distribution<RealType, Policy>& /*dist*/)
{
   // See http://mathworld.wolfram.com/ExtremeValueDistribution.html
   return RealType(27) / 5;
}

template <class RealType, class Policy>
inline RealType kurtosis_excess(const extreme_value_distribution<RealType, Policy>& /*dist*/)
{
   // See http://mathworld.wolfram.com/ExtremeValueDistribution.html
   return RealType(12) / 5;
}

} // namespace math
} // namespace boost

#ifdef _MSC_VER
# pragma warning(pop)
#endif

// This include must be at the end, *after* the accessors
// for this distribution have been defined, in order to
// keep compilers that support two-phase lookup happy.
#include <boost/math/distributions/detail/derived_accessors.hpp>

#endif // BOOST_STATS_EXTREME_VALUE_HPP


/*
Copyright (c) 2020 Naomasa Matsubayashi

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.
*/

#include <fstream>
#include <iostream>
#include <chrono>
#include <boost/program_options.hpp>
#include <nlohmann/json.hpp>
#include "ifm/bessel.h"
int main( int argc, char* argv[] ) {
  boost::program_options::options_description options("オプション");
  options.add_options()
    ("help,h",    "ヘルプを表示")
    ("bessel,b", boost::program_options::value<std::string>()->default_value( "bessel.mp" ), "ベッセル関数近似係数");
  boost::program_options::variables_map params;
  boost::program_options::store( boost::program_options::parse_command_line( argc, argv, options ), params );
  boost::program_options::notify( params );
  if( params.count("help") ) {
    std::cout << options << std::endl;
    return 0;
  }
  std::vector< std::pair< float, float > > pre;
  {
    std::ifstream bessel_file( params["bessel"].as<std::string>(), std::ofstream::binary );
    nlohmann::json bessel = nlohmann::json::from_msgpack( bessel_file );
    for( const auto &v: bessel )
      pre.emplace_back( v.at( 0 ), v.at( 1 ) );
  }
  const auto t0 = std::chrono::steady_clock::now();
  for( unsigned int b_ = 0.f; b_ != 400; ++b_ ) {
    float b = b_ * 0.1f;
    for( unsigned int n = 0; n != 60; ++n ) {
      ifm::bessel_kind1( b, n );
    }
  }
  const auto t1 = std::chrono::steady_clock::now();
  for( unsigned int b_ = 0.f; b_ != 400; ++b_ ) {
    float b = b_ * 0.1f;
    float l1 = 0;
    float l2 = 0;
    for( unsigned int n = 0; n != 60; ++n ) {
      float y = ifm::bessel_kind1_approx_2019( b, n, l1, l2, pre );
      l2 = l1;
      l1 = y;
    }
  }
  const auto t2 = std::chrono::steady_clock::now();
  float e0 = float( std::chrono::duration_cast< std::chrono::microseconds >( t1 - t0 ).count() );
  float e1 = float( std::chrono::duration_cast< std::chrono::microseconds >( t2 - t1 ).count() );
  std::cout << "台形公式: " << (400*60)/e0 << "Mbps(" << 400*60 << " samples in " << e0 << "microseconds)" << std::endl;
  std::cout << "近似式: " << (400*60)/e1 << "Mbps(" << 400*60 << " samples in " << e1 << "microseconds)" << std::endl;
}


//  Copyright John Maddock 2012 - 2021.
//  Copyright Christopher Kormanyos 2016 - 2021.
//  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)
//

// Wrapper that works with e_float-2021, see also
// https://github.com/ckormanyos/e_float-2021

#ifndef E_FLOAT_2017_08_18_HPP_
  #define E_FLOAT_2017_08_18_HPP_

  #include <cstdint>
  #include <sstream>
  #include <tuple>
  #include <type_traits>

  #include <e_float/e_float.h>
  #include <e_float/e_float_functions.h>

  #include <boost/config.hpp>
  #include <boost/multiprecision/number.hpp>

  namespace boost { namespace math { namespace ef {

  // Forward declaration of the e_float multiple precision class.
  // This class binds native ::e_float to boost::multiprecsion::e_float.
  class e_float;

  } } }

  // Define the number category as a floating-point kind
  // for the e_float. This is needed for properly
  // interacting as a backend with boost::muliprecision.
  #if (BOOST_VERSION <= 107200)
  template<>
  struct boost::multiprecision::number_category<boost::math::ef::e_float>
    : public boost::integral_constant<int, boost::multiprecision::number_kind_floating_point> { };
  #elif (BOOST_VERSION <= 107500)
  template<>
  struct boost::multiprecision::number_category<boost::math::ef::e_float>
    : public boost::integral_constant<int, boost::multiprecision::number_kind_floating_point> { };
  #else
  template<>
  struct boost::multiprecision::number_category<boost::math::ef::e_float>
    : public std::integral_constant<int, boost::multiprecision::number_kind_floating_point> { };
  #endif

  namespace boost { namespace math { namespace ef {

  // This is the e_float multiple precision class.
  class e_float
  {
  public:
    #if (BOOST_VERSION <= 107500)
    using signed_types   = mpl::list<std::int64_t>;
    using unsigned_types = mpl::list<std::uint64_t>;
    using float_types    = mpl::list<long double>;
    #else
    using   signed_types = std::tuple<  signed char,   signed short,   signed int,   signed long,   signed long long, std::intmax_t>;
    using unsigned_types = std::tuple<unsigned char, unsigned short, unsigned int, unsigned long, unsigned long long, std::uintmax_t>;
    using float_types    = std::tuple<float, double, long double>;
    #endif
    using exponent_type  = std::int64_t;

    e_float() : m_value() { }

    explicit e_float(const ::e_float& rep) : m_value(rep) { }

    e_float(const e_float& other) : m_value(other.m_value) { }
    e_float(e_float&& other) : m_value(static_cast<::e_float&&>(other.m_value)) { }

    template<typename UnsignedIntegralType,
             typename std::enable_if<(   (std::is_integral<UnsignedIntegralType>::value == true)
                                      && (std::is_unsigned<UnsignedIntegralType>::value == true))>::type const* = nullptr>
    e_float(UnsignedIntegralType u) : m_value(::e_float(std::uint64_t(u))) { }

    template<typename SignedIntegralType,
             typename std::enable_if<(   (std::is_integral<SignedIntegralType>::value == true)
                                      && (std::is_signed  <SignedIntegralType>::value == true))>::type const* = nullptr>
    e_float(SignedIntegralType n) : m_value(::e_float(std::int64_t(n))) { }

    template<typename FloatingPointType,
             typename std::enable_if<std::is_floating_point<FloatingPointType>::value == true>::type const* = nullptr>
    e_float(FloatingPointType f) : m_value(::e_float(static_cast<long double>(f))) { }

    e_float(const char* c) : m_value(c) { }

    e_float(const std::string& str) : m_value(str) { }

    ~e_float() { }

    e_float& operator=(const e_float& other)
    {
      if(this != &other)
      {
        m_value = other.m_value;
      }

      return *this;
    }

    e_float& operator=(e_float&& other)
    {
      m_value.operator=(static_cast<::e_float&&>(other.m_value));

      return *this;
    }

    template<typename ArithmeticType,
             typename std::enable_if<std::is_arithmetic<ArithmeticType>::value == true>::type const* = nullptr>
    e_float& operator=(const ArithmeticType& x)
    {
      m_value = ::e_float(x);

      return *this;
    }

    e_float& operator=(const std::string& str_rep)  { m_value = ::e_float(str_rep);  return *this; }
    e_float& operator=(const char*        char_ptr) { m_value = ::e_float(char_ptr); return *this; }

    void swap(e_float& other_mp_cpp_backend)
    {
      m_value.swap(other_mp_cpp_backend.m_value);
    }

          ::e_float&  representation()       { return m_value; }
    const ::e_float&  representation() const { return m_value; }
    const ::e_float& crepresentation() const { return m_value; }

    std::string str(std::streamsize number_of_digits, const std::ios::fmtflags format_flags) const
    {
      std::string        my_result_str;
      std::stringstream  my_stream_str;

      my_stream_str.flags(format_flags);

      static_cast<void>(my_stream_str.precision(number_of_digits));

      m_value.wr_string(my_result_str, my_stream_str);

      return my_result_str;
    }

    void negate()
    {
      m_value.negate();
    }

    int compare(const e_float& other_mp_cpp_backend) const
    {
      return static_cast<int>(m_value.compare(other_mp_cpp_backend.m_value));
    }

    template<typename ArithmeticType,
             typename std::enable_if<std::is_arithmetic<ArithmeticType>::value == true>::type const* = nullptr>
    int compare(ArithmeticType x)
    {
      return static_cast<int>(m_value.compare(::e_float(x)));
    }

  private:
    ::e_float m_value;

    e_float& operator=(const ::e_float&) = delete;
  };

  inline void eval_add(e_float& result, const e_float& x)
  {
    result.representation() += x.crepresentation();
  }

  inline void eval_subtract(e_float& result, const e_float& x)
  {
    result.representation() -= x.crepresentation();
  }

  inline void eval_multiply(e_float& result, const e_float& x)
  {
    result.representation() *= x.crepresentation();
  }

  inline void eval_multiply(e_float& result, const e_float& x, const e_float& y)
  {
    result.representation() = x.crepresentation() * y.crepresentation();
  }

  template<typename SignedIntegralType,
           typename std::enable_if<(   (std::is_integral<SignedIntegralType>::value == true)
                                    && (std::is_signed  <SignedIntegralType>::value == true))>::type const* = nullptr>
  void eval_multiply(e_float& result, const SignedIntegralType& n)
  {
    result.representation().mul_signed_long_long(static_cast<std::int64_t>(n));
  }

  inline void eval_divide(e_float& result, const e_float& x)
  {
    result.representation() /= x.crepresentation();
  }

  template<typename SignedIntegralType,
           typename std::enable_if<(   (std::is_integral<SignedIntegralType>::value == true)
                                    && (std::is_signed  <SignedIntegralType>::value == true))>::type const* = nullptr>
  void eval_divide(e_float& result, const SignedIntegralType& n)
  {
    result.representation().div_signed_long_long(static_cast<std::int64_t>(n));
  }

  inline bool eval_eq(const e_float& a, const e_float& b)
  {
    return (a.compare(b) == 0);
  }

  template<typename ArithmeticType,
           typename std::enable_if<std::is_arithmetic<ArithmeticType>::value == true>::type const* = nullptr>
  bool eval_eq(const e_float& a, const ArithmeticType& b)
  {
    return (a.compare(b) == 0);
  }

  template<typename ArithmeticType,
           typename std::enable_if<std::is_arithmetic<ArithmeticType>::value == true>::type const* = nullptr>
  bool eval_eq(const ArithmeticType& a, const e_float& b)
  {
    return (e_float(a).compare(b) == 0);
  }

  inline bool eval_gt(const e_float& a, const e_float& b)
  {
    return (a.compare(b) == 1);
  }

  template<typename ArithmeticType,
           typename std::enable_if<std::is_arithmetic<ArithmeticType>::value == true>::type const* = nullptr>
  bool eval_gt(const e_float& a, const ArithmeticType& b)
  {
    return (a.compare(b) == 1);
  }

  template<typename ArithmeticType,
           typename std::enable_if<std::is_arithmetic<ArithmeticType>::value>::type const* = nullptr>
  bool eval_gt(const ArithmeticType& a, const e_float& b)
  {
    return (e_float(a).compare(b) == 1);
  }

  inline bool eval_lt(const e_float& a, const e_float& b)
  {
    return (a.compare(b) == -1);
  }

  template<typename ArithmeticType,
           typename std::enable_if<std::is_arithmetic<ArithmeticType>::value == true>::type const* = nullptr>
  bool eval_lt(const e_float& a, const ArithmeticType& b)
  {
    return (a.compare(b) == -1);
  }

  template<typename ArithmeticType,
           typename std::enable_if<std::is_arithmetic<ArithmeticType>::value == true>::type const* = nullptr>
  bool eval_lt(const ArithmeticType& a, const e_float& b)
  {
    return (e_float(a).compare(b) == -1);
  }

  inline bool eval_is_zero(const e_float& x)
  {
    return x.crepresentation().iszero();
  }

  inline int eval_get_sign(const e_float& x)
  {
    if     (x.crepresentation().iszero()) { return  0; }
    else if(x.crepresentation().isneg ()) { return -1; }
    else                                  { return  1; }
  }

  inline void eval_convert_to(unsigned long long* result,
                              const e_float& val)
  {
    *result = (val.crepresentation()).extract_unsigned_long_long();
  }

  inline void eval_convert_to(signed long long* result,
                              const e_float& val)
  {
    *result = (val.crepresentation()).extract_signed_long_long();
  }

  inline void eval_convert_to(long double* result,
                              const e_float& val)
  {
    *result = (val.crepresentation()).extract_long_double();
  }

  inline void eval_frexp(      e_float&                         result,
                         const e_float&                         x,
                               typename e_float::exponent_type* expptr)
  {
    using local_exponent_type = int;

    local_exponent_type exp2;

    result.representation() = ::ef::frexp(x.crepresentation(), &exp2);

    *expptr = static_cast<typename e_float::exponent_type>(exp2);
  }

  inline void eval_frexp(      e_float& result,
                         const e_float& x,
                         int*           expptr,
                         typename std::enable_if<std::is_same<typename e_float::exponent_type, int>::value == false>::type const* = nullptr)
  {
    result.representation() = ::ef::frexp(x.crepresentation(), expptr);
  }

  inline void eval_ldexp(      e_float& result,
                         const e_float& x,
                         const typename e_float::exponent_type exp_value)
  {
    using local_exponent_type = int;

    result.representation() = ::ef::ldexp(x.crepresentation(), local_exponent_type(exp_value));
  }

  inline void eval_ldexp(      e_float& result,
                         const e_float& x,
                         int            exp_value,
                         typename std::enable_if<std::is_same<typename e_float::exponent_type, int>::value == false>::type const* = nullptr)
  {
    using local_exponent_type = int;

    result.representation() = ::ef::ldexp(x.crepresentation(), local_exponent_type(exp_value));
  }

  inline e_float::exponent_type eval_ilogb(const e_float& val)
  {
    if(val.crepresentation().iszero())
    {
      return (std::numeric_limits<e_float::exponent_type>::min)();
    }

    if ((val.crepresentation().isinf)())
    {
      return (std::numeric_limits<e_float::exponent_type>::max)();
    }

    if ((val.crepresentation().isnan)())
    {
      #if defined(FP_ILOGBNAN)
      return FP_ILOGBNAN;
      #else
      return (std::numeric_limits<e_float::exponent_type>::max)();
      #endif
    }

   // Set the result to base-10 exponent (order) of the value.
   return val.crepresentation().order();
  }

  template<typename IntegralType,
           typename std::enable_if<(   (std::is_fundamental<IntegralType>::value == true)
                                    && (std::is_integral   <IntegralType>::value == true))>::type const* = nullptr>
  void eval_scalbn(e_float& result, const e_float& val, IntegralType e)
  {
    const std::int64_t my_e = static_cast<std::int64_t>(e);

    const e_float t(::e_float(1.0, my_e));

    eval_multiply(result, val, t);
  }

  inline void eval_floor(      e_float& result,
                         const e_float& x)
  {
    result.representation() = ::ef::floor(x.crepresentation());
  }

  inline void eval_ceil(      e_float& result,
                        const e_float& x)
  {
    result.representation() = ::ef::ceil(x.crepresentation());
  }

  inline int eval_fpclassify(const e_float& x)
  {
    if     ((x.crepresentation().isinf)()) { return FP_INFINITE; }
    else if((x.crepresentation().isnan)()) { return FP_NAN; }
    else if( x.crepresentation().iszero()) { return FP_ZERO; }
    else                                   { return FP_NORMAL; }
  }

  inline void eval_trunc(      e_float& result,
                         const e_float& x)
  {
    result.representation() = ::ef::integer_part(x.crepresentation());
  }

  inline void eval_abs(      e_float& result,
                       const e_float& x)
  {
    result.representation() = x.crepresentation();

    if(result.crepresentation().isneg())
    {
      result.representation().negate();
    }
  }

  inline void eval_fabs(      e_float& result,
                        const e_float& x)
  {
    result.representation() = x.crepresentation();

    if(result.crepresentation().isneg())
    {
      result.representation().negate();
    }
  }

  inline void eval_sqrt(      e_float& result,
                        const e_float& x)
  {
    result.representation() = ::ef::sqrt(x.crepresentation());
  }

  inline void eval_sin(      e_float& result,
                       const e_float& x)
  {
    result.representation() = ::ef::sin(x.crepresentation());
  }

  inline void eval_cos(      e_float& result,
                       const e_float& x)
  {
    result.representation() = ::ef::cos(x.crepresentation());
  }

  inline void eval_tan(      e_float& result,
                       const e_float& x)
  {
    result.representation() = ::ef::tan(x.crepresentation());
  }

  inline void eval_asin(      e_float& result,
                        const e_float& x)
  {
    result.representation() = ::ef::asin(x.crepresentation());
  }

  inline void eval_acos(      e_float& result,
                        const e_float& x)
  {
    result.representation() = ::ef::acos(x.crepresentation());
  }

  inline void eval_atan(      e_float& result,
                        const e_float& x)
  {
    result.representation() = ::ef::atan(x.crepresentation());
  }

  inline void eval_atan2(      e_float& result,
                         const e_float& y,
                         const e_float& x)
  {
    result.representation() = ::ef::atan2(y.crepresentation(), x.crepresentation());
  }

  inline void eval_log(      e_float& result,
                       const e_float& x)
  {
    result.representation() = ::ef::log(x.crepresentation());
  }

  inline void eval_log10(      e_float& result,
                         const e_float& x)
  {
    result.representation() = ::ef::log10(x.crepresentation());
  }

  inline void eval_exp(      e_float& result,
                       const e_float& x)
  {
    result.representation() = ::ef::exp(x.crepresentation());
  }

  inline void eval_sinh(      e_float& result,
                        const e_float& x)
  {
    result.representation() = ::ef::sinh(x.crepresentation());
  }

  inline void eval_cosh(      e_float& result,
                        const e_float& x)
  {
    result.representation() = ::ef::cosh(x.crepresentation());
  }

  inline void eval_tanh(      e_float& result,
                        const e_float& x)
  {
    result.representation() = ::ef::tanh(x.crepresentation());
  }

  inline void eval_fmod(      e_float& result,
                        const e_float& x,
                        const e_float& y)
  {
    if(y.crepresentation().iszero())
    {
      result.representation() = ::ef::zero();
    }
    else
    {
      // Calculate the fractional part of x such that:
      //   x = (integer_part * y) + fractional_part,
      // where
      //   |fractional_part| < |y|,
      // and fractional_part has the same sign as x.

      const ::e_float integer_part = ::ef::floor(x.crepresentation() / y.crepresentation());

      result.representation() =
        x.crepresentation() - (integer_part * y.crepresentation());

      if(x.crepresentation().isneg() != y.crepresentation().isneg())
      {
        result.representation() -= y.crepresentation();
      }
    }
  }

  inline void eval_pow(      e_float& result,
                       const e_float& x,
                       const e_float& a)
  {
    result.representation() = ::ef::pow(x.crepresentation(), a.crepresentation());
  }

  } } } // namespace boost::math::ef

  namespace boost { namespace math { namespace policies {

  // Specialization of the precision structure.
  template<typename ThisPolicy,
           const boost::multiprecision::expression_template_option ExpressionTemplates>
  struct precision<boost::multiprecision::number<boost::math::ef::e_float,
                                                 ExpressionTemplates>,
                   ThisPolicy>
  {
    using precision_type = typename ThisPolicy::precision_type;

    using local_digits_2 = digits2<((::e_float::ef_digits10 + 1LL) * 1000LL) / 301LL>;

    #if (BOOST_VERSION <= 107500)
    using type = typename mpl::if_c       <((local_digits_2::value <= precision_type::value) || (precision_type::value <= 0)),
                                           local_digits_2,
                                           precision_type>::type;
    #else
    using type = typename std::conditional<((local_digits_2::value <= precision_type::value) || (precision_type::value <= 0)),
                                           local_digits_2,
                                           precision_type>::type;
    #endif
  };

  } } } // namespaces boost::math::policies

  namespace std
  {
    template<const boost::multiprecision::expression_template_option ExpressionTemplates>
    class numeric_limits<boost::multiprecision::number<boost::math::ef::e_float,
                                                       ExpressionTemplates>>
    {
    public:
      static constexpr bool is_specialized = true;
      static constexpr bool is_signed      = true;
      static constexpr bool is_integer     = false;
      static constexpr bool is_exact       = false;
      static constexpr bool is_bounded     = true;
      static constexpr bool is_modulo      = false;
      static constexpr bool is_iec559      = false;
      static constexpr int  digits         = ::e_float::ef_digits10;
      static constexpr int  digits10       = ::e_float::ef_digits10;
      static constexpr int  max_digits10   = ::e_float::ef_digits10 + 1;

      static constexpr std::int64_t max_exponent   = ::e_float::ef_max_exp;
      static constexpr std::int64_t max_exponent10 = ::e_float::ef_max_exp10;
      static constexpr std::int64_t min_exponent   = ::e_float::ef_min_exp;
      static constexpr std::int64_t min_exponent10 = ::e_float::ef_min_exp10;

      static constexpr int                     radix             = 10;
      static constexpr std::float_round_style  round_style       = std::round_indeterminate;
      static constexpr bool                    has_infinity      = true;
      static constexpr bool                    has_quiet_NaN     = true;
      static constexpr bool                    has_signaling_NaN = false;
      static constexpr std::float_denorm_style has_denorm        = std::denorm_absent;
      static constexpr bool                    has_denorm_loss   = false;
      static constexpr bool                    traps             = false;
      static constexpr bool                    tinyness_before   = false;

      static constexpr boost::multiprecision::number<boost::math::ef::e_float, ExpressionTemplates> (min)        () { return boost::math::ef::e_float(ef::value_min()); }
      static constexpr boost::multiprecision::number<boost::math::ef::e_float, ExpressionTemplates> (max)        () { return boost::math::ef::e_float(ef::value_max()); }
      static constexpr boost::multiprecision::number<boost::math::ef::e_float, ExpressionTemplates> lowest       () { return boost::math::ef::e_float(ef::zero()); }
      static constexpr boost::multiprecision::number<boost::math::ef::e_float, ExpressionTemplates> epsilon      () { return boost::math::ef::e_float(ef::value_eps()); }
      static constexpr boost::multiprecision::number<boost::math::ef::e_float, ExpressionTemplates> round_error  () { return boost::math::ef::e_float(ef::half()); }
      static constexpr boost::multiprecision::number<boost::math::ef::e_float, ExpressionTemplates> infinity     () { return boost::math::ef::e_float(ef::value_inf()); }
      static constexpr boost::multiprecision::number<boost::math::ef::e_float, ExpressionTemplates> quiet_NaN    () { return boost::math::ef::e_float(ef::value_nan()); }
      static constexpr boost::multiprecision::number<boost::math::ef::e_float, ExpressionTemplates> signaling_NaN() { return boost::math::ef::e_float(ef::zero()); }
      static constexpr boost::multiprecision::number<boost::math::ef::e_float, ExpressionTemplates> denorm_min   () { return boost::math::ef::e_float(ef::zero()); }
    };
  } // namespace std

#endif // E_FLOAT_2017_08_18_HPP_


// Boost.Geometry

// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.

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

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

#ifndef BOOST_GEOMETRY_ALGORITHMS_AZIMUTH_HPP
#define BOOST_GEOMETRY_ALGORITHMS_AZIMUTH_HPP

#include <boost/geometry/algorithms/not_implemented.hpp>

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

#include <boost/geometry/strategies/default_strategy.hpp>
#include <boost/geometry/strategies/azimuth/cartesian.hpp>
#include <boost/geometry/strategies/azimuth/geographic.hpp>
#include <boost/geometry/strategies/azimuth/spherical.hpp>

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

namespace boost { namespace geometry
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail
{

} // namespace detail
#endif // DOXYGEN_NO_DETAIL

#ifndef DOXYGEN_NO_DISPATCH
namespace dispatch
{

template
<
    typename Geometry1, typename Geometry2,
    typename Tag1 = typename tag<Geometry1>::type,
    typename Tag2 = typename tag<Geometry2>::type
>
struct azimuth : not_implemented<Tag1, Tag2>
{};

template <typename Point1, typename Point2>
struct azimuth<Point1, Point2, point_tag, point_tag>
{
    template <typename Strategy>
    static auto apply(Point1 const& p1, Point2 const& p2, Strategy const& strategy)
    {
        typedef typename decltype(strategy.azimuth())::template result_type
            <
                typename coordinate_type<Point1>::type,
                typename coordinate_type<Point2>::type
            >::type calc_t;

        calc_t result = 0;
        calc_t const x1 = geometry::get_as_radian<0>(p1);
        calc_t const y1 = geometry::get_as_radian<1>(p1);
        calc_t const x2 = geometry::get_as_radian<0>(p2);
        calc_t const y2 = geometry::get_as_radian<1>(p2);

        strategy.azimuth().apply(x1, y1, x2, y2, result);

        // NOTE: It is not clear which units we should use for the result.
        //   For now radians are always returned but a user could expect
        //   e.g. something like this:
        /*
        bool const both_degree = std::is_same
                <
                    typename detail::cs_angular_units<Point1>::type,
                    geometry::degree
                >::value
            && std::is_same
                <
                    typename detail::cs_angular_units<Point2>::type,
                    geometry::degree
                >::value;
        if (both_degree)
        {
            result *= math::r2d<calc_t>();
        }
        */

        return result;
    }
};

} // namespace dispatch
#endif // DOXYGEN_NO_DISPATCH

namespace resolve_strategy
{

template
<
    typename Strategy,
    bool IsUmbrella = strategies::detail::is_umbrella_strategy<Strategy>::value
>
struct azimuth
{
    template <typename P1, typename P2>
    static auto apply(P1 const& p1, P2 const& p2, Strategy const& strategy)
    {
        return dispatch::azimuth<P1, P2>::apply(p1, p2, strategy);
    }
};

template <typename Strategy>
struct azimuth<Strategy, false>
{
    template <typename P1, typename P2>
    static auto apply(P1 const& p1, P2 const& p2, Strategy const& strategy)
    {
        using strategies::azimuth::services::strategy_converter;
        return dispatch::azimuth
            <
                P1, P2
            >::apply(p1, p2, strategy_converter<Strategy>::get(strategy));
    }
};

template <>
struct azimuth<default_strategy, false>
{
    template <typename P1, typename P2>
    static auto apply(P1 const& p1, P2 const& p2, default_strategy)
    {
        typedef typename strategies::azimuth::services::default_strategy
            <
                P1, P2
            >::type strategy_type;

        return dispatch::azimuth<P1, P2>::apply(p1, p2, strategy_type());
    }
};

} // namespace resolve_strategy

namespace resolve_variant
{
} // namespace resolve_variant

/*!
\brief Calculate azimuth of a segment defined by a pair of points.
\ingroup azimuth
\tparam Point1 Type of the first point of a segment.
\tparam Point2 Type of the second point of a segment.
\param point1 First point of a segment.
\param point2 Second point of a segment.
\return Azimuth in radians.

\qbk{[include reference/algorithms/azimuth.qbk]}

\qbk{
[heading Example]
[azimuth]
[azimuth_output]
}
*/
template <typename Point1, typename Point2>
inline auto azimuth(Point1 const& point1, Point2 const& point2)
{
    concepts::check<Point1 const>();
    concepts::check<Point2 const>();

    return resolve_strategy::azimuth
            <
                default_strategy
            >::apply(point1, point2, default_strategy());
}

/*!
\brief Calculate azimuth of a segment defined by a pair of points.
\ingroup azimuth
\tparam Point1 Type of the first point of a segment.
\tparam Point2 Type of the second point of a segment.
\tparam Strategy Type of an umbrella strategy defining azimuth strategy.
\param point1 First point of a segment.
\param point2 Second point of a segment.
\param strategy Umbrella strategy defining azimuth strategy.
\return Azimuth in radians.

\qbk{distinguish,with strategy}
\qbk{[include reference/algorithms/azimuth.qbk]}

\qbk{
[heading Example]
[azimuth_strategy]
[azimuth_strategy_output]
}
*/
template <typename Point1, typename Point2, typename Strategy>
inline auto azimuth(Point1 const& point1, Point2 const& point2, Strategy const& strategy)
{
    concepts::check<Point1 const>();
    concepts::check<Point2 const>();

    return resolve_strategy::azimuth<Strategy>::apply(point1, point2, strategy);
}

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_ALGORITHMS_AZIMUTH_HPP


// Boost.Geometry (aka GGL, Generic Geometry Library)
// This file is manually converted from PROJ4 (projects.h)

// Copyright (c) 2008-2012 Barend Gehrels, Amsterdam, the Netherlands.

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

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

// This file is converted from PROJ4, http://trac.osgeo.org/proj
// PROJ4 is originally written by Gerald Evenden (then of the USGS)
// PROJ4 is maintained by Frank Warmerdam
// PROJ4 is converted to Geometry Library by Barend Gehrels (Geodan, Amsterdam)

// Original copyright notice:

// 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.

#ifndef BOOST_GEOMETRY_PROJECTIONS_IMPL_PROJECTS_HPP
#define BOOST_GEOMETRY_PROJECTIONS_IMPL_PROJECTS_HPP

#include <cstring>
#include <string>
#include <vector>

#include <boost/geometry/srs/projections/exception.hpp>
#include <boost/math/constants/constants.hpp>
#include <boost/mpl/if.hpp>
#include <boost/type_traits/is_pod.hpp>

namespace boost { namespace geometry { namespace projections
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail
{

/* some useful constants */
template <typename T>
inline T ONEPI() { return boost::math::constants::pi<T>(); }
template <typename T>
inline T HALFPI() { return boost::math::constants::half_pi<T>(); }
template <typename T>
inline T FORTPI() { return boost::math::constants::pi<T>() / T(4); }
template <typename T>
inline T TWOPI() { return boost::math::constants::two_pi<T>(); }
template <typename T>
inline T TWO_D_PI() { return boost::math::constants::two_div_pi<T>(); }
template <typename T>
inline T HALFPI_SQR() { return 2.4674011002723396547086227499689; }
template <typename T>
inline T PI_SQR() { return boost::math::constants::pi_sqr<T>(); }
template <typename T>
inline T THIRD() { return 0.3333333333333333333333333333333; }
template <typename T>
inline T TWOTHIRD() { return 0.6666666666666666666666666666666; }
template <typename T>
inline T PI_HALFPI() { return 4.7123889803846898576939650749193; }
template <typename T>
inline T TWOPI_HALFPI() { return 7.8539816339744830961566084581988; }
template <typename T>
inline T PI_DIV_3() { return 1.0471975511965977461542144610932; }

/* datum_type values */
static const int PJD_UNKNOWN = 0;
static const int PJD_3PARAM = 1;
static const int PJD_7PARAM = 2;
static const int PJD_GRIDSHIFT = 3;
static const int PJD_WGS84 = 4;   /* WGS84 (or anything considered equivelent) */

/* library errors */
static const int PJD_ERR_GEOCENTRIC = -45;
static const int PJD_ERR_AXIS = -47;
static const int PJD_ERR_GRID_AREA = -48;
static const int PJD_ERR_CATALOG = -49;

template <typename T>
struct pvalue
{
    std::string param;
    int used;

    int i;
    T f;
    std::string s;
};

template <typename T>
struct pj_const_pod
{
    int over;   /* over-range flag */
    int geoc;   /* geocentric latitude flag */
    int is_latlong; /* proj=latlong ... not really a projection at all */
    int is_geocent; /* proj=geocent ... not really a projection at all */
    T
        a,  /* major axis or radius if es==0 */
        a_orig, /* major axis before any +proj related adjustment */
        es, /* e ^ 2 */
        es_orig, /* es before any +proj related adjustment */
        e,  /* eccentricity */
        ra, /* 1/A */
        one_es, /* 1 - e^2 */
        rone_es, /* 1/one_es */
        lam0, phi0, /* central longitude, latitude */
        x0, y0, /* easting and northing */
        k0,    /* general scaling factor */
        to_meter, fr_meter, /* cartesian scaling */
        vto_meter, vfr_meter;      /* Vertical scaling. Internal unit [m] */

    int datum_type; /* PJD_UNKNOWN/3PARAM/7PARAM/GRIDSHIFT/WGS84 */
    T  datum_params[7];
    T  from_greenwich; /* prime meridian offset (in radians) */
    T  long_wrap_center; /* 0.0 for -180 to 180, actually in radians*/
    bool    is_long_wrap_set;

    // Initialize all variables to zero
    pj_const_pod()
    {
        std::memset(this, 0, sizeof(pj_const_pod));
    }
};

template <typename T>
struct pj_const_non_pod
{
    int over;   /* over-range flag */
    int geoc;   /* geocentric latitude flag */
    int is_latlong; /* proj=latlong ... not really a projection at all */
    int is_geocent; /* proj=geocent ... not really a projection at all */
    T
        a,  /* major axis or radius if es==0 */
        a_orig, /* major axis before any +proj related adjustment */
        es, /* e ^ 2 */
        es_orig, /* es before any +proj related adjustment */
        e,  /* eccentricity */
        ra, /* 1/A */
        one_es, /* 1 - e^2 */
        rone_es, /* 1/one_es */
        lam0, phi0, /* central longitude, latitude */
        x0, y0, /* easting and northing */
        k0,    /* general scaling factor */
        to_meter, fr_meter, /* cartesian scaling */
        vto_meter, vfr_meter;      /* Vertical scaling. Internal unit [m] */

    int datum_type; /* PJD_UNKNOWN/3PARAM/7PARAM/GRIDSHIFT/WGS84 */
    T  datum_params[7];
    T  from_greenwich; /* prime meridian offset (in radians) */
    T  long_wrap_center; /* 0.0 for -180 to 180, actually in radians*/
    bool    is_long_wrap_set;

    // Initialize all variables to zero
    pj_const_non_pod()
        : over(0), geoc(0), is_latlong(0), is_geocent(0)
        , a(0), a_orig(0), es(0), es_orig(0), e(0), ra(0)
        , one_es(0), rone_es(0), lam0(0), phi0(0), x0(0), y0(0), k0(0)
        , to_meter(0), fr_meter(0), vto_meter(0), vfr_meter(0)
        , datum_type(PJD_UNKNOWN)
        , from_greenwich(0), long_wrap_center(0), is_long_wrap_set(false)
    {
        datum_params[0] = 0;
        datum_params[1] = 0;
        datum_params[2] = 0;
        datum_params[3] = 0;
        datum_params[4] = 0;
        datum_params[5] = 0;
        datum_params[6] = 0;
    }
};

template <typename T>
struct pj_const
    : boost::mpl::if_c
        <
            boost::is_pod<T>::value,
            pj_const_pod<T>,
            pj_const_non_pod<T>
        >::type
{};

// PROJ4 complex. Might be replaced with std::complex
template <typename T>
struct COMPLEX { T r, i; };

struct PJ_ELLPS
{
    std::string id;    /* ellipse keyword name */
    std::string major;    /* a= value */
    std::string ell;    /* elliptical parameter */
    std::string name;    /* comments */
};

struct PJ_DATUMS
{
    std::string id;     /* datum keyword */
    std::string defn;   /* ie. "to_wgs84=..." */
    std::string ellipse_id; /* ie from ellipse table */
    std::string comments; /* EPSG code, etc */
};

struct PJ_PRIME_MERIDIANS
{
    std::string id;     /* prime meridian keyword */
    std::string defn;   /* offset from greenwich in DMS format. */
};

struct PJ_UNITS
{
    std::string id;    /* units keyword */
    std::string to_meter;    /* multiply by value to get meters */
    std::string name;    /* comments */
};

template <typename T>
struct DERIVS
{
    T x_l, x_p; /* derivatives of x for lambda-phi */
    T y_l, y_p; /* derivatives of y for lambda-phi */
};

template <typename T>
struct FACTORS
{
    DERIVS<T> der;
    T h, k;    /* meridinal, parallel scales */
    T omega, thetap;    /* angular distortion, theta prime */
    T conv;    /* convergence */
    T s;        /* areal scale factor */
    T a, b;    /* max-min scale error */
    int code;        /* info as to analytics, see following */
};

} // namespace detail
#endif // DOXYGEN_NO_DETAIL

/*!
    \brief parameters, projection parameters
    \details This structure initializes all projections
    \ingroup projection
*/
template <typename T>
struct parameters : public detail::pj_const<T>
{
    typedef T type;

    std::string name;
    std::vector<detail::pvalue<T> > params;
};

}}} // namespace boost::geometry::projections
#endif // BOOST_GEOMETRY_PROJECTIONS_IMPL_PROJECTS_HPP


#include <stdio.h>
#include <fstream>
#include <cmath>
#include <malloc.h>
#include <armadillo>
#include <stdlib.h>
#include <functions.h>
#include <stdbool.h>
#include <string.h>
char *outFile;

char* combine(char *a,char *b)
{
  char *c=(char*)malloc(strlen(a) + strlen(b) + 1);
  strcpy(c,a);
  strcat(c,b);
  return c;
}

#define FOR(i,n) for(i=0;i<n;i++)
#define error(i) printf(i);
using namespace arma;
using namespace std;
typedef double DD;
typedef long LL;

typedef struct{
	LL rows;
	LL columns;
	DD** matrix;
} MAT;

typedef struct{
    DD delta;
    LL nofIter;
    DD C;
    DD Cinit;
    DD Cfinal;
    LL nofNn;
    DD sigma;
    DD opt_tb;
    LL maxiter;
    DD tolfun;
    DD s;
    DD* splits;
} Opt;

typedef struct{
	DD tolX;
	LL length;
	DD tolFun;

} Opt2;

typedef struct{
    DD pathNorm;
    LL nofNn;
    DD annEps;
    LL rbf;
    DD sigma;
} Param;

#include "CalcNnDists.c"
#include "GraphDistKernelC.c"
#include "MDS.c"

typedef struct{
	DD obj;
	DD b;
	MAT Yu;
	MAT w;
} Output_Primal;

DD** Multiply_Matrices(DD **Mat1, DD **Mat2, LL M, LL Q, LL P)
{
	LL i, c, d, k;
	DD Sum = 0;
	DD** Temp = (DD**)calloc(M, sizeof(DD*));
	FOR(i, M)
		Temp[i] = (DD*)calloc(Q, sizeof(DD));
	FOR(c, M)
	{
		FOR(d, Q)
		{
			FOR(k, P)
			{
				Sum += (Mat1[c][k] * Mat2[k][d]);
			}
			Temp[c][d] = Sum;
			Sum = 0;
		}
	}
	return Temp;
}

DD** Transpose(DD **Mat1, LL Rows, LL Columns)
{
	LL i, j;
	DD** Temp = (DD**)calloc(Columns, sizeof(DD*));
	for (i = 0; i<Columns; i++)
	{
		Temp[i] = (DD*)calloc(Rows, sizeof(DD));
	}
	for (i = 0; i<Rows; i++)
	{
		for (j = 0; j<Columns; j++)
		{
			Temp[j][i] = Mat1[i][j];
		}
	}
	return Temp;
}
#include "minimize.c"
Output_Primal primal_tsvm(MAT X, MAT Y, MAT w0, Opt opt);
double ** train_one_split(MAT Xnldr, double *Yl, long Yl_rows, long m0, Opt opt, long classes);

DD** LDS(DD **Xl, LL d0, LL m0, DD **Xu, LL d1, LL m1, DD *Yl, LL Yl_rows, LL classes, DD rho, Opt opt)
{
	/*
       --> Run the Low Density Separation algorithm as described in
       --> "Semi-supervised classification by Low Density Separation" by O. Chapelle and A. Zien
	   --> Xl:  d0 x m0 matrix with the labeled points
	   --> Xu:  d1 x m1 matrix with the unlabeled points
	   --> Yl:  column vector of length n containing the labels (+1 or -1 for binary)
	   --> rho: constant
	   --> opt: optional structure containing the (optional) fields,
	   -->	   C:       the soft margin parameter relative to 1/var^2
	   -->                  [default = 1]
	   -->     nofNn:   number of NN in the graph construction
	   -->               [default = 0, i.e. fully connected graph]
	   -->     sigma:   the width of the RBF kernel
	   -->     delta:   threshold
	   -->     nofIter: number of iterations for C* to reach C [default = 10]
	   -->     Cinit:   initial value for C* relative to C [default = 0.01]
	   -->     Cfinal:  final value of C* relative to C [default = 1]
	   -->     maxiter: maximum number of iterations in each gradient
	   -->              descent (multiplied by nb of variables) [default = 3]
	   -->     tolfun:  stopping criterion on the function value
	   -->              (relative to C) [default = 1e-5]
   */
	LL i, j, k, K;
	Param param;
	MAT D2, NN;
	opt.delta = 0.1;
	opt.nofIter = 10;
	opt.C = 0.390;
	opt.Cinit = 0.01;
	opt.Cfinal = 1;
	opt.nofNn = 100;
	opt.sigma = INFINITY;
	opt.opt_tb = 1;
	opt.maxiter = 3;
	opt.tolfun = exp(-5);
	if((1>opt.nofIter)|(opt.C<0)|((opt.Cinit<0)&&(opt.Cinit>opt.Cfinal))|(opt.sigma<0))error("Assertion error");
	LL m = m0 + m1;
	if((opt.nofNn<0)|(opt.nofNn>m))error("Assertion error");
	DD** mat = (DD**)calloc(m, sizeof(DD*));
	FOR(i, m)mat[i] = (DD*)calloc(d0, sizeof(DD));
	MAT X;
	X.matrix = mat; X.rows = m; X.columns = d0;
	FOR(i, X.rows)
	{
		FOR(j, X.columns)
		{
			if (i < m0)
			{
				X.matrix[i][j] = Xl[j][i];
			}
			else{
				X.matrix[i][j] = Xu[j][i - m0];
			}
		}
	}
	K = classes;
	//===================================================
	//    ****** Compute the full distance graph ******
	//===================================================

	output t = CalcNnDists(X.matrix, X.rows, X.columns, opt.nofNn);
	D2 = t.D2; NN = t.NN;
	param.pathNorm = rho;
	param.sigma = INFINITY;
	param.nofNn = opt.nofNn;
	DD* idx = (DD*)calloc(m, sizeof(DD));
	FOR(i, m)
	{
		idx[i] = i + 1;
	}
	MAT E2 = graphDistKernelC(X, D2, NN, param, idx, m);
    FOR(i,X.rows)free(X.matrix[i]);free(X.matrix);
    FOR(i,opt.nofNn){free(D2.matrix[i]);free(NN.matrix[i]);}free(D2.matrix);free(NN.matrix);
    D2.rows=0;D2.columns=0;NN.rows=0;NN.columns=0;
	if (opt.nofNn != 0)
	{
		DD** new_E2 = (DD**)calloc(E2.rows, sizeof(DD*));
		DD max1 = E2.matrix[0][0];
		FOR(i, E2.rows)
		{
			new_E2[i] = (DD*)calloc(E2.columns, sizeof(DD));
			FOR(j, E2.columns)
			{
				new_E2[i][j] = min(E2.matrix[i][j], E2.matrix[j][i]);
				if ((new_E2[i][j] > max1) && (!isinf(new_E2[i][j])))
				{
					max1 = new_E2[i][j];
				}
			}
		}
		FOR(i, E2.rows)free(E2.matrix[i]);free(E2.matrix);
		E2.matrix = new_E2;
		FOR(i, E2.rows)
		{
			FOR(j, E2.columns)
			{
				if (isinf(E2.matrix[i][j]))E2.matrix[i][j] = 2 * max1;
			}
		}
	}
	DD* E2_array = (DD*)calloc(E2.rows*E2.columns, sizeof(DD));
	bool isinf_E2 = false;
	FOR(i, E2.columns)
	{
		FOR(j, E2.rows)
		{
			if (isinf(E2.matrix[j][i]))isinf_E2 = true;
			E2_array[i*E2.rows + j] = E2.matrix[j][i];
		}
	}
	//===========================================================================
	//        ********* Compute the new kernel, do the MDS reduction **********
	//===========================================================================

	double defaultSigma = calcDefaultSigma(E2_array, 2, E2.columns*E2.rows);
	double sigma = opt.sigma * defaultSigma;
	Output_mds mds;
	if (isinf(sigma))
	{
		if (isinf_E2)
		{
			error("Cannot do the MDS: The graph is not connected");
		}
		mds = MDS(E2.matrix, E2.rows, E2.columns, opt.delta);
	}
	else
	{
		param.rbf = 1;// set RBF name equal to Gauss
		//=================================================
		//       ****** Calculate RBF Kernel *******
		//=================================================
		double **K = calcRbfKernel(E2.matrix, E2.rows, E2.columns, param.rbf, sigma, 1);
		FOR(i, E2.rows)
		{
			FOR(j, E2.columns)
			{
				K[i][j] = 2 - 2 * K[i][j];
			}
		}
		mds = MDS(K, E2.rows, E2.columns, opt.delta);
	}
	double **cumsum = 0, sum = 0;
	cumsum = (DD**)calloc(E2.rows, sizeof(DD*));
	FOR(i, E2.rows){
		cumsum[i] = (DD*)calloc(1, sizeof(DD));
		if (i == 0)cumsum[i][0] = mds.eigen[i];
		else cumsum[i][0] = mds.eigen[i] + cumsum[i - 1][0];
	}
	sum = cumsum[E2.rows - 1][0];
	LL nb_comp;
	if (opt.delta >= 0)
	{
		FOR(i, E2.rows)
		{
			if ((1 - cumsum[i][0] < opt.delta)&(mds.eigen[i] < opt.delta*mds.eigen[0]))
			{
				nb_comp = i;
				break;
			}
		}
	}
	else
	{
		nb_comp = E2.rows;
	}
	double **Xnldr = (DD**)calloc(mds.Y.rows, sizeof(DD*));
	double *mean_Xnldr = (DD*)calloc(nb_comp, sizeof(DD));
	FOR(i, mds.Y.rows)
	{
		Xnldr[i] = (DD*)calloc(nb_comp, sizeof(DD));
		// Keep only the first components
		FOR(j, nb_comp){
			Xnldr[i][j] = mds.Y.matrix[i][j];
			mean_Xnldr[j] += mds.Y.matrix[i][j];
			if (i == mds.Y.rows - 1){
				mean_Xnldr[j] = (mean_Xnldr[j])/ mds.Y.rows;
			}
		}
	}
	mds.Y.columns = nb_comp;
	mds.Y.matrix = Xnldr;
	double sum_var = 0;
	FOR(i, nb_comp)
	{
		double var = 0;
		FOR(j, mds.Y.rows)
		{
			var += pow(Xnldr[j][i] - mean_Xnldr[i], 2);
		}
		sum_var += var / (mds.Y.rows - 1);
	}
	// Default value of C = invert of the variance
	double defaultC = 1 / sum_var;
	double C = opt.C*defaultC; opt.Cinit = C*opt.Cinit; opt.Cfinal = C*opt.Cfinal; opt.C = C;
	opt.s = 3;
	//============================================
	//       ******* Train the TSVM ********
	//============================================
	double **Yu = train_one_split(mds.Y, Yl,Yl_rows,m0, opt, classes);
	return Yu;
}
double ** train_one_split(MAT Xnldr, double *Yl,long Yl_rows,long m0, Opt opt,long classes){
	MAT Y0,w0;
	w0.matrix = NULL;
	Y0.matrix = (DD**)calloc(Xnldr.rows, sizeof(DD*)); Y0.rows = Xnldr.rows; Y0.columns = 1;
	long i, j;
	if (classes == 2){
		if (Yl_rows > m0)
		{
			opt.Cinit = opt.Cfinal; opt.nofIter = 1;
		}
		else{
			FOR(i, Y0.rows)
			{
				Y0.matrix[i] = (DD*)calloc(1, sizeof(DD));
				if (i < Yl_rows)Y0.matrix[i][0] = Yl[i];
			}
			Output_Primal pr = primal_tsvm(Xnldr, Y0, w0, opt);
			int count=0;
            FOR(i,pr.Yu.rows)
			{
				double boo=(pr.Yu.matrix[i][0]>0)?1:-1;
                                if(boo==1){
                                 printf("%ld Kareena \n",i);
                                }
                                else{
                                 printf("%ld Amir \n",i);
                                 }
            }
            printf("\n total %d\n\n",count);
               return pr.Yu.matrix;
              }
	}
	else{
		MAT new_Y;
		DD final_obj=0;
		Output_Primal pr;
		new_Y.rows = Xnldr.rows - Yl_rows; new_Y.columns = classes;
		new_Y.matrix = (DD**)calloc(new_Y.rows, sizeof(DD*));
		FOR(i, new_Y.rows)new_Y.matrix[i] = (DD*)calloc(new_Y.columns, sizeof(DD));
		FOR(i, Y0.rows)
		{
			Y0.matrix[i] = (DD*)calloc(1, sizeof(DD));
		}
		FOR(i, classes)
		{
			FOR(j, Yl_rows)
			{
				if (Yl[j] == i+1)
					Y0.matrix[j][0] = 1;
				else Y0.matrix[j][0] = -1;
			}
			pr = primal_tsvm(Xnldr, Y0, w0, opt);
			FOR(j, new_Y.rows)new_Y.matrix[j][i] = pr.Yu.matrix[j][0];
			final_obj += pr.obj;
		}
        int count=0;
        FOR(i,new_Y.rows){
                        LL index=0;
                        FOR(j,new_Y.columns)
                        {
                          if(new_Y.matrix[i][j]>new_Y.matrix[i][index])index=j;
                        }
                        new_Y.matrix[i][0]=index+1;
                }
        FILE *fp;
        char *new_string;
        new_string=combine(outFile,"/../results.txt");
        fp=fopen(new_string,"w");
        FOR(i,new_Y.rows)
		{
			fprintf(fp,"%lf\n",new_Y.matrix[i][0]);
        }
        fclose(fp);
	}
			return NULL;
}
void SVD(double*** return_mat,MAT inp)
{
  // Calculate Singular Value Decomposition using Armadillo library
  long i ,j;
  fmat A=randu<fmat>(inp.columns,inp.rows);
  double **my_mat=(double**)calloc(inp.columns,sizeof(double*));
  for(i=0;i<inp.columns;i++)
  {
   my_mat[i]=(double*)calloc(inp.columns,sizeof(double));
   for(j=0;j<inp.rows;j++)A(i,j)=inp.matrix[j][i];
  }
  fmat U;
  fvec S;
  fmat V;
  svd(U, S, V, A);
  for(i=0;i<inp.columns;i++){
    for(j=0;j<inp.columns;j++)
    {
     my_mat[i][j]=U(i,j);
     }
  }
  *return_mat=my_mat;
}
Output_Primal primal_tsvm(MAT X, MAT Y, MAT w0, Opt opt)
{
	//======================================================
	//   **** Solve the TSVM problem in the primal****
	//======================================================

	MAT new_X;
	new_X.matrix = (double**)calloc(X.rows, sizeof(double*));
	new_X.rows = X.rows; new_X.columns = X.columns + 1;
	double  C, C2, exponent, cbal = 0, sum_vbal = 0, tolfun2;
	MAT Xu, R, vbal;
	vbal.matrix = (double**)calloc(1, sizeof(double*)); vbal.rows = 1; vbal.columns = X.rows;
	vbal.matrix[0] = (double*)calloc(vbal.columns, sizeof(double));
	long i, j, zeros = 0, n, maxiter2;
	for (i = 0; i < new_X.rows; i++)
	{
		vbal.matrix[0][i] = 1;
		new_X.matrix[i] = (double*)calloc(new_X.columns, sizeof(double));
		FOR(j, new_X.columns - 1)new_X.matrix[i][j] = X.matrix[i][j];
		new_X.matrix[i][new_X.columns - 1] = 1;
	}
	for (i = 0; i < Y.rows; i++){ if (Y.matrix[i][0] == 0)zeros++; }
	Xu.matrix = (double**)calloc(zeros, sizeof(double*));
	Xu.rows = zeros;
	Xu.columns = new_X.columns;
	for (i = 0; i < Xu.rows; i++)
	{
		Xu.matrix[i] = (double*)calloc(Xu.columns, sizeof(double));
	}
	zeros = 0;
	for (i = 0; i < Y.rows; i++)
	{
		if (Y.matrix[i][0] == 0)
		{
			for (j = 0; j < Xu.columns; j++)
			{
				Xu.matrix[zeros][j] = new_X.matrix[i][j];
			}
			zeros++;
		}
	}
	Xu.matrix = Transpose(Xu.matrix, Xu.rows, Xu.columns); i = Xu.rows; Xu.rows = Xu.columns; Xu.columns = i;
	exponent = pow(opt.Cfinal / opt.Cinit, 1 / (opt.nofIter + 1));
	C = opt.C;
	C2 = opt.Cinit;
	n = new_X.columns;
	if (zeros > 0)
	{
		for (i = 0; i < Y.rows; i++)
		{
			if (Y.matrix[i][0] != 0)
			{
				vbal.matrix[0][i] = 0;
			}
		}
	}
	vbal.matrix = Multiply_Matrices(vbal.matrix, new_X.matrix, vbal.rows, new_X.columns, vbal.columns); vbal.columns = new_X.columns;
	j = 0;
	for (i = 0; i < Y.rows; i++)
	{
		if (Y.matrix[i][0] != 0)
		{
			j += 1;
			cbal += Y.matrix[i][0];
		}
	}
	for (i = 0; i < vbal.columns; i++)sum_vbal += vbal.matrix[0][i];
	cbal = (cbal / j)*sum_vbal;
    R.rows = vbal.columns; R.columns = vbal.columns;
	SVD(&R.matrix,vbal);
	DD** newR = (double**)calloc(R.rows, sizeof(double*));
	for (i = 0; i < R.rows; i++)
	{
		newR[i] = (double*)calloc(1, sizeof(double));
		newR[i][0] = R.matrix[i][0];
	}
	MAT w;
	w.matrix = Multiply_Matrices(vbal.matrix, newR, 1, 1, R.rows);
	DD w1 = cbal / w.matrix[0][0];
	if (w0.matrix == NULL)
	{
		w.matrix = (double**)calloc(n - 1, sizeof(double*));
		for (i = 0; i < n - 1; i++)
		{
			w.matrix[i] = (double*)calloc(1, sizeof(double));
			for (j = 0; j < 1; j++)w.matrix[i][j] = 0;
		}
		w.rows = n - 1; w.columns = 1;
	}
	else
	{
		DD** new_newR = (double**)calloc(R.rows, sizeof(double*));
		for (i = 0; i < R.rows; i++)
		{
			new_newR[i] = (double*)calloc(R.columns - 1, sizeof(double));
			for (j = 0; j < R.columns - 1; j++)
			{
				new_newR[i][j] = R.matrix[i][j + 1];
			}
		}
		w.matrix = Multiply_Matrices(Transpose(new_newR, R.rows, R.columns - 1), w0.matrix, R.columns - 1, w0.rows, R.rows);
		w.rows = R.columns - 1; w.columns = w0.rows;
	}
	maxiter2 = opt.maxiter*new_X.rows;
	tolfun2 = opt.tolfun*C;
	Opt2 opt2;
	opt2.length = maxiter2;
	opt2.tolX = 0;
	opt2.tolFun = tolfun2;
	DD* y_temp = (DD*)calloc(Y.rows, sizeof(DD));
	FOR(i, Y.rows)
	{
		y_temp[i] = Y.matrix[i][0];
	}
	bool FLAG;
	Output_Obj  oo;
	FOR(i, opt.nofIter)
	{
		C2 = C2*exponent;
		Output_mini tt = minimize(w, w1, y_temp, Y.rows, C, C2, opt.s, new_X, Xu, R, opt2);
		oo = obj_fun(w, w1, y_temp, Y.rows, C, C2, opt.s,new_X,Xu,R);
		FLAG = (tt.FX < opt2.length) ? 1 : 0;
	}
	DD **Temp=(DD**)calloc(w.rows+1,sizeof(DD*));
	FOR(i, w.rows + 1)
	{
		Temp[i] = (DD*)calloc(1, sizeof(DD));
		if (i !=0)Temp[i][0] = w.matrix[i-1][0];
		else Temp[i][0] = w1;
	}
	w.matrix = Multiply_Matrices(R.matrix, Temp, R.rows, 1, w.rows + 1); w.rows = R.rows; w.columns = 1;
	MAT Yu;
	Yu.matrix = Multiply_Matrices(Transpose(Xu.matrix, Xu.rows, Xu.columns), w.matrix, Xu.columns, w.columns, w.rows); Yu.rows = Xu.columns; Yu.columns = w.columns;
	Output_Primal pr;
	pr.obj = oo.obj;
	pr.b = w.matrix[w.rows - 1][0];
	pr.Yu = Yu;
	free(w.matrix[w.rows - 1]);
	w.rows = w.rows - 1;
	pr.w = w;
	return pr;
}

//==============================
//      ******  MAIN *******
//==============================

int main(int argc,char * argv[]){
  long rows=atoi(argv[3]),columns=atoi(argv[2]),columns2=atoi(argv[1])-columns,i,j;
  FILE *fp;
  char *new_string;
  new_string=combine(argv[4],"/../data/final_file_train.txt");
  fp=fopen(new_string,"r");
  DD** k=(DD**)calloc(rows,sizeof(DD*));
  FOR(i,rows)
  {
	k[i]=(DD*)calloc(columns,sizeof(DD));
  }
  FOR(j,columns)
  {
	  FOR(i,rows)
	  {
	   fscanf(fp,"%lf ",&k[i][j]);
	  }
  }
  fclose(fp);
  new_string=combine(argv[4],"/../data/final_file_test.txt");
  fp=fopen(new_string,"r");
  DD** k2=(DD**)calloc(rows,sizeof(DD*));
  FOR(i,rows)
  {
	k2[i]=(DD*)calloc(columns2,sizeof(DD));
  }
  FOR(j,columns2)
  {
	  FOR(i,rows)
	  {
	     fscanf(fp,"%lf ",&k2[i][j]);
	  }
  }
  fclose(fp);
  DD* Yl=(DD*)calloc(columns,sizeof(DD*));
  FOR(i,columns)
  {
	  if(i<columns/2)
	     Yl[i]=1;
	  else
	     Yl[i]=2;
  }
  Opt opt;
  opt.delta = 0.1;
  opt.nofIter = 10;
  opt.C = 2;
  opt.Cinit = 0.01;
  opt.Cfinal = 1;
  opt.nofNn = 50;
  opt.sigma = INFINITY;
  opt.opt_tb = 1;
  opt.maxiter = 3;
  opt.tolfun = exp(-5);
  outFile=argv[4];
  DD** y=LDS(k,rows,columns,k2,rows,columns2,Yl,columns,3,0.5,opt);
  return 0;
}