#ifndef INCLUDE_MIMKL_SOLVERS_KOMD_HPP_
#define INCLUDE_MIMKL_SOLVERS_KOMD_HPP_

#include <Eigen/Core>
#include <dlib/optimization.h>
#include <iostream>
#include <mimkl/definitions.hpp>
#include <mimkl/linear_algebra.hpp>
#include <mimkl/solvers/solver.hpp>
#include <spdlog/fmt/ostr.h>
#include <spdlog/spdlog.h>

using dlib::mat;

namespace mimkl
{
namespace solvers
{

template <typename Scalar>
class KOMD : private Solver<Scalar>
{

    private:
    // logger instance
    static std::shared_ptr<spdlog::logger> _logger;
    // typedefs
    typedef MATRIX(Scalar) Matrix;
    typedef COLUMN(Scalar) Column;
    typedef Eigen::Map<const Column> MapColumn;
    // ensure Col-Major for dlib (default is row-major) as we use Eigen::Map<>
    // on
    // it.
    typedef dlib::matrix<Scalar,
                         0,
                         1,
                         dlib::default_memory_manager,
                         dlib::column_major_layout>
    DlibColumn; // runtime sized
                // column
                // vector.  -->
                // LibColumn a ;
                // a.set_size()
    // members
    // trade-off hard-svm vs centroids 0<=lambda<=1
    double _lambda;
    double _epsilon;
    bool _regularization_factor;
    Scalar _reg_factor = 1;
    dlib::solve_qp2_using_smo<DlibColumn> _solver;
    Matrix _K;
    //  Column _y; //boolian labels
    Matrix _Q;
    DlibColumn _gamma;
    void setup(const Column &);

    public:
    KOMD() {}
    //! Setup KOMD parameters prior to solving the QP.
    /*!
     \param K unweighted sum of (all multiple) kernels (or a single kernel)
     \param lambda trade-off parameter between hard SVM (default) vs L2
     regularization, 0<=lambda<=1.
     \param epsilon.
     */
    KOMD(const Matrix &,
         const double lambda = 0.,
         const double epsilon = 0.0001,
         const bool regularization_factor = false);

    //! Solve KOMD QP following setup.
    /*!
     \param y class labels, where y_i == 1 or y_i == -1.
     */
    void solve(const Column &);

    void set_parameters(const double, const double epsilon = 0.0001);
    void set_lambda(const double);
    void set_epsilon(const double);
    double get_lambda() const;
    double get_epsilon() const;

    // Get result from KOMD QP.
    MapColumn get_result() const;
};

template <typename Scalar>
std::shared_ptr<spdlog::logger>
KOMD<Scalar>::_logger = spdlog::stdout_color_mt("komd_console");

template <typename Scalar>
KOMD<Scalar>::KOMD(const Matrix &K,
                   const double lambda,
                   const double epsilon,
                   const bool regularization_factor)
{
    _K = K;
    _logger->debug("norm of K\n{}", K.norm());
    _epsilon = epsilon;
    _lambda = lambda;
    _regularization_factor = regularization_factor;
}

// implementation
template <typename Scalar>
void KOMD<Scalar>::setup(const Column &y)
{

    _Q = (1. - _lambda) * y.asDiagonal() * _K * y.asDiagonal(); // KLL
    _logger->debug("norm KLL:\n{}", _Q.norm());
    if (_regularization_factor)
    {
        Scalar nneg = (y.array() < 0.).count();
        Scalar npos = (y.array() > 0.).count();
        _reg_factor = nneg * npos / (nneg + npos);
        _Q.diagonal().array() += _reg_factor * _lambda; // LID
    }
    else
    {
        _Q.diagonal().array() += _lambda; // LID
    }
    _logger->debug("norm( KLL + LID):\n{}", _Q.norm());
    _Q *= 2.; // qp solves 0.5*Q
}

template <typename Scalar>
void KOMD<Scalar>::solve(const Column &y)
{
    setup(y);
    try
    {
        _solver(mat(_Q), mat(y), (double)2. / y.size(), _gamma,
                _epsilon); // FIXME wrong solver?
        _logger->trace("Q:\n{}\ny:\n{}\nnu:\n{}\ngamma "
                       "(dlib):\n{}\nepsilon:\n{}",
                       _Q, y, (double)2. / y.size(), _gamma, _epsilon);
    }
    catch (dlib::invalid_nu_error &exception)
    {
        DlibColumn dlib_y = mat(y);
        long positive_count = 0;
        long negative_count = 0;
        for (long r = 0; r < dlib_y.nr(); ++r)
        {
            if (dlib_y(r) == 1.0)
                ++positive_count;
            else if (dlib_y(r) == -1.0)
                ++negative_count;
        }
        if (positive_count == 1 || negative_count == 1)
        {
            _logger->error("y:\n{}", y);
            throw std::invalid_argument(
            "Each class in y should have more than one member");
        }
        else
            throw exception;
    }
    _logger->debug("sum over gamma: \n{}", dlib::sum(_gamma));
    _logger->debug("sum over directed gamma: \n{}",
                   dlib::sum(dlib::trans(mat(y)) * _gamma));
}

template <typename Scalar>
typename KOMD<Scalar>::MapColumn KOMD<Scalar>::get_result() const
{
    // Map Dlib to Eigen
    _logger->debug("sum over gamma (EigenMap): \n{}",
                   mimkl::linear_algebra::dlib_to_eigen<Scalar>(_gamma).sum());
    return mimkl::linear_algebra::dlib_to_eigen<Scalar>(_gamma);
}

template <typename Scalar>
void KOMD<Scalar>::set_parameters(const double lambda, const double epsilon)
{
    _epsilon = epsilon;
    _lambda = lambda;
}

template <typename Scalar>
double KOMD<Scalar>::get_lambda() const
{
    return _lambda;
}

template <typename Scalar>
void KOMD<Scalar>::set_lambda(const double lambda)
{
    _lambda = lambda;
}

template <typename Scalar>
double KOMD<Scalar>::get_epsilon() const
{
    return _epsilon;
}

template <typename Scalar>
void KOMD<Scalar>::set_epsilon(const double epsilon)
{
    _epsilon = epsilon;
}

} // namespace solvers
} // namespace mimkl

#endif /* #ifndef INCLUDE_MIMKL_SOLVERS_KOMD_HPP_ */


// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
// ** Copyright UCAR, CSU  (c) 1990 - 2018
// ** University Corporation for Atmospheric Research (UCAR)
// ** National Center for Atmospheric Research (NCAR)
// ** Colorado State University
// ** BSD licence applies
// ** DISCLAIMER: THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS
// ** OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
// ** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*

#include <iostream>
#include <string>
#include "Fractl.hh"
#include "Params.hh"
#include "Filters.hh"
#include "Interps.hh"

// Eigen linear algebra library
// http://eigen.tuxfamily.org/index.php?title=Main_Page
#include <Eigen/Dense>

double sumTimea = 0;
int cntTimeb = 0;
double sumTimeb = 0;
double sumTimec = 0;
double sumTimee = 0;
double sumTimef = 0;

bool Fractl::calcWinds()
{
  // One approach is to iterate:
  //   calculate V and U winds
  //   calculate W winds
  //   recalculate V and U winds using the new W winds
  //   recalculate W with the new V, U.
  //   etc.
  //
  // However, tests show that there is negligible gain
  // in accuracy after the first iteration.
  // So the loop limit is hardcoded at 1.

  for (int imain = 0; imain < 1; imain++) {
    // bool showStat = true;
    // Based on current w, calc u, v.
    if (gridType == Params::GRID_MISH)
      calcAllVUOnMish(
		numNbrMax,       // max num nearest nbrs
		pointVec,        // all observations
		radarKdTree,     // nearest nbr tree for pointVec
		cellMat);
    else
      calcAllVU(
		numNbrMax,       // max num nearest nbrs
		pointVec,        // all observations
		radarKdTree,     // nearest nbr tree for pointVec
		cellMat);

    printRunTime("calcAllVU", &timea);

    // Run low-pass filtering if requested

    Filter *filter = FilterFactory::createFilter(uvFilter);		// TOD indices
    if (filter != NULL) {
      filter->filter_U(cellMat, nradx, nrady, nradz, 1, NULL);
      filter->filter_V(cellMat, nradx, nrady, nradz, 1, NULL);
      delete filter;
    }

    // Run interpolation for missing data if requested
    // TODO: add old RadarWind interpolation and code Interp::interpolate

    Interp *interp = InterpFactory::createInterp(uvInterp);
    if (interp != NULL) {
      interp->interpolate_U(cellMat, nradx, nrady, nradz, std::numeric_limits<double>::quiet_NaN());
      interp->interpolate_V(cellMat, nradx, nrady, nradz, std::numeric_limits<double>::quiet_NaN());
      delete interp;
    }

    // NO LONGER USED:
    // Interpolate missing values from neighbors.
    //
    // The problem is that some cells don't have enough valid
    // neighbors to calculate their winds, so their values are
    // left as NaN.
    //
    // The idea was to fill the NaN cells
    // by interpolating the wind values from nearby cells.
    //
    // But what happens is that generally if a cell doesn't have
    // enough good neighbors for a valid wind calculation,
    // any neighbor cells we might use for interpolation are
    // pretty flakey.
    //xxx del interpolation?
    // Interpolate NaN values in cellMat.uu, cellMat.vv.
    //interpMissing( bugs, nradz, nrady, nradx, cellMat, tmpmat);
    //printRunTime("interpMissing for V,U", &timea);

    // Based on u, v, calc w

    if (gridType == Params::GRID_MISH)
      calcAllWOnMish(cellMat);
    else
      calcAllW(cellMat);

    printRunTime("calcAllW", &timea);

    // Run low-pass filtering on W if requested

    filter = FilterFactory::createFilter(wFilter);
    if (filter != NULL) {
      filter->filter_W(cellMat, nradx, nrady, nradz, 1, NULL);  // TODO indices
      delete filter;
    }

    // Calc deltas using verification data, if any.
    // Write outTxt file.
    checkVerif(imain, cellMat);
    printRunTime("checkVerif", &timea);
  } // for imain
  return true;
}

//======================================================================

// Calculate V and U winds at all cells in the z,y,x grid.

void Fractl::calcAllVU(
		       long numNbrMax,              // max num nearest nbrs
		       vector<Point *> *pointVec,   // all observations
		       KD_tree * radarKdTree,       // nearest nbr tree for pointVec
		       Cell ***& cellMat)           // we set Cell.uu, vv
{

  KD_real * centerLoc = new KD_real[ndim];
  long okCount = 0;

  for (long iz = 0; iz < nradz; iz++) {
    for (long iy = 0; iy < nrady; iy++) {
      for (long ix = 0; ix < nradx; ix++) {

        centerLoc[0] = zgridmin + iz * zgridinc;
        centerLoc[1] = ygridmin + iy * ygridinc;
        centerLoc[2] = xgridmin + ix * xgridinc;

        if (bugs >= Params::DEBUG_NORM) {
          cout << setprecision(5);
          cout << endl << "calcAllVU: iz: " << iz
	       << "  iy: " << iy
	       << "  ix: " << ix
	       << "  z: " << centerLoc[0]
	       << "  y: " << centerLoc[1]
	       << "  x: " << centerLoc[2]
	       << endl;
        }

        Cell * pcell = & cellMat[iz][iy][ix];
        calcCellVU(
		   centerLoc,             // query point
		   numNbrMax,             // max num nearest nbrs
		   pointVec,              // all observations
		   radarKdTree,           // nearest nbr tree for pointVec
		   pcell);                 // Cell.vv, uu are set.

        bool ok = false;
        if (isOkDouble( pcell->vv)
	    && isOkDouble( pcell->uu))
	  {
	    ok = true;
	    okCount++;
	  }

        if (bugs >= Params::DEBUG_VERBOSE) {
          cout << setprecision(7);
          cout << "calcAllVU: ok:" << ok
	       << "  iz: " << iz
	       << "  iy: " << iy
	       << "  ix: " << ix
	       << "  loc:"
	       << "  " << centerLoc[0]
	       << "  " << centerLoc[1]
	       << "  " << centerLoc[2]
	       << "  W: " << cellMat[iz][iy][ix].ww
	       << "  V: " << cellMat[iz][iy][ix].vv
	       << "  U: " << cellMat[iz][iy][ix].uu << endl;
        }

      } // for ix
    } // for iy
  } // for iz

  cout << "---------------- okCount: " << okCount << endl;

  cout << "sumTimea: " << sumTimea << endl;
  cout << "cntTimeb: " << cntTimeb << "  sumTimeb: " << sumTimeb << endl;
  cout << "sumTimec: " << sumTimec << endl;
  cout << "sumTimee: " << sumTimee << endl;
  cout << "sumTimef: " << sumTimef << endl;

  delete[] centerLoc;

} // end calcAllVU

//======================================================================

// Try to calc V and U on the Samurai Mish grid

void Fractl::calcAllVUOnMish(
			     long numNbrMax,              // max num nearest nbrs
			     vector<Point *> *pointVec,   // all observations
			     KD_tree * radarKdTree,       // nearest nbr tree for pointVec
			     Cell ***& cellMat)           // we set Cell.uu, vv
{

  KD_real * centerLoc = new KD_real[ndim];
  long okCount = 0;

  // for (long iz = 0; iz < nradz; iz++)
  //   for (long iy = 0; iy < nrady; iy++)
  //     for (long ix = 0; ix < nradx; ix++)

  //       centerLoc[0] = zgridmin + iz * zgridinc;
  //       centerLoc[1] = ygridmin + iy * ygridinc;
  //       centerLoc[2] = xgridmin + ix * xgridinc;

  // This is the way Samurai iterates on the Mish.
  // *Pos is the position in meter of the observation
  // i*   is the index of the position in node table

  for (int ii = -1; ii < (nradx); ii++) {
    for (int imu = -1; imu <= 1; imu += 2) {
      double iPos = xgridmin + xgridinc * (ii + (0.5 * sqrt(1. / 3.) * imu + 0.5));

      for (int ji = -1; ji < (nrady); ji++) {
	for (int jmu = -1; jmu <= 1; jmu += 2) {
	  double jPos = ygridmin + ygridinc * (ji + (0.5 * sqrt(1. / 3.) * jmu + 0.5));

	  for (int ki = -1; ki < (nradz); ki++) {
	    for (int kmu = -1; kmu <= 1; kmu += 2) {
	      double kPos = zgridmin + zgridinc * (ki + (0.5 * sqrt(1. / 3.) * kmu + 0.5));

	      int ix = (ii + 1) * 2 + (imu + 1) / 2;
	      int iy = (ji + 1) * 2 + (jmu + 1) / 2;
	      int iz = (ki + 1) * 2 + (kmu + 1) / 2;

	      // centerLoc[0] = zgridmin + iz * zgridinc;
	      // centerLoc[1] = ygridmin + iy * ygridinc;
	      // centerLoc[2] = xgridmin + ix * xgridinc;

	      centerLoc[0] = kPos;
	      centerLoc[1] = jPos;
	      centerLoc[2] = iPos;

	      if (bugs >= Params::DEBUG_NORM) {
		std::cout << setprecision(5);
		std::cout << endl << "calcAllVU: iz: " << iz
			  << "  iy: " << iy
			  << "  ix: " << ix
			  << "  z: " << centerLoc[0]
			  << "  y: " << centerLoc[1]
			  << "  x: " << centerLoc[2]
			  << endl;
	      }

	      Cell * pcell = & cellMat[iz][iy][ix];
	      calcCellVU(centerLoc,             // query point
			 numNbrMax,             // max num nearest nbrs
			 pointVec,              // all observations
			 radarKdTree,           // nearest nbr tree for pointVec
			 pcell);                 // Cell.vv, uu are set.

	      bool ok = false;
	      if (isOkDouble( pcell->vv) && isOkDouble( pcell->uu)) {
		ok = true;
		okCount++;
	      }

	      if (bugs >= Params::DEBUG_VERBOSE) {
		cout << setprecision(7);
		cout << "calcAllVU: ok:" << ok
		     << "  iz: " << iz
		     << "  iy: " << iy
		     << "  ix: " << ix
		     << "  loc:"
		     << "  " << centerLoc[0]
		     << "  " << centerLoc[1]
		     << "  " << centerLoc[2]
		     << "  W: " << cellMat[iz][iy][ix].ww
		     << "  V: " << cellMat[iz][iy][ix].vv
		     << "  U: " << cellMat[iz][iy][ix].uu << endl;
	      }
	    }
	  } // ki
	}
      } // ji
    }
  }  // ii

  cout << "---------------- okCount: " << okCount << endl;

  cout << "sumTimea: " << sumTimea << endl;
  cout << "cntTimeb: " << cntTimeb << "  sumTimeb: " << sumTimeb << endl;
  cout << "sumTimec: " << sumTimec << endl;
  cout << "sumTimee: " << sumTimee << endl;
  cout << "sumTimef: " << sumTimef << endl;

  delete[] centerLoc;

} // end calcAllVUOnMish

//======================================================================

// Calculate V and U winds for a single location.
//
// At a given location centerLoc, find the nearest nbrs
// in pointVec, and use the radial velocities to calculate
// the winds V, U, (not W).
//
// Let vx, vy, vz be estimates of the wind velocity,
// and vr be the radial velocity.
//
// For each radar m, we use the linear model:
//    vr_est[m] = cos(theta) cos(elev) vx
//      + sin(theta) cos(elev) vy
//      + sin(elev) vz
//    vr_est[m] = conx vx + cony * vy + conz * vz
//
// Let E[m] = error = vr_true[m] - vr_est[m]
//
// We want to minimize Q = sum_m E[m]^2
//
//
// Let
//   sumxx = sum(conx * conx)
//   sumxr = sum(conx * vr)
//   etc.
//
// Normal equations, derived from
// 0 = d(Q)/d(vx), 0 = d(Q)/d(vy), 0 = d(Q)/d(vz),
// give:
// vx sumxx + vy sumxy + vz sumxz = sumxr
// vx sumxy + vy sumyy + vz sumyz = sumyr
// vx sumxz + vy sumyz + vz sumzz = sumzr
//
// Or for 2 dim,
// vx sumxx + vy sumxy = sumxr - vz sumxz
// vx sumxy + vy sumyy = sumyr - vz sumyz
//
// Using Cramer's rule on the 2-dim case:
// Let demom = sumxx sumyy - sumxy^2
// vx_hat = ( (sumxr - vz sumxz) sumyy - (sumyr - vz sumyz) sumxy ) / denom
// vy_hat = ( (sumyr - vz sumyz) sumxx - (sumxr - vz sumxz) sumxy ) / denom

void Fractl::calcCellVU(
			KD_real * centerLoc,           // query point: z, y, x
			long numNbrMax,                // max num nearest nbrs
			vector<Point *> *pointVec,     // all observations
			KD_tree * radarKdTree,         // nearest nbr tree for pointVec
			Cell * pcell)                  // we fill vv, uu.
{
  struct timeval timea;
  addDeltaTime( &timea, NULL);

  pcell->uu = numeric_limits<double>::quiet_NaN();
  pcell->vv = numeric_limits<double>::quiet_NaN();

  bool showDetail = testDetail(
			       centerLoc[0],         // z
			       centerLoc[1],         // y
			       centerLoc[2]);        // x

  if (bugs >= Params::DEBUG_EXTRA && showDetail) {
    cout << setprecision(7);
    cout << "calcCellVU.entry: showDetail:" << endl;
    cout << "    centerLoc: z: " << centerLoc[0] << endl;
    cout << "    centerLoc: y: " << centerLoc[1] << endl;
    cout << "    centerLoc: x: " << centerLoc[2] << endl;
    cout << "    ww: " << pcell->ww << endl;
    cout << "    vv: " << pcell->vv << endl;
    cout << "    uu: " << pcell->uu << endl;
    cout << "    meanNbrDbz: " << pcell->meanNbrDbz << endl;
    cout << "    meanNbrNcp: " << pcell->meanNbrNcp << endl;
    cout << "    meanNbrElevDeg: " << pcell->meanNbrElevDeg << endl;
    cout << "    meanNbrKeepDist: " << pcell->meanNbrKeepDist << endl;
    cout << "    meanNbrOmitDist: " << pcell->meanNbrOmitDist << endl;
    cout << "    condNum: " << pcell->conditionNumber << endl;
  }

  if (numNbrMax == 0) throwerr("cell numNbrMax == 0");

  int nbrIxs[numNbrMax];
  KD_real nbrDistSqs[numNbrMax];              // nbr dist^2
  for (long inbr = 0; inbr < numNbrMax; inbr++) {
    nbrIxs[inbr] = -1;
  }

  Statistic nbrDbzStat;
  Statistic nbrNcpStat;
  Statistic nbrElevDegStat;
  Statistic nbrKeepDistStat;
  Statistic nbrOmitDistStat;

  Point** nearPts = new Point *[numNbrMax];

  addDeltaTime( &timea, &sumTimea);
  struct timeval timeb;
  addDeltaTime( &timeb, NULL);

  // Find nearest nbrs
  radarKdTree->nnquery(
		       centerLoc,        // query point
		       numNbrMax,        // desired num nearest nbrs
		       KD_EUCLIDEAN,     // Metric
		       1,                // MinkP
		       nbrIxs,           // out: parallel array, indices of nearest nbrs
		       nbrDistSqs);      // out: parallel array, squares of distances of nbrs

  cntTimeb++;
  addDeltaTime( &timeb, &sumTimeb);
  struct timeval timec;
  addDeltaTime( &timec, NULL);

  if (bugs >= Params::DEBUG_VERBOSE && showDetail) {
    cout << setprecision(7);
    cout << "  calcCellVU: showDetail:  nearPts for centerLoc: z: "
	 << centerLoc[0] << "  y: " << centerLoc[1]
	 << "  x: " << centerLoc[2] << endl;
  }

  int numNbrActual = 0;

  // Keep points within base + factor * aircraft dist

  for (long inbr = 0; inbr < numNbrMax; inbr++) {
    if (nbrIxs[inbr] < 0) throwerr("nbrIxs < 0");

    Point * nearPt = pointVec->at( nbrIxs[inbr]);
    double aircraftDist = calcDistPtAircraft( nearPt);
    double localDist = calcDistLocPt( centerLoc, nearPt);
    double maxDist = maxDistBase + maxDistFactor * aircraftDist;

    // Use local distance constraint from grid point center
    double roi = sqrt(xgridinc * xgridinc + ygridinc * ygridinc + zgridinc * zgridinc);
    if (roi < maxDist) maxDist = roi;

    const char * msg;

    if (localDist < maxDist) {
      nbrDbzStat.addOb( nearPt->dbz);
      nbrNcpStat.addOb( nearPt->ncp);
      nbrElevDegStat.addOb( nearPt->elevRad * 180 / M_PI);
      nbrKeepDistStat.addOb( localDist);
      nearPts[numNbrActual++] = nearPt;
      msg = "KEEP";
    } // if localDist < maxDist

    else {
      nbrOmitDistStat.addOb( localDist);
      msg = "OMIT";
    }

    if (bugs >= Params::DEBUG_VERBOSE && showDetail) {
      cout << setprecision(5);
      cout << "    " << msg
	   << "  inbr: " << inbr
	   << "  dist: " << localDist
	   << "  pt:"
	   << "  coordz: " << nearPt->coordz
	   << "  coordy: " << nearPt->coordy
	   << "  coordx: " << nearPt->coordx
	   << "  vg: " << nearPt->vg
	   << endl;
    }

  } // for inbr

  if (bugs >= Params::DEBUG_EXTRA && showDetail) {
    cout << "  calcCellVU: showDetail: cell z: "
	 << centerLoc[0] << "  y: " << centerLoc[1]
	 << "  x: " << centerLoc[2] << "  numNbrActual: " << numNbrActual << endl;
  }

  if (numNbrActual >= 2) {    // if numNbrActual is ok

    pcell->meanNbrDbz = nbrDbzStat.dsum / nbrDbzStat.numGood;
    pcell->meanNbrNcp = nbrNcpStat.dsum / nbrNcpStat.numGood;
    pcell->meanNbrElevDeg = nbrElevDegStat.dsum / nbrElevDegStat.numGood;
    pcell->meanNbrKeepDist = nbrKeepDistStat.dsum / nbrKeepDistStat.numGood;
    pcell->meanNbrOmitDist = nbrOmitDistStat.dsum / nbrOmitDistStat.numGood;

    if (bugs >= Params::DEBUG_EXTRA && showDetail) {
      for (long inbr = 0; inbr < numNbrActual; inbr++) {
	Point * pt = nearPts[inbr];
	cout << setprecision(7);
	cout << "  calcCellVU: showDetail: nbr: inbr: " << inbr
	     << "  vg: " << pt->vg
	     << "  dbz: " << pt->dbz
	     << "  ncp: " << pt->ncp
	     << "  theta deg: " << (pt->thetaRad * 180 / M_PI)
	     << "  elev deg: " << (pt->elevRad * 180 / M_PI)
	     << setprecision(15)
	     << "  deltaTime: "
	     << (pt->rayTime - nearPts[0]->rayTime)
	     << endl;
      }
      for (long inbr = 0; inbr < numNbrActual; inbr++) {
	Point * pt = nearPts[inbr];
	cout << "  showDetail.from.aircraft.to.nbr: set arrow "
	     << (inbr + 1)
	     << " from " << pt->aircraftx << "," << pt->aircrafty
	     << " to " << pt->coordx << "," << pt->coordy
	     << endl;
      }
    } // if showDetail

    addDeltaTime( &timec, &sumTimec);
    struct timeval timee;
    addDeltaTime( &timee, NULL);

    // 3d solve
    //    vr = cos(theta) cos(elev) vx
    //      + sin(theta) cos(elev) vy
    //      + sin(elev) vz

    // 2d solve
    //    vr - sin(elev) vz = cos(theta) cos(elev) vx
    //      + sin(theta) cos(elev) vy

    // Find wwind = W wind estimate near the pt.
    double wwind = pcell->ww;
    bool isCellOk = false;

    if (useEigen) {

      Eigen::MatrixXd amat( numNbrActual, 2);
      Eigen::VectorXd bvec( numNbrActual);

      for (long inbr = 0; inbr < numNbrActual; inbr++) {
	Point * pt = nearPts[inbr];
	amat( inbr, 0) = cos( pt->thetaRad) * cos( pt->elevRad);
	amat( inbr, 1) = sin( pt->thetaRad) * cos( pt->elevRad);

	// Find wwind = W wind estimate near the pt.
	double wwind = pcell->ww;

	bvec( inbr) = pt->vg - wwind * sin( pt->elevRad);
      }
      if (bugs >= Params::DEBUG_EXTRA && showDetail) {
	cout << "\n  calcCellVU: eigen: amat:\n" << amat << endl;
	cout << "\n  calcCellVU: eigen: bvec:\n" << bvec << endl;
      }

      Eigen::JacobiSVD<Eigen::MatrixXd> svd(
					    amat, Eigen::ComputeThinU | Eigen::ComputeThinV);
      Eigen::VectorXd singVals = svd.singularValues();
      Eigen::MatrixXd thinV = svd.matrixV();
      long slen = singVals.size();
      // long vlen = thinV.rows();

      pcell->conditionNumber = numeric_limits<double>::infinity();
      if (slen > 0 && singVals[slen-1] != 0)
	pcell->conditionNumber = singVals[0] / singVals[slen-1];

      if (bugs >= Params::DEBUG_EXTRA && showDetail) {
	cout << "\n  calcCellVU: eigen: singVals:\n" << singVals << endl;
	cout << "\n  calcCellVU: num nonzeroSingularValues:\n"
	     << svd.nonzeroSingularValues() << endl;
	cout << "  calcCellVU: conditionNumber: "
	     << pcell->conditionNumber << endl;
      }

      // Using a conditionNumberCutoff > 10 causes some cells
      // to have extreme values for U and V.

      if (pcell->conditionNumber < conditionNumberCutoff)
	isCellOk = true;
      if (isCellOk) {
	Eigen::VectorXd xvec = svd.solve( bvec);
	Eigen::VectorXd errvec = amat * xvec - bvec;
	// double maxAbsErr = errvec.array().abs().maxCoeff();
	// double meanSqErr = errvec.dot( errvec) / numNbrActual;   // dot product

	pcell->vv = xvec(1);       // v
	pcell->uu = xvec(0);       // u

	double ustd = 0;
	double vstd = 0;

	for (long s = 0; s < slen; s++) {
	  // double thin0 = TODO check if the calls to thinV() has side effects
	  thinV(0, s);
	  // double thin1 =
	  thinV(1, s);
	  // double sing =
	  singVals[s];
	  ustd += pow( (thinV(s, 0) / singVals[s]) , 2.0);
	  vstd += pow( (thinV(s, 1) / singVals[s]) , 2.0);
	}
	pcell->ustd = sqrt(ustd);
	pcell->vstd = sqrt(vstd);

        if (fabs(pcell->vv) > maxV) {
          pcell->vv = numeric_limits<double>::quiet_NaN();
	  pcell->vstd = numeric_limits<double>::quiet_NaN();
	}
        if (fabs(pcell->uu) > maxU) {
          pcell->uu = numeric_limits<double>::quiet_NaN();
	  pcell->ustd = numeric_limits<double>::quiet_NaN();
	}

      } // if isCellOk
    } // if useEigen

    // Else use cramer's method
    else {
      double sxx = 0;
      double sxy = 0;
      double syy = 0;
      double sxz = 0;
      double syz = 0;

      for (long inbr = 0; inbr < numNbrActual; inbr++) {
	Point * pt = nearPts[inbr];
	double xval = cos( pt->thetaRad) * cos( pt->elevRad);
	double yval = sin( pt->thetaRad) * cos( pt->elevRad);
	double zval = pt->vg - wwind * sin( pt->elevRad);

	sxx += xval * xval;
	sxy += xval * yval;
	syy += yval * yval;
	sxz += xval * zval;
	syz += yval * zval;
      }
      double detbase = sxx*syy - sxy*sxy;
      double det1    = sxz*syy - syz*sxy;
      double det2    = sxx*syz - sxy*sxz;
      if (bugs >= Params::DEBUG_EXTRA && showDetail) {
	cout << "  calcCellVU: cramer: showDetail: "
	     << "  sxx: " << sxx
	     << "  sxy: " << sxy
	     << "  syy: " << syy
	     << "  sxz: " << sxz
	     << "  syz: " << syz << endl;
	cout << "  calcCellVU: cramer: showDetail: "
	     << "  detbase: " << detbase
	     << "  det1: " << det1
	     << "  det2: " << det2 << endl;
      }

      // Using a detCutoff <= 0.01 causes some cells
      // to have extreme values for U and V.
      // A detCutoff of 0.1 gives roughly similar results to using
      // a conditionNumber cutoff of 10.
      double detCutoff = 0.1;    // xxxxxxxxx

      if (fabs(detbase) > detCutoff) {
	pcell->uu = det1 / detbase;
	pcell->vv = det2 / detbase;
      }
    }

    addDeltaTime( &timee, &sumTimee);
    delete[] nearPts;
  } // else numNbrActual is ok

  struct timeval timef;
  addDeltaTime( &timef, NULL);

  if (bugs >= Params::DEBUG_EXTRA && showDetail) {
    cout << setprecision(7);
    cout << "calcCellVU.exit: showDetail:" << endl;
    cout << "    centerLoc: z: " << centerLoc[0] << endl;
    cout << "    centerLoc: y: " << centerLoc[1] << endl;
    cout << "    centerLoc: x: " << centerLoc[2] << endl;
    cout << "    ww: " << pcell->ww << endl;
    cout << "    vv: " << pcell->vv << endl;
    cout << "    uu: " << pcell->uu << endl;
    cout << "    meanNbrDbz: " << pcell->meanNbrDbz << endl;
    cout << "    meanNbrNcp: " << pcell->meanNbrNcp << endl;
    cout << "    meanNbrElevDeg: " << pcell->meanNbrElevDeg << endl;
    cout << "    meanNbrKeepDist: " << pcell->meanNbrKeepDist << endl;
    cout << "    meanNbrOmitDist: " << pcell->meanNbrOmitDist << endl;
    cout << "    condNum: " << pcell->conditionNumber << endl;
  }

} // end calcCellVU

// xxx all alloc: delete

//======================================================================

// Calculate the W (vertical) winds for all cells in the z,y,x grid.
//
// Consider the vertical column of cells for some
// given horizontal coordinates iy, ix.
// By the mass continuity equation, the total volume of air coming
// into the column must equal the total volume of air leaving the column.
//
// 0 = totalFlow = sum_iz (density_iz * sideFlow_iz) + endFlow
// where
//   density_iz = the density at level iz
//   sideFlow_iz = flow through the sides of the cell at level iz
//   endFlow = flow in the bottom and out the top end of the column
//           = densityBot * bottomFlow - densityTop * topFlow
//
// We assume the end flows are 0, so
//   bottendFlow = 0
//   topFlow = 0
//   endFlow = 0
//
// sideFlow_iz = uFlow_iz + vFlow_iz
// uFlow_iz = (flow in from cell ix-1) - (flow out to cell ix+1)
// uFlow_iz = 0.5 * (u[ix-1] + u[ix]) - 0.5 * (u[ix] + u[ix+1])
//          = 0.5 * (u[ix-1] - u[ix+1])
//
// sideFlow_iz = 0.5 * (u[ix-1] - u[ix+1] + v[ix-1] - v[ix+1])
//
// 0 = totalFlow = sum_iz (density_iz * 0.5 * (
//       v[iy-1] - v[iy+1]
//     + u[ix-1] - u[ix+1]))
//
// But in real life the totalFlow sum is not 0.
//
// Now we ask what modifications to the U and V values
// would make the totalFlow 0.
// We want to modify those U,V values with larger elevation angles more.
// xxx future: handle geometric uncertainty values too.
//
// At each layer, we have 4 terms to adjust: 2 U terms and 2 V terms.
// Let us subtract h[iz] from each term on level iz.
//
// Find h[iz] such that:
//   0 = sum_iz (density_iz * 0.5 * (
//         (v[iy-1]-h[iz]) - (v[iy+1]+h[iz])
//       + (u[ix-1]-h[iz]) - (u[ix+1])+h[iz]))
//
//     = totalFlow - sum_iz (density_iz * 0.5 * 4 * h[iz])
//
//   totalFlow = 2 * sum_iz (density_iz * h[iz])
//
// Let h[iz] be weighted by the elevation angle, so
//   h[iz] = H * wgt[iz]
//   wgt[iz] = cos(elevationAngle)
//
//   totalFlow = 2 * sum_iz (density[iz] * H * wgt[iz])
//   H = totalFlow / (2 * sum_iz (density[iz] * wgt[iz]))

void Fractl::calcAllWOnMish(
		      Cell ***& cellMat)           // We set Cell.ww
{
  long maxx = (xgridmax - xgridmin) * 2 / xgridinc + 1;
  long maxy = (ygridmax - ygridmin) * 2 / ygridinc + 1;
  long maxz = (zgridmax - zgridmin) * 2 / zgridinc + 1;

  double * density = new double[maxz + 3];	 // range is 0..maxz
  double * wgts = new double[maxz + 3];

  // double * density = new double[nradz];
  // double * wgts = new double[nradz];

  //  for (long iz = 0; iz < nradz; iz++)
  //    density[iz] = calcDensity( zgridmin + iz * zgridinc);

  // Use samurai index computation since we need both position and index

  for (int ki = -1; ki < (nradz); ki++) {
    for (int kmu = -1; kmu <= 1; kmu += 2) {
      double kPos = zgridmin + zgridinc * (ki + (0.5 * sqrt(1. / 3.) * kmu + 0.5));
      int ik = (ki + 1) * 2 + (kmu + 1) / 2;
      density[ik] = calcDensity(kPos);
    }
  }

  // Omit the edges of the region as we use ix-1, ix+1, iy-1, iy+1.

  // for (long iy = 1; iy < nrady - 1; iy++)
  //   for (long ix = 1; ix < nradx - 1; ix++)

  for (long iy = 1; iy < maxy - 1; iy++) {
    for (long ix = 1; ix < maxx - 1; ix++) {

      // for (long iz = 0; iz < nradz; iz++)
      for (long iz = 0; iz < maxz; iz++) {
        // xxx Future:
        // Find c = the nearest cell to this one
        // through which the aircraft flew.
        // Let cosElev = horizDistToC / slantDistToC
        // wgt = cosElev

        wgts[iz] = 1.0;
      }

      // Calc totalFlow = sum of everything flowing into the column,
      // not counting the top or bottom faces.

      double totalFlow = 0;
      double sumWgt = 0;

      // for (long iz = 0; iz < nradz; iz++)
      for (long iz = 0; iz < maxz; iz++) {

        if ( isOkDouble( cellMat[iz][iy][ix-1].uu)
	     && isOkDouble( cellMat[iz][iy][ix+1].uu)
	     && isOkDouble( cellMat[iz][iy-1][ix].vv)
	     && isOkDouble( cellMat[iz][iy+1][ix].vv))
	  {
	    totalFlow += density[iz] * 0.5
	      * ( cellMat[iz][iy][ix-1].uu - cellMat[iz][iy][ix+1].uu
		  +  cellMat[iz][iy-1][ix].vv - cellMat[iz][iy+1][ix].vv);
	    sumWgt += density[iz] * wgts[iz];
	  }
      } // for iz

      double hcon;
      if (fabs(sumWgt) < epsilon) hcon = 0;
      else hcon = totalFlow / (2 * sumWgt);

      // Calc W wind = totalFlow, starting at the bottom,
      // using the modified U, V winds.
      double wwind = baseW;
      // for (long iz = 0; iz < nradz; iz++)
      for (long iz = 0; iz < maxz; iz++) {
        if ( isOkDouble( cellMat[iz][iy][ix-1].uu)
	     && isOkDouble( cellMat[iz][iy][ix+1].uu)
	     && isOkDouble( cellMat[iz][iy-1][ix].vv)
	     && isOkDouble( cellMat[iz][iy+1][ix].vv))
	  {
	    wwind += density[iz] * 0.5
	      * (  cellMat[iz][iy][ix-1].uu - cellMat[iz][iy][ix+1].uu
		   + cellMat[iz][iy-1][ix].vv - cellMat[iz][iy+1][ix].vv
		   - 4 * hcon * wgts[iz]);
	    if (! isOkDouble( wwind))
	      throwerr("calcAllW: invalid w wind");
	    if (fabs(wwind) < maxW) {
	      cellMat[iz][iy][ix].ww = wwind;
	    } else cellMat[iz][iy][ix].ww = numeric_limits<double>::quiet_NaN();
	  } else cellMat[iz][iy][ix].ww = numeric_limits<double>::quiet_NaN();
      } // for iz
      if (fabs(wwind - baseW) > epsilon) {
        cout << setprecision(15);
        cout << "calcAllW: iy: " << iy << "  ix: " << ix
	     << "  baseW: " << baseW << "  wwind: " << wwind << endl;
        cout.flush();
        throwerr("wwind error");
      }
    } // for ix
  } // for iy

  //xxx maybe omit:
  // We only calculated the inner points for iy, ix.
  // But we also will need the edge values when
  // we calc the next iteration of U, V values.
  // So set the edges from the nearest interior points.
  // May be NaN.

  // for (long iz = 0; iz < nradz; iz++)
  for (long iz = 0; iz < maxz; iz++) {
    // Corners
    cellMat[iz][0][0].ww             = cellMat[iz][1][1].ww;
    cellMat[iz][0][maxx-1].ww       = cellMat[iz][1][maxx-2].ww;
    cellMat[iz][maxy-1][0].ww       = cellMat[iz][maxy-2][1].ww;
    cellMat[iz][maxy-1][maxx-1].ww = cellMat[iz][maxy-2][maxx-2].ww;

    // Side edges
    // for (long iy = 1; iy < nrady - 1; iy++)
    for (long iy = 1; iy < maxy - 1; iy++) {
      cellMat[iz][iy][0].ww       = cellMat[iz][iy][1].ww;
      cellMat[iz][iy][maxx-1].ww = cellMat[iz][iy][maxx-2].ww;
    }
    // Top and bottom edges
    // for (long ix = 1; ix < nradx - 1; ix++)
    for (long ix = 1; ix < maxx - 1; ix++) {
      cellMat[iz][0][ix].ww       = cellMat[iz][1][ix].ww;
      cellMat[iz][maxy-1][ix].ww = cellMat[iz][maxy-2][ix].ww;
    }
  } // for iz

  delete[] density;
  delete[] wgts;

} // end calcAllWOnMish

//==================================================================

void Fractl::calcAllW(
		      Cell ***& cellMat)           // We set Cell.ww
{
  double * density = new double[nradz];
  double * wgts = new double[nradz];
  for (long iz = 0; iz < nradz; iz++)
    density[iz] = calcDensity( zgridmin + iz * zgridinc);

  // Omit the edges of the region as we use ix-1, ix+1, iy-1, iy+1.
  for (long iy = 1; iy < nrady - 1; iy++) {
    for (long ix = 1; ix < nradx - 1; ix++) {

      for (long iz = 0; iz < nradz; iz++) {
        // xxx Future:
        // Find c = the nearest cell to this one
        // through which the aircraft flew.
        // Let cosElev = horizDistToC / slantDistToC
        // wgt = cosElev

        wgts[iz] = 1.0;
      }

      // Calc totalFlow = sum of everything flowing into the column,
      // not counting the top or bottom faces.

      double totalFlow = 0;
      double sumWgt = 0;

      for (long iz = 0; iz < nradz; iz++) {
        if ( isOkDouble( cellMat[iz][iy][ix-1].uu)
	     && isOkDouble( cellMat[iz][iy][ix+1].uu)
	     && isOkDouble( cellMat[iz][iy-1][ix].vv)
	     && isOkDouble( cellMat[iz][iy+1][ix].vv))
	  {
	    totalFlow += density[iz] * 0.5
	      * ( cellMat[iz][iy][ix-1].uu - cellMat[iz][iy][ix+1].uu
		  +  cellMat[iz][iy-1][ix].vv - cellMat[iz][iy+1][ix].vv);
	    sumWgt += density[iz] * wgts[iz];

	    bool showDetail = testDetail(
					 zgridmin + iz * zgridinc,      // z
					 ygridmin + iy * ygridinc,      // y
					 xgridmin + ix * xgridinc);      // x
	    // detailSpec);                   // z, y, x, delta

	    if (showDetail) {
	      cout << setprecision(7);
	      cout << "calcAllW: showDetail:" << endl
		   << "    iz: " << iz << endl
		   << "    iy: " << iy << endl
		   << "    ix: " << ix << endl
		   << "    den: " << density[iz] << endl
		   << "    wgt: " << wgts[iz] << endl
		   << "    U-: " << cellMat[iz][iy][ix-1].uu << endl
		   << "    U+: " << cellMat[iz][iy][ix+1].uu << endl
		   << "    V-: " << cellMat[iz][iy-1][ix].vv << endl
		   << "    V+: " << cellMat[iz][iy+1][ix].vv << endl;
	    }
	  }
      } // for iz

      double hcon;
      if (fabs(sumWgt) < epsilon) hcon = 0;
      else hcon = totalFlow / (2 * sumWgt);

      // Calc W wind = totalFlow, starting at the bottom,
      // using the modified U, V winds.
      double wwind = baseW;
      for (long iz = 0; iz < nradz; iz++) {
        if ( isOkDouble( cellMat[iz][iy][ix-1].uu)
	     && isOkDouble( cellMat[iz][iy][ix+1].uu)
	     && isOkDouble( cellMat[iz][iy-1][ix].vv)
	     && isOkDouble( cellMat[iz][iy+1][ix].vv))
	  {
	    wwind += density[iz] * 0.5
	      * (  cellMat[iz][iy][ix-1].uu - cellMat[iz][iy][ix+1].uu
		   + cellMat[iz][iy-1][ix].vv - cellMat[iz][iy+1][ix].vv
		   - 4 * hcon * wgts[iz]);
	    if (! isOkDouble( wwind))
	      throwerr("calcAllW: invalid w wind");
            if (fabs(wwind) < maxW) {
              cellMat[iz][iy][ix].ww = wwind;
            } else cellMat[iz][iy][ix].ww = numeric_limits<double>::quiet_NaN();

	    bool showDetail = testDetail(
					 zgridmin + iz * zgridinc,      // z
					 ygridmin + iy * ygridinc,      // y
					 xgridmin + ix * xgridinc);      // x

	    if (showDetail) {
	      cout << setprecision(7);
	      cout << "calcAllW: showDetail:" << endl
		   << "  iz: " << iz << endl
		   << "  iy: " << iy << endl
		   << "  ix: " << ix << endl
		   << "  den: " << density[iz] << endl
		   << "  U-: " << cellMat[iz][iy][ix-1].uu << endl
		   << "  U+: " << cellMat[iz][iy][ix+1].uu << endl
		   << "  V-: " << cellMat[iz][iy-1][ix].vv << endl
		   << "  V+: " << cellMat[iz][iy+1][ix].vv << endl
		   << "  hcon: " << hcon << endl
		   << "  wgt: " << wgts[iz] << endl
		   << "  con: " << (4 * hcon * wgts[iz]) << endl
		   << "  wwind: " << wwind << endl;
	    }
	  }
        else cellMat[iz][iy][ix].ww = numeric_limits<double>::quiet_NaN();
      } // for iz
      if (fabs(wwind - baseW) > epsilon) {
        cout << setprecision(15);
        cout << "calcAllW: iy: " << iy << "  ix: " << ix
	     << "  baseW: " << baseW << "  wwind: " << wwind << endl;
        cout.flush();
        throwerr("wwind error");
      }
    } // for ix
  } // for iy

  //xxx maybe omit:
  // We only calculated the inner points for iy, ix.
  // But we also will need the edge values when
  // we calc the next iteration of U, V values.
  // So set the edges from the nearest interior points.
  // May be NaN.

  for (long iz = 0; iz < nradz; iz++) {
    // Corners
    cellMat[iz][0][0].ww             = cellMat[iz][1][1].ww;
    cellMat[iz][0][nradx-1].ww       = cellMat[iz][1][nradx-2].ww;
    cellMat[iz][nrady-1][0].ww       = cellMat[iz][nrady-2][1].ww;
    cellMat[iz][nrady-1][nradx-1].ww = cellMat[iz][nrady-2][nradx-2].ww;

    // Side edges
    for (long iy = 1; iy < nrady - 1; iy++) {
      cellMat[iz][iy][0].ww       = cellMat[iz][iy][1].ww;
      cellMat[iz][iy][nradx-1].ww = cellMat[iz][iy][nradx-2].ww;
    }
    // Top and bottom edges
    for (long ix = 1; ix < nradx - 1; ix++) {
      cellMat[iz][0][ix].ww       = cellMat[iz][1][ix].ww;
      cellMat[iz][nrady-1][ix].ww = cellMat[iz][nrady-2][ix].ww;
    }
  } // for iz

  delete[] density;
  delete[] wgts;

} // end calcAllWOnMish

//==================================================================

// Calc distance from between a Point and the aircraft

double Fractl::calcDistPtAircraft( Point * pta) {
  double sumsq = 0;
  double delta;
  delta = pta->coordz - pta->aircraftz;
  sumsq += delta * delta;
  delta = pta->coordy - pta->aircrafty;
  sumsq += delta * delta;
  delta = pta->coordx - pta->aircraftx;
  sumsq += delta * delta;
  return sqrt( sumsq);
}

//==================================================================

// Calc distance from a location to Point, in km.

double Fractl::calcDistLocPt( double * loc, Point * pta) {
  double sumsq = 0;
  double delta;
  delta = loc[0] - pta->coordz;
  sumsq += delta * delta;
  delta = loc[1] - pta->coordy;
  sumsq += delta * delta;
  delta = loc[2] - pta->coordx;
  sumsq += delta * delta;
  return sqrt( sumsq);
}

//==================================================================

// Calc distance between two Points, in km.

double Fractl::calcDistPtPt( Point * pta, Point * ptb) {
  double sumsq = 0;
  double delta;
  delta = pta->coordz - ptb->coordz;
  sumsq += delta * delta;
  delta = pta->coordy - ptb->coordy;
  sumsq += delta * delta;
  delta = pta->coordx - ptb->coordx;
  sumsq += delta * delta;
  return sqrt( sumsq);
}


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

#include "PauliOperator.hpp"
#include "cppmicroservices/BundleActivator.h"
#include "cppmicroservices/BundleContext.h"
#include "cppmicroservices/ServiceProperties.h"
#include "qrt.hpp"
#include "xacc.hpp"
#include "xacc_internal_compiler.hpp"
#include "xacc_service.hpp"
using namespace cppmicroservices;

namespace {
class FtqcQubitAllocator : public qcor::AncQubitAllocator {
public:
  static FtqcQubitAllocator *getInstance() {
    if (!g_instance) {
      g_instance = new FtqcQubitAllocator();
    }
    return g_instance;
  }
  static FtqcQubitAllocator *g_instance;
};

FtqcQubitAllocator *FtqcQubitAllocator::g_instance = nullptr;
} // namespace

namespace qcor {
class FTQC : public quantum::QuantumRuntime {
public:
  virtual void initialize(const std::string kernel_name) override {
    provider = xacc::getIRProvider("quantum");
    qpu = xacc::internal_compiler::qpu;
    qubitIdToGlobalIdx.clear();
    setGlobalQubitManager(FtqcQubitAllocator::getInstance());
  }

  void __begin_mark_segment_as_compute() override { mark_as_compute = true; }
  void __end_mark_segment_as_compute() override { mark_as_compute = false; }
  bool isComputeSection() override { return mark_as_compute; }
  const std::string name() const override { return "ftqc"; }
  const std::string description() const override { return ""; }

  virtual void h(const qubit &qidx) override { applyGate("H", {qidx}); }
  virtual void x(const qubit &qidx) override { applyGate("X", {qidx}); }
  virtual void y(const qubit &qidx) override { applyGate("Y", {qidx}); }
  virtual void z(const qubit &qidx) override { applyGate("Z", {qidx}); }
  virtual void t(const qubit &qidx) override { applyGate("T", {qidx}); }
  virtual void tdg(const qubit &qidx) override { applyGate("Tdg", {qidx}); }
  virtual void s(const qubit &qidx) override { applyGate("S", {qidx}); }
  virtual void sdg(const qubit &qidx) override { applyGate("Sdg", {qidx}); }

  // Common single-qubit, parameterized instructions
  virtual void rx(const qubit &qidx, const double theta) override {
    applyGate("Rx", {qidx}, {theta});
  }
  virtual void ry(const qubit &qidx, const double theta) override {
    applyGate("Ry", {qidx}, {theta});
  }
  virtual void rz(const qubit &qidx, const double theta) override {
    applyGate("Rz", {qidx}, {theta});
  }
  // U1(theta) gate
  virtual void u1(const qubit &qidx, const double theta) override {
    applyGate("U1", {qidx}, {theta});
  }
  virtual void u3(const qubit &qidx, const double theta, const double phi,
                  const double lambda) override {
    applyGate("U", {qidx}, {theta, phi, lambda});
  }

  virtual void reset(const qubit &qidx) override { applyGate("Reset", {qidx}); }

  // Measure-Z
  virtual bool mz(const qubit &qidx) override {
    applyGate("Measure", {qidx});
    // Return the measure result stored in the q reg.
    return (*qReg)[qidx.second];
  }

  // Common two-qubit gates.
  virtual void cnot(const qubit &src_idx, const qubit &tgt_idx) override {
    applyGate("CNOT", {src_idx, tgt_idx});
  }
  virtual void cy(const qubit &src_idx, const qubit &tgt_idx) override {
    applyGate("CY", {src_idx, tgt_idx});
  }
  virtual void cz(const qubit &src_idx, const qubit &tgt_idx) override {
    applyGate("CZ", {src_idx, tgt_idx});
  }
  virtual void ch(const qubit &src_idx, const qubit &tgt_idx) override {
    applyGate("CH", {src_idx, tgt_idx});
  }
  virtual void swap(const qubit &src_idx, const qubit &tgt_idx) override {
    applyGate("Swap", {src_idx, tgt_idx});
  }

  // Common parameterized 2 qubit gates.
  virtual void cphase(const qubit &src_idx, const qubit &tgt_idx,
                      const double theta) override {
    applyGate("CPhase", {src_idx, tgt_idx}, {theta});
  }
  virtual void crz(const qubit &src_idx, const qubit &tgt_idx,
                   const double theta) override {
    applyGate("CRZ", {src_idx, tgt_idx}, {theta});
  }

  // exponential of i * theta * H, where H is an Observable pointer
  virtual void exp(qreg q, const double theta,
                   xacc::Observable &H) override { /* TODO */
  }
  virtual void exp(qreg q, const double theta,
                   xacc::Observable *H) override { /* TODO */
  }
  virtual void exp(qreg q, const double theta,
                   std::shared_ptr<xacc::Observable> H) override { /* TODO */
  }

  // Submission API: sanity check that we don't call these API's.
  // e.g. catch high-level code gen errors.
  virtual void submit(xacc::AcceleratorBuffer *buffer) override {
    // Special FTQC submit call to denote an entry kernel has finished.
    // This allows FTQC to handle multiple kernel calls consecutively
    // in the classical code.
    if (buffer == nullptr) {
      entryPoint.reset();
      if (instruction_collect_mode && instruction_collector &&
          instruction_collector->nInstructions() > 0) {
        // Apply these pending instructions:
        // Handle sequential calling of kernels (outside CSP scope, i.e., not nested)
        applyComposite(instruction_collector);
      }
      instruction_collect_mode = false;
      instruction_collector.reset();
      qReg.reset();
    }
  }
  virtual void submit(xacc::AcceleratorBuffer **buffers,
                      const int nBuffers) override {
    throw std::runtime_error("FTQC runtime doesn't support submit API.");
  }

  void general_instruction(std::shared_ptr<xacc::Instruction> inst) override {
    std::vector<double> params;
    for (auto p : inst->getParameters()) {
      params.push_back(p.as<double>());
    }
    applyGate(inst->name(), inst->bits(), params);
  }

  // Some getters for the qcor runtime library.
  virtual void
  set_current_program(std::shared_ptr<xacc::CompositeInstruction> p) override {
    if (!entryPoint) {
      entryPoint = p;
    } else {
      if (p.get() == entryPoint.get()) {
        // std::cout << "Restart FTQC execution\n";
        instruction_collect_mode = false;
        // Now apply these gates:
        applyComposite(p);
        // We have executed all pending instructions...
        entryPoint->clear();
      } else {
        // std::cout << "Begin instruction collection\n";
        // Switch to NISQ mode to collect these instructions
        instruction_collector = p;
        instruction_collect_mode = true;
      }
    }
  }
  virtual std::shared_ptr<xacc::CompositeInstruction>
  get_current_program() override {
    return nullptr;
  }

  void set_current_buffer(xacc::AcceleratorBuffer *buffer) override {
    if (!qReg) {
      qReg = xacc::as_shared_ptr(buffer);
      qubitIdToGlobalIdx.clear();
      // The base qreg will always have exact address in the global register.
      for (size_t i = 0; i < buffer->size(); ++i) {
        qubitIdToGlobalIdx[std::make_pair(buffer->name(), i)] = i;
      }
    }
  }

  QubitAllocator *get_anc_qubit_allocator() {
    return FtqcQubitAllocator::getInstance();
  }

private:
  // Notes: all gate parameters must be resolved (to double) for FT-QRT
  // execution.
  void applyGate(const std::string &gateName, const std::vector<size_t> &bits,
                 const std::vector<double> &params = {}) {
    std::vector<qubit> qubits;
    for (const auto& bit: bits) {
      qubits.emplace_back(qubit("q", bit, qReg.get()));
    }
    applyGate(gateName, qubits, params);
  }

  void applyGate(const std::string &gateName,
                 std::initializer_list<size_t> bits,
                 const std::vector<double> &params = {}) {
    applyGate(gateName, std::vector<size_t>(bits), params);
  }

  void applyGate(const std::string &gateName, const std::vector<qubit> &qbits,
                 const std::vector<double> &params = {}) {
    std::vector<xacc::InstructionParameter> instParams;
    for (const auto &val : params) {
      instParams.emplace_back(val);
    }
    std::vector<size_t> bits;
    for (const auto &qb : qbits) {
      // Never seen this qubit
      const auto qubitId = std::make_pair(qb.first, qb.second);
      if (qubitIdToGlobalIdx.find(qubitId) == qubitIdToGlobalIdx.end()) {
        qubitIdToGlobalIdx[qubitId] = qubitIdToGlobalIdx.size();
        std::stringstream logss;
        logss << "Map " << qb.first << "[" << qb.second << "] to global ID "
              << qubitIdToGlobalIdx[qubitId];
        xacc::info(logss.str());
        qReg->setSize(qubitIdToGlobalIdx.size());
      }
      bits.emplace_back(qubitIdToGlobalIdx[qubitId]);
    }
    auto gateInst = provider->createInstruction(gateName, bits, instParams);
    if (mark_as_compute) {
      gateInst->attachMetadata({{"__qcor__compute__segment__", true}});
    }

    if (!instruction_collect_mode) {
      if (__print_final_submission) {
        std::cout << "Apply gate: " << gateInst->toString() << "\n";
      }
      qpu->apply(qReg, gateInst);
    } else {
      // Note: for instruction collection, we must keep the
      // buffer names as-is (only map to global reg when executed)
      std::vector<std::string> buffer_names;
      std::vector<size_t> bits;
      for (const auto &qb : qbits) {
        bits.emplace_back(qb.second);
        buffer_names.emplace_back(qb.first);
      }
      gateInst->setBits(bits);
      gateInst->setBufferNames(buffer_names);
      instruction_collector->addInstruction(gateInst);
    }
  }

  // Apply a batched composite:
  void applyComposite(std::shared_ptr<xacc::CompositeInstruction> program) {
    for (auto &inst : program->getInstructions()) {
      std::vector<size_t> mapped_bits;
      for (int i = 0; i < inst->bits().size(); ++i) {
        const auto qubitId =
            std::make_pair(inst->getBufferNames()[i], inst->bits()[i]);
        if (qubitIdToGlobalIdx.find(qubitId) == qubitIdToGlobalIdx.end()) {
          qubitIdToGlobalIdx[qubitId] = qubitIdToGlobalIdx.size();
          std::stringstream logss;
          logss << "Map " << inst->getBufferNames()[i] << "[" << inst->bits()[i]
                << "] to global ID " << qubitIdToGlobalIdx[qubitId];
          xacc::info(logss.str());
          qReg->setSize(qubitIdToGlobalIdx.size());
        }
        mapped_bits.emplace_back(qubitIdToGlobalIdx[qubitId]);
      }
      inst->setBits(mapped_bits);
      if (__print_final_submission) {
        std::cout << "Apply gate: " << inst->toString() << "\n";
      }
      qpu->apply(qReg, inst);
    }
  }

private:
  bool mark_as_compute = false;
  // Are we in a instruction collection mode?
  // cannot execute the instructions now.
  bool instruction_collect_mode = false;
  std::shared_ptr<xacc::CompositeInstruction> instruction_collector;
  std::shared_ptr<xacc::IRProvider> provider;
  std::shared_ptr<xacc::Accelerator> qpu;
  // TODO: eventually, we may want to support an arbitrary number of qubit
  // registers when the FTQC backend can support it.
  std::shared_ptr<xacc::AcceleratorBuffer> qReg;
  std::map<std::pair<std::string, size_t>, size_t> qubitIdToGlobalIdx;
  std::shared_ptr<xacc::CompositeInstruction> entryPoint;
};
} // namespace qcor

namespace {
class US_ABI_LOCAL FtqcQRTActivator : public BundleActivator {
public:
  FtqcQRTActivator() {}
  void Start(BundleContext context) {
    auto xt = std::make_shared<qcor::FTQC>();
    context.RegisterService<quantum::QuantumRuntime>(xt);
  }
  void Stop(BundleContext /*context*/) {}
};
} // namespace

CPPMICROSERVICES_EXPORT_BUNDLE_ACTIVATOR(FtqcQRTActivator)


//===-- Preprocessor Self Iteration -----------------------------*- C++ -*-===//
///
/// \file
/// \brief 复杂宏的自包含迭代。
/// \details 该文件通过预处理，生成形如：
///          \code{.cpp}
///          constexpr int sample0 { 0 };
///          constexpr int sample1 { 1 };
///          constexpr int sample2 { 2 };
///          constexpr int sample3 { 3 };
///          constexpr int sample4 { 4 };
///          constexpr int sample5 { 5 };
///          constexpr int sample6 { 6 };
///          constexpr int sample7 { 7 };
///          constexpr int sample8 { 8 };
///          constexpr int sample9 { 9 };
///          \endcode
///          的模板。
///
/// \version 2019-09-06
/// \since 2019-09-05
/// \authors zhengrr
/// \copyright Unlicense
///
//===----------------------------------------------------------------------===//

//==============================================================================
#ifndef BOOST_PP_IS_ITERATING

#ifndef PREPROCESSOR_SELF_ITERATION_HPP_
#define PREPROCESSOR_SELF_ITERATION_HPP_

#include <boost/preprocessor.hpp>

#ifndef SAMPLE_MAX
#define SAMPLE_MAX 10
#endif

#define BOOST_PP_ITERATION_PARAMS_1 (3, (0, SAMPLE_MAX, "preprocessor_self_iteration.hpp"))
#include BOOST_PP_ITERATE()

#endif

//==============================================================================
#else

#define k BOOST_PP_ITERATION_START()
#define n BOOST_PP_ITERATION()
#define j BOOST_PP_ITERATION_FINISH()

constexpr int BOOST_PP_CAT(sample, n) { n };

#undef j
#undef n
#undef k

#endif


//---------------------------------------------------------------------------//
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//
// See http://kylelutz.github.com/compute for more information.
//---------------------------------------------------------------------------//

#ifndef BOOST_COMPUTE_FUNCTIONAL_HPP
#define BOOST_COMPUTE_FUNCTIONAL_HPP

/// \file
///
/// Meta-header to include all Boost.Compute functional headers.

#include <boost/compute/functional/as.hpp>
#include <boost/compute/functional/atomic.hpp>
#include <boost/compute/functional/common.hpp>
#include <boost/compute/functional/convert.hpp>
#include <boost/compute/functional/field.hpp>
#include <boost/compute/functional/geometry.hpp>
#include <boost/compute/functional/get.hpp>
#include <boost/compute/functional/hash.hpp>
#include <boost/compute/functional/integer.hpp>
#include <boost/compute/functional/logical.hpp>
#include <boost/compute/functional/math.hpp>
#include <boost/compute/functional/operator.hpp>
#include <boost/compute/functional/popcount.hpp>
#include <boost/compute/functional/relational.hpp>

#endif // BOOST_COMPUTE_FUNCTIONAL_HPP


/*
 * PersistentPerturbation.hpp
 *
 *  Created on: Sep 2, 2009
 *      Author: stewie
 */

#ifndef PERSISTENTPERTURBATION_HPP_
#define PERSISTENTPERTURBATION_HPP_

#include <cassert>
#include <cstddef>
#include <vector>
#include <functional>

#include <boost/iterator/counting_iterator.hpp>

#include "network_state.hpp"

namespace bn {

class PersistentPerturbation : public std::unary_function<State&, void> {
protected:
	const State init;

public:
	PersistentPerturbation() :
		init(0) {
	}

	PersistentPerturbation(const State& init) :
		init(init) {
	}

	const State& getState() const {
		return init;
	}

	virtual ~PersistentPerturbation() {
	}

	virtual result_type operator()(argument_type s) = 0;
};

class Flip : public PersistentPerturbation {
private:
	const size_t i;
	const bool value;

public:
	Flip(State s, const size_t i) :
		PersistentPerturbation(s.flip(i)), i(i), value(s[i]) {
		assert(init[i] == value);
	}

	result_type operator()(argument_type s) {
		s[i] = value;
	}
};

class Clamp : public PersistentPerturbation {
private:
	const State andMask;
	const State orMask;

public:
	Clamp(const State& value, const State& mask) :
		PersistentPerturbation(value), andMask(~mask), orMask(mask & value) {
	}

	result_type operator()(argument_type s) {
		s &= andMask;
		s |= orMask;
#ifndef NDEBUG
		State temp(s);
		temp &= andMask;
		temp |= orMask;
		assert(temp == s);
#endif
	}
};

class PullUp : public PersistentPerturbation {
private:
	const State mask;

public:
	PullUp(const State& mask, const State& init) :
		PersistentPerturbation(init | mask), mask(mask) {
	}

	PullUp(const State& mask) :
		mask(mask) {
	}

	result_type operator()(argument_type s) {
		s |= mask;
	}
};

class PullDown : public PersistentPerturbation {
private:
	const State mask;

public:
	PullDown(const State& mask, const State& init) :
		PersistentPerturbation(init & ~mask), mask(~mask) {
	}

	PullDown(const State& mask) :
		mask(~mask) {
	}

	result_type operator()(argument_type s) {
		s &= mask;
	}
};

Clamp* perturb_state_randomly(State s, const size_t h) {
#ifndef NDEBUG
	State temp = s;
#endif
	const size_t n = s.size();
	State mask(n);
	std::vector<size_t> indexes(boost::counting_iterator<size_t>(0),
			boost::counting_iterator<size_t>(n));
	size_t limit = n;
	for (size_t i = 0; i < h; ++i) {
		const size_t current = rand() % (limit--);
		s.flip(indexes[current]);
		mask.flip(indexes[current]);
		std::swap(indexes[limit], indexes[current]);
	}
#ifndef NDEBUG
	assert((temp ^ s).count() == h);
	Clamp* const clamp = new Clamp(s, mask);
	assert((temp ^ clamp->getState()).count() == h);
	(*clamp)(temp);
	assert(temp == s);
	delete clamp;
#endif
	return new Clamp(s, mask);
}

}

#endif /* PERSISTENTPERTURBATION_HPP_ */


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

#include "DecPOMDPUtilities.h"
#include <boost/random/uniform_01.hpp>
#include <boost/random/uniform_int.hpp>
#include "PolicyGraphTraversal.h"

namespace pgi {
DecPOMDPHistory sample_random_joint_history(const DecPOMDPDiscrete& decpomdp,
                                            const belief_t& b0, PRNG& rng,
                                            unsigned int length) {
  std::vector<std::size_t> states;
  boost::random::uniform_01<double> dist01;
  states.emplace_back(sample_state(b0, rng(dist01)));

  if (length == 0) return DecPOMDPHistory{{states}, History()};

  History h;
  h.reserve(length);
  boost::random::uniform_int_distribution<std::size_t> act_dist(
      0, decpomdp.joint_action_space().num_joint_indices() - 1);
  for (unsigned int i = 0; i < length; ++i) {
    const std::size_t j_act = rng(act_dist);
    states.emplace_back(decpomdp.transition_model().sample_next_state(
        states.back(), j_act, rng(dist01)));
    const std::size_t j_obs = decpomdp.observation_model().sample_observation(
        states.back(), j_act, rng(dist01));
    h.emplace_back(ActionObservation{j_act, j_obs});
  }
  return DecPOMDPHistory{{states}, h};
}

std::pair<DecPOMDPHistory, JointPolicy::joint_vertex_t> sample_joint_history(
    const DecPOMDPDiscrete& decpomdp, const JointPolicy& jp, const belief_t& b0,
    PRNG& rng, unsigned int length) {
  JointPolicyGraphTraversal tv(jp.root(), jp);
  std::vector<std::size_t> states;
  boost::random::uniform_01<double> dist01;
  states.emplace_back(sample_state(b0, rng(dist01)));

  if (length == 0)
    return std::make_pair(DecPOMDPHistory{{states}, History()},
                          tv.current_vertex());

  History h;
  h.reserve(length);

  const JointActionSpace jas = decpomdp.joint_action_space();
  const JointObservationSpace jos = decpomdp.joint_observation_space();

  for (unsigned int i = 0; i < length; ++i) {
    const std::size_t j_act = tv.current_action(jas);
    states.emplace_back(decpomdp.transition_model().sample_next_state(
        states.back(), j_act, rng(dist01)));
    const std::size_t j_obs = decpomdp.observation_model().sample_observation(
        states.back(), j_act, rng(dist01));
    h.emplace_back(ActionObservation{j_act, j_obs});
    tv.traverse(j_obs, decpomdp.joint_observation_space());
  }
  return std::make_pair(DecPOMDPHistory{{states}, h}, tv.current_vertex());
}

}  // namespace pgi


// Boost.Geometry (aka GGL, Generic Geometry Library)
//
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#include <geometry_test_common.hpp>

#include <algorithms/test_overlay.hpp>

#include <boost/range/algorithm/copy.hpp>

#include <boost/geometry/geometry.hpp>
#include <boost/geometry/algorithms/detail/overlay/select_rings.hpp>
#include <boost/geometry/algorithms/detail/overlay/assign_parents.hpp>

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

#include <boost/geometry/io/wkt/read.hpp>

#include <boost/assign/list_of.hpp>
#include <boost/foreach.hpp>
#include <boost/tuple/tuple.hpp>

template
<
    typename Geometry1,
    typename Geometry2,
    bg::overlay_type OverlayType,
    typename RingIdVector,
    typename WithinVector
>
void test_geometry(std::string const& wkt1, std::string const& wkt2,
    RingIdVector const& expected_ids,
    WithinVector const& expected_withins)
{
    typedef bg::detail::overlay::ring_properties<typename bg::point_type<Geometry1>::type> properties;

    Geometry1 geometry1;
    Geometry2 geometry2;

    bg::read_wkt(wkt1, geometry1);
    bg::read_wkt(wkt2, geometry2);

    typedef std::map<bg::ring_identifier, properties> map_type;
    map_type selected;
    std::map<bg::ring_identifier, int> empty;

    bg::detail::overlay::select_rings<OverlayType>(geometry1, geometry2, empty, selected, true);

    BOOST_CHECK_EQUAL(selected.size(), expected_ids.size());
    BOOST_CHECK_EQUAL(selected.size(), expected_withins.size());

    if (selected.size() <= expected_ids.size())
    {
        BOOST_AUTO(eit, expected_ids.begin());
        BOOST_AUTO(wit, expected_withins.begin());
        for(typename map_type::const_iterator it = selected.begin(); it != selected.end(); ++it, ++eit, ++wit)
        {
            bg::ring_identifier const ring_id = it->first;
            BOOST_CHECK_EQUAL(ring_id.source_index, eit->source_index);
            BOOST_CHECK_EQUAL(ring_id.multi_index, eit->multi_index);
            BOOST_CHECK_EQUAL(ring_id.ring_index, eit->ring_index);
            BOOST_CHECK_EQUAL(it->second.within_code, *wit);
        }
    }
}

template <typename P>
void test_all()
{
    // Point in correct clockwise ring -> should return true
    typedef bg::ring_identifier rid;

    test_geometry<bg::model::polygon<P>, bg::model::polygon<P>, bg::overlay_union>(
        winded[0], winded[1],
        boost::assign::list_of
                (rid(0,-1,-1))
                (rid(0,-1, 0))
                (rid(0,-1, 1))
                (rid(0,-1, 3))
                (rid(1,-1, 1))
                (rid(1,-1, 2)),

        boost::assign::list_of
                (-1)
                (-1)
                (-1)
                (-1)
                (-1)
                (-1)
            );

            //boost::assign::tuple_list_of(0,-1,-1,-1)(0,-1,0,-1)(0,-1,1,-1)(0,-1,3,-1)(1,-1,1,-1)(1,-1,2,-1));

    test_geometry<bg::model::polygon<P>, bg::model::polygon<P>, bg::overlay_intersection>(
            winded[0], winded[1],
        boost::assign::list_of
                (rid(0,-1, 2))
                (rid(1,-1,-1))
                (rid(1,-1, 0))
                (rid(1,-1, 3)),

        boost::assign::list_of
                (1)
                (1)
                (1)
                (1)
            );

            //boost::assign::tuple_list_of(0,-1,2,1)(1,-1,-1,1)(1,-1,0,1)(1,-1,3,1));
}

int test_main( int , char* [] )
{
    test_all<bg::model::d2::point_xy<double> >();

    return 0;
}


/**
 * \file Math.hpp
 * \brief Header for GeographicLib::Math class
 *
 * Copyright (c) Charles Karney (2008-2019) <charles@karney.com> and licensed
 * under the MIT/X11 License.  For more information, see
 * https://geographiclib.sourceforge.io/
 **********************************************************************/

// Constants.hpp includes Math.hpp.  Place this include outside Math.hpp's
// include guard to enforce this ordering.
#include <GeographicLib/Constants.hpp>

#if !defined(GEOGRAPHICLIB_MATH_HPP)
#define GEOGRAPHICLIB_MATH_HPP 1

/**
 * Are C++11 math functions available?
 **********************************************************************/
#if !defined(GEOGRAPHICLIB_CXX11_MATH)
// Recent versions of g++ -std=c++11 (4.7 and later?) set __cplusplus to 201103
// and support the new C++11 mathematical functions, std::atanh, etc.  However
// the Android toolchain, which uses g++ -std=c++11 (4.8 as of 2014-03-11,
// according to Pullan Lu), does not support std::atanh.  Android toolchains
// might define __ANDROID__ or ANDROID; so need to check both.  With OSX the
// version is GNUC version 4.2 and __cplusplus is set to 201103, so remove the
// version check on GNUC.
#  if defined(__GNUC__) && __cplusplus >= 201103 && \
  !(defined(__ANDROID__) || defined(ANDROID) || defined(__CYGWIN__))
#    define GEOGRAPHICLIB_CXX11_MATH 1
// Visual C++ 12 supports these functions
#  elif defined(_MSC_VER) && _MSC_VER >= 1800
#    define GEOGRAPHICLIB_CXX11_MATH 1
#  else
#    define GEOGRAPHICLIB_CXX11_MATH 0
#  endif
#endif

#if !defined(GEOGRAPHICLIB_WORDS_BIGENDIAN)
#  define GEOGRAPHICLIB_WORDS_BIGENDIAN 0
#endif

#if !defined(GEOGRAPHICLIB_HAVE_LONG_DOUBLE)
#  define GEOGRAPHICLIB_HAVE_LONG_DOUBLE 0
#endif

#if !defined(GEOGRAPHICLIB_PRECISION)
/**
 * The precision of floating point numbers used in %GeographicLib.  1 means
 * float (single precision); 2 (the default) means double; 3 means long double;
 * 4 is reserved for quadruple precision.  Nearly all the testing has been
 * carried out with doubles and that's the recommended configuration.  In order
 * for long double to be used, GEOGRAPHICLIB_HAVE_LONG_DOUBLE needs to be
 * defined.  Note that with Microsoft Visual Studio, long double is the same as
 * double.
 **********************************************************************/
#  define GEOGRAPHICLIB_PRECISION 2
#endif

#include <cmath>
#include <algorithm>
#include <limits>

#if GEOGRAPHICLIB_PRECISION == 4
#include <boost/version.hpp>
#include <boost/multiprecision/float128.hpp>
#include <boost/math/special_functions.hpp>
#elif GEOGRAPHICLIB_PRECISION == 5
#include <mpreal.h>
#endif

#if GEOGRAPHICLIB_PRECISION > 3
// volatile keyword makes no sense for multiprec types
#define GEOGRAPHICLIB_VOLATILE
// Signal a convergence failure with multiprec types by throwing an exception
// at loop exit.
#define GEOGRAPHICLIB_PANIC \
  (throw GeographicLib::GeographicErr("Convergence failure"), false)
#else
#define GEOGRAPHICLIB_VOLATILE volatile
// Ignore convergence failures with standard floating points types by allowing
// loop to exit cleanly.
#define GEOGRAPHICLIB_PANIC false
#endif

namespace GeographicLib {

  /**
   * \brief Mathematical functions needed by %GeographicLib
   *
   * Define mathematical functions in order to localize system dependencies and
   * to provide generic versions of the functions.  In addition define a real
   * type to be used by %GeographicLib.
   *
   * Example of use:
   * \include example-Math.cpp
   **********************************************************************/
  class GEOGRAPHICLIB_EXPORT Math {
  private:
    void dummy();               // Static check for GEOGRAPHICLIB_PRECISION
    Math();                     // Disable constructor
  public:

#if GEOGRAPHICLIB_HAVE_LONG_DOUBLE
    /**
     * The extended precision type for real numbers, used for some testing.
     * This is long double on computers with this type; otherwise it is double.
     **********************************************************************/
    typedef long double extended;
#else
    typedef double extended;
#endif

#if GEOGRAPHICLIB_PRECISION == 2
    /**
     * The real type for %GeographicLib. Nearly all the testing has been done
     * with \e real = double.  However, the algorithms should also work with
     * float and long double (where available).  (CAUTION: reasonable
     * accuracy typically cannot be obtained using floats.)
     **********************************************************************/
    typedef double real;
#elif GEOGRAPHICLIB_PRECISION == 1
    typedef float real;
#elif GEOGRAPHICLIB_PRECISION == 3
    typedef extended real;
#elif GEOGRAPHICLIB_PRECISION == 4
    typedef boost::multiprecision::float128 real;
#elif GEOGRAPHICLIB_PRECISION == 5
    typedef mpfr::mpreal real;
#else
    typedef double real;
#endif

    /**
     * @return the number of bits of precision in a real number.
     **********************************************************************/
    static int digits();

    /**
     * Set the binary precision of a real number.
     *
     * @param[in] ndigits the number of bits of precision.
     * @return the resulting number of bits of precision.
     *
     * This only has an effect when GEOGRAPHICLIB_PRECISION = 5.  See also
     * Utility::set_digits for caveats about when this routine should be
     * called.
     **********************************************************************/
    static int set_digits(int ndigits);

    /**
     * @return the number of decimal digits of precision in a real number.
     **********************************************************************/
    static int digits10();

    /**
     * Number of additional decimal digits of precision for real relative to
     * double (0 for float).
     **********************************************************************/
    static int extra_digits();

    /**
     * true if the machine is big-endian.
     **********************************************************************/
    static const bool bigendian = GEOGRAPHICLIB_WORDS_BIGENDIAN;

    /**
     * @tparam T the type of the returned value.
     * @return π.
     **********************************************************************/
    template<typename T> static T pi() {
      using std::atan2;
      static const T pi = atan2(T(0), T(-1));
      return pi;
    }
    /**
     * A synonym for pi<real>().
     **********************************************************************/
    static real pi() { return pi<real>(); }

    /**
     * @tparam T the type of the returned value.
     * @return the number of radians in a degree.
     **********************************************************************/
    template<typename T> static T degree() {
      static const T degree = pi<T>() / 180;
      return degree;
    }
    /**
     * A synonym for degree<real>().
     **********************************************************************/
    static real degree() { return degree<real>(); }

    /**
     * Square a number.
     *
     * @tparam T the type of the argument and the returned value.
     * @param[in] x
     * @return x2.
     **********************************************************************/
    template<typename T> static T sq(T x)
    { return x * x; }

    /**
     * The hypotenuse function avoiding underflow and overflow.
     *
     * @tparam T the type of the arguments and the returned value.
     * @param[in] x
     * @param[in] y
     * @return sqrt(x2 + y2).
     **********************************************************************/
    template<typename T> static T hypot(T x, T y);

    /**
     * exp(\e x) − 1 accurate near \e x = 0.
     *
     * @tparam T the type of the argument and the returned value.
     * @param[in] x
     * @return exp(\e x) − 1.
     **********************************************************************/
    template<typename T> static T expm1(T x);

    /**
     * log(1 + \e x) accurate near \e x = 0.
     *
     * @tparam T the type of the argument and the returned value.
     * @param[in] x
     * @return log(1 + \e x).
     **********************************************************************/
    template<typename T> static T log1p(T x);

    /**
     * The inverse hyperbolic sine function.
     *
     * @tparam T the type of the argument and the returned value.
     * @param[in] x
     * @return asinh(\e x).
     **********************************************************************/
    template<typename T> static T asinh(T x);

    /**
     * The inverse hyperbolic tangent function.
     *
     * @tparam T the type of the argument and the returned value.
     * @param[in] x
     * @return atanh(\e x).
     **********************************************************************/
    template<typename T> static T atanh(T x);

    /**
     * Copy the sign.
     *
     * @tparam T the type of the argument.
     * @param[in] x gives the magitude of the result.
     * @param[in] y gives the sign of the result.
     * @return value with the magnitude of \e x and with the sign of \e y.
     *
     * This routine correctly handles the case \e y = −0, returning
     * &minus|x|.
     **********************************************************************/
    template<typename T> static T copysign(T x, T y);

    /**
     * The cube root function.
     *
     * @tparam T the type of the argument and the returned value.
     * @param[in] x
     * @return the real cube root of \e x.
     **********************************************************************/
    template<typename T> static T cbrt(T x);

    /**
     * The remainder function.
     *
     * @tparam T the type of the arguments and the returned value.
     * @param[in] x
     * @param[in] y
     * @return the remainder of \e x/\e y in the range [−\e y/2, \e y/2].
     **********************************************************************/
    template<typename T> static T remainder(T x, T y);

    /**
     * The remquo function.
     *
     * @tparam T the type of the arguments and the returned value.
     * @param[in] x
     * @param[in] y
     * @param[out] n the low 3 bits of the quotient
     * @return the remainder of \e x/\e y in the range [−\e y/2, \e y/2].
     **********************************************************************/
    template<typename T> static T remquo(T x, T y, int* n);

    /**
     * The round function.
     *
     * @tparam T the type of the argument and the returned value.
     * @param[in] x
     * @return \e x round to the nearest integer (ties round away from 0).
     **********************************************************************/
    template<typename T> static T round(T x);

    /**
     * The lround function.
     *
     * @tparam T the type of the argument.
     * @param[in] x
     * @return \e x round to the nearest integer as a long int (ties round away
     *   from 0).
     *
     * If the result does not fit in a long int, the return value is undefined.
     **********************************************************************/
    template<typename T> static long lround(T x);

    /**
     * Fused multiply and add.
     *
     * @tparam T the type of the arguments and the returned value.
     * @param[in] x
     * @param[in] y
     * @param[in] z
     * @return xy + z, correctly rounded (on those platforms with
     *   support for the fma instruction).
     *
     * On platforms without the fma instruction, no attempt is
     * made to improve on the result of a rounded multiplication followed by a
     * rounded addition.
     **********************************************************************/
    template<typename T> static T fma(T x, T y, T z);

    /**
     * Normalize a two-vector.
     *
     * @tparam T the type of the argument and the returned value.
     * @param[in,out] x on output set to x/hypot(x, y).
     * @param[in,out] y on output set to y/hypot(x, y).
     **********************************************************************/
    template<typename T> static void norm(T& x, T& y)
    { T h = hypot(x, y); x /= h; y /= h; }

    /**
     * The error-free sum of two numbers.
     *
     * @tparam T the type of the argument and the returned value.
     * @param[in] u
     * @param[in] v
     * @param[out] t the exact error given by (\e u + \e v) - \e s.
     * @return \e s = round(\e u + \e v).
     *
     * See D. E. Knuth, TAOCP, Vol 2, 4.2.2, Theorem B.  (Note that \e t can be
     * the same as one of the first two arguments.)
     **********************************************************************/
    template<typename T> static T sum(T u, T v, T& t);

    /**
     * Evaluate a polynomial.
     *
     * @tparam T the type of the arguments and returned value.
     * @param[in] N the order of the polynomial.
     * @param[in] p the coefficient array (of size \e N + 1).
     * @param[in] x the variable.
     * @return the value of the polynomial.
     *
     * Evaluate y = ∑n=0..N
     * pn xN−n.
     * Return 0 if \e N < 0.  Return p0, if \e N = 0 (even
     * if \e x is infinite or a nan).  The evaluation uses Horner's method.
     **********************************************************************/
    template<typename T> static T polyval(int N, const T p[], T x)
    // This used to employ Math::fma; but that's too slow and it seemed not to
    // improve the accuracy noticeably.  This might change when there's direct
    // hardware support for fma.
    { T y = N < 0 ? 0 : *p++; while (--N >= 0) y = y * x + *p++; return y; }

    /**
     * Normalize an angle.
     *
     * @tparam T the type of the argument and returned value.
     * @param[in] x the angle in degrees.
     * @return the angle reduced to the range (−180°, 180°].
     *
     * The range of \e x is unrestricted.
     **********************************************************************/
    template<typename T> static T AngNormalize(T x) {
      x = remainder(x, T(360)); return x != -180 ? x : 180;
    }

    /**
     * Normalize a latitude.
     *
     * @tparam T the type of the argument and returned value.
     * @param[in] x the angle in degrees.
     * @return x if it is in the range [−90°, 90°], otherwise
     *   return NaN.
     **********************************************************************/
    template<typename T> static T LatFix(T x)
    { using std::abs; return abs(x) > 90 ? NaN<T>() : x; }

    /**
     * The exact difference of two angles reduced to
     * (−180°, 180°].
     *
     * @tparam T the type of the arguments and returned value.
     * @param[in] x the first angle in degrees.
     * @param[in] y the second angle in degrees.
     * @param[out] e the error term in degrees.
     * @return \e d, the truncated value of \e y − \e x.
     *
     * This computes \e z = \e y − \e x exactly, reduced to
     * (−180°, 180°]; and then sets \e z = \e d + \e e where \e d
     * is the nearest representable number to \e z and \e e is the truncation
     * error.  If \e d = −180, then \e e > 0; If \e d = 180, then \e e
     * ≤ 0.
     **********************************************************************/
    template<typename T> static T AngDiff(T x, T y, T& e) {
      T t, d = AngNormalize(sum(remainder(-x, T(360)),
                                remainder( y, T(360)), t));
      // Here y - x = d + t (mod 360), exactly, where d is in (-180,180] and
      // abs(t) <= eps (eps = 2^-45 for doubles).  The only case where the
      // addition of t takes the result outside the range (-180,180] is d = 180
      // and t > 0.  The case, d = -180 + eps, t = -eps, can't happen, since
      // sum would have returned the exact result in such a case (i.e., given t
      // = 0).
      return sum(d == 180 && t > 0 ? -180 : d, t, e);
    }

    /**
     * Difference of two angles reduced to [−180°, 180°]
     *
     * @tparam T the type of the arguments and returned value.
     * @param[in] x the first angle in degrees.
     * @param[in] y the second angle in degrees.
     * @return \e y − \e x, reduced to the range [−180°,
     *   180°].
     *
     * The result is equivalent to computing the difference exactly, reducing
     * it to (−180°, 180°] and rounding the result.  Note that
     * this prescription allows −180° to be returned (e.g., if \e x
     * is tiny and negative and \e y = 180°).
     **********************************************************************/
    template<typename T> static T AngDiff(T x, T y)
    { T e; return AngDiff(x, y, e); }

    /**
     * Coarsen a value close to zero.
     *
     * @tparam T the type of the argument and returned value.
     * @param[in] x
     * @return the coarsened value.
     *
     * The makes the smallest gap in \e x = 1/16 − nextafter(1/16, 0) =
     * 1/257 for reals = 0.7 pm on the earth if \e x is an angle in
     * degrees.  (This is about 1000 times more resolution than we get with
     * angles around 90°.)  We use this to avoid having to deal with near
     * singular cases when \e x is non-zero but tiny (e.g.,
     * 10−200).  This converts −0 to +0; however tiny
     * negative numbers get converted to −0.
     **********************************************************************/
    template<typename T> static T AngRound(T x);

    /**
     * Evaluate the sine and cosine function with the argument in degrees
     *
     * @tparam T the type of the arguments.
     * @param[in] x in degrees.
     * @param[out] sinx sin(x).
     * @param[out] cosx cos(x).
     *
     * The results obey exactly the elementary properties of the trigonometric
     * functions, e.g., sin 9° = cos 81° = − sin 123456789°.
     * If x = −0, then \e sinx = −0; this is the only case where
     * −0 is returned.
     **********************************************************************/
    template<typename T> static void sincosd(T x, T& sinx, T& cosx);

    /**
     * Evaluate the sine function with the argument in degrees
     *
     * @tparam T the type of the argument and the returned value.
     * @param[in] x in degrees.
     * @return sin(x).
     **********************************************************************/
    template<typename T> static T sind(T x);

    /**
     * Evaluate the cosine function with the argument in degrees
     *
     * @tparam T the type of the argument and the returned value.
     * @param[in] x in degrees.
     * @return cos(x).
     **********************************************************************/
    template<typename T> static T cosd(T x);

    /**
     * Evaluate the tangent function with the argument in degrees
     *
     * @tparam T the type of the argument and the returned value.
     * @param[in] x in degrees.
     * @return tan(x).
     *
     * If \e x = ±90°, then a suitably large (but finite) value is
     * returned.
     **********************************************************************/
    template<typename T> static T tand(T x);

    /**
     * Evaluate the atan2 function with the result in degrees
     *
     * @tparam T the type of the arguments and the returned value.
     * @param[in] y
     * @param[in] x
     * @return atan2(y, x) in degrees.
     *
     * The result is in the range (−180° 180°].  N.B.,
     * atan2d(±0, −1) = +180°; atan2d(−ε,
     * −1) = −180°, for ε positive and tiny;
     * atan2d(±0, +1) = ±0°.
     **********************************************************************/
    template<typename T> static T atan2d(T y, T x);

    /**
     * Evaluate the atan function with the result in degrees
     *
     * @tparam T the type of the argument and the returned value.
     * @param[in] x
     * @return atan(x) in degrees.
     **********************************************************************/
   template<typename T> static T atand(T x);

    /**
     * Evaluate e atanh(e x)
     *
     * @tparam T the type of the argument and the returned value.
     * @param[in] x
     * @param[in] es the signed eccentricity =  sign(e2)
     *    sqrt(|e2|)
     * @return e atanh(e x)
     *
     * If e2 is negative (e is imaginary), the
     * expression is evaluated in terms of atan.
     **********************************************************************/
    template<typename T> static T eatanhe(T x, T es);

    /**
     * tanχ in terms of tanφ
     *
     * @tparam T the type of the argument and the returned value.
     * @param[in] tau τ = tanφ
     * @param[in] es the signed eccentricity = sign(e2)
     *   sqrt(|e2|)
     * @return τ′ = tanχ
     *
     * See Eqs. (7--9) of
     * C. F. F. Karney,
     *
     * Transverse Mercator with an accuracy of a few nanometers,
     * J. Geodesy 85(8), 475--485 (Aug. 2011)
     * (preprint
     * arXiv:1002.1417).
     **********************************************************************/
    template<typename T> static T taupf(T tau, T es);

    /**
     * tanφ in terms of tanχ
     *
     * @tparam T the type of the argument and the returned value.
     * @param[in] taup τ′ = tanχ
     * @param[in] es the signed eccentricity = sign(e2)
     *   sqrt(|e2|)
     * @return τ = tanφ
     *
     * See Eqs. (19--21) of
     * C. F. F. Karney,
     *
     * Transverse Mercator with an accuracy of a few nanometers,
     * J. Geodesy 85(8), 475--485 (Aug. 2011)
     * (preprint
     * arXiv:1002.1417).
     **********************************************************************/
    template<typename T> static T tauf(T taup, T es);

    /**
     * Test for finiteness.
     *
     * @tparam T the type of the argument.
     * @param[in] x
     * @return true if number is finite, false if NaN or infinite.
     **********************************************************************/
    template<typename T> static bool isfinite(T x);

    /**
     * The NaN (not a number)
     *
     * @tparam T the type of the returned value.
     * @return NaN if available, otherwise return the max real of type T.
     **********************************************************************/
    template<typename T> static T NaN();

    /**
     * A synonym for NaN<real>().
     **********************************************************************/
    static real NaN() { return NaN<real>(); }

    /**
     * Test for NaN.
     *
     * @tparam T the type of the argument.
     * @param[in] x
     * @return true if argument is a NaN.
     **********************************************************************/
    template<typename T> static bool isnan(T x);

    /**
     * Infinity
     *
     * @tparam T the type of the returned value.
     * @return infinity if available, otherwise return the max real.
     **********************************************************************/
    template<typename T> static T infinity();

    /**
     * A synonym for infinity<real>().
     **********************************************************************/
    static real infinity() { return infinity<real>(); }

    /**
     * Swap the bytes of a quantity
     *
     * @tparam T the type of the argument and the returned value.
     * @param[in] x
     * @return x with its bytes swapped.
     **********************************************************************/
    template<typename T> static T swab(T x) {
      union {
        T r;
        unsigned char c[sizeof(T)];
      } b;
      b.r = x;
      for (int i = sizeof(T)/2; i--; )
        std::swap(b.c[i], b.c[sizeof(T) - 1 - i]);
      return b.r;
    }

  };

} // namespace GeographicLib

#endif  // GEOGRAPHICLIB_MATH_HPP


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

    @copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_FUNCTION_IS_NOT_IMAG_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_IS_NOT_IMAG_HPP_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{

  /*!
    @ingroup group-predicates

    This function object returns @ref True or @ref False according x is not pure imaginary or is.

    For non complex numbers it is @ref True except if x is @ref Zero.

    @par Header <boost/simd/function/is_not_imag.hpp>

    @par Note

     Using `is_not_imag(x)` is similar to: `x != 0` for real entries.

    @par Example:

      @snippet is_not_imag.cpp is_not_imag

    @par Possible output:

      @snippet is_not_imag.txt is_not_imag

  **/
  as_logical_t<Value> is_not_imag(Value const& x);
} }
#endif

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

#endif


#include <Eigen/Core>

#include <numpy_eigen/boost_python_headers.hpp>
Eigen::Matrix<boost::uint8_t, 6, 2> test_uchar_6_2(const Eigen::Matrix<boost::uint8_t, 6, 2> & M)
{
	return M;
}
void export_uchar_6_2()
{
	boost::python::def("test_uchar_6_2",test_uchar_6_2);
}


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

#ifndef MTL_REAL_INCLUDE
#define MTL_REAL_INCLUDE

#include <complex>
#include <boost/numeric/mtl/mtl_fwd.hpp>
#include <boost/numeric/mtl/utility/enable_if.hpp>
#include <boost/numeric/mtl/utility/tag.hpp>
#include <boost/numeric/mtl/utility/category.hpp>
#include <boost/numeric/mtl/interface/vpt.hpp>
#include <boost/numeric/mtl/vector/map_view.hpp>

namespace mtl {

namespace sfunctor {

    template <typename Value>
    struct real
    {
	typedef Value result_type;

	static inline Value apply(const Value& v)
	{
	    return v;
	}

	Value operator()(const Value& v) const
	{
	    return v;
	}
    };

    template <typename Value>
    struct real<std::complex<Value> >
    {
	typedef Value result_type;

	static inline result_type apply(const std::complex<Value>& v)
	{
	    return std::real(v);
	}
	result_type operator()(const std::complex<Value>& v) const
	{
	    return std::real(v);
	}
    };
}

/// real part of scalars (including non-complex)
template <typename Value>
typename mtl::traits::enable_if_scalar<Value, typename sfunctor::real<Value>::result_type>::type
inline real(const Value& v)
{
    vampir_trace<3> tracer;
    return sfunctor::real<Value>::apply(v);
}

namespace vector {

    /// Real part of a vector
    template <typename Vector>
    typename mtl::traits::enable_if_vector<Vector, real_view<Vector> >::type
    inline real(const Vector& v)
    {
	return real_view<Vector>(v);
    }
}

namespace matrix {

    /// Real part of a matrix
    template <typename Matrix>
    typename mtl::traits::enable_if_matrix<Matrix, real_view<Matrix> >::type
    inline real(const Matrix& v)
    {
	return real_view<Matrix>(v);
    }
}

} // namespace mtl

#endif // MTL_REAL_INCLUDE


/**
 * Copyright (c) 2022 <Daumantas Kavolis>
 *
 * 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.
 */

#pragma once

#include <type_traits>
#include <utility>

#include "config.hpp"

MSVC_WARNING_DISABLE(4619)
#include <boost/range/iterator.hpp>
MSVC_WARNING_POP()

namespace poly {

namespace detail {
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define POLY_MAKE_OPERATOR_MEMBER_DETECTOR(U, name) \
  template <typename T, typename = int>             \
  struct has_##name##_member : std::false_type {};  \
  template <typename T>                             \
  struct has_##name##_member<T, decltype((void)&T::U, 0)> : std::true_type {}

// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define POLY_MAKE_MEMBER_DETECTOR(U) POLY_MAKE_OPERATOR_MEMBER_DETECTOR(U, U)

POLY_MAKE_MEMBER_DETECTOR(eval);
POLY_MAKE_MEMBER_DETECTOR(prime);
POLY_MAKE_MEMBER_DETECTOR(zeros);
POLY_MAKE_MEMBER_DETECTOR(next);
POLY_MAKE_MEMBER_DETECTOR(weights);
POLY_MAKE_MEMBER_DETECTOR(abscissa);

template <typename T, typename = int>
struct is_trivial : std::false_type {};

template <typename T>
struct is_trivial<T, decltype((void)T::is_trivial, 0)> : std::bool_constant<T::is_trivial> {};

template <typename T, typename = int>
struct is_orthonormal : std::false_type {};

template <typename T>
struct is_orthonormal<T, decltype((void)T::is_orthonormal, 0)>
    : std::bool_constant<T::is_orthonormal> {};

template <typename T, typename = int>
struct is_orthogonal_impl : std::false_type {};

template <typename T>
struct is_orthogonal_impl<T, decltype((void)T::is_orthogonal, 0)>
    : std::bool_constant<T::is_orthogonal> {};

// orthonormal implies orthogonal
template <typename T>
struct is_orthogonal : std::disjunction<is_orthonormal<T>, is_orthogonal_impl<T>> {};

template <typename T, typename = void>
struct is_range : std::false_type {};

template <typename T>
struct is_range<T, std::void_t<typename boost::range_iterator<T>::type>> : std::true_type {};

template <typename T>
struct is_range<T const, std::void_t<typename boost::range_iterator<T>::type>> : std::true_type {};

}  // namespace detail

template <typename Impl>
struct PolynomialTraits {
  using Real = typename Impl::Real;
  using Storage = typename Impl::Storage;
  using OrderType = typename Impl::OrderType;

  // default member checker
  constexpr static inline bool has_eval = detail::has_eval_member<Impl>::value;
  constexpr static inline bool has_prime = detail::has_prime_member<Impl>::value;
  constexpr static inline bool has_zeros = detail::has_zeros_member<Impl>::value;
  constexpr static inline bool has_next = detail::has_next_member<Impl>::value;

  // quadrature
  constexpr static inline bool has_weights = detail::has_weights_member<Impl>::value;
  constexpr static inline bool has_abscissa = detail::has_abscissa_member<Impl>::value;
  constexpr static inline bool has_quadrature = has_weights && has_abscissa;

  // additional
  constexpr static inline bool is_trivial = detail::is_trivial<Impl>::value;
  constexpr static inline bool is_orthonormal = detail::is_orthonormal<Impl>::value;
  constexpr static inline bool is_orthogonal = detail::is_orthogonal<Impl>::value;
};
}  // namespace poly


//==============================================================================
//         Copyright 2003 - 2014   LASMEA UMR 6602 CNRS/UBP
//         Copyright 2009 - 2014   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
// cover for functor pow in simd mode
#include <nt2/exponential/include/functions/pow.hpp>
#include <boost/simd/sdk/simd/io.hpp>
#include <boost/simd/sdk/simd/native.hpp>
#include <cmath>
#include <iostream>
#include <nt2/sdk/meta/as_integer.hpp>
#include <nt2/sdk/unit/args.hpp>
#include <nt2/sdk/unit/module.hpp>
#include <nt2/sdk/unit/tests/cover.hpp>
#include <vector>
extern "C" { long double cephes_powl(long double,long double); }

NT2_TEST_CASE_TPL(pow_1,  NT2_SIMD_REAL_TYPES)
{
  using boost::simd::native;
  typedef BOOST_SIMD_DEFAULT_EXTENSION  ext_t;
  typedef native<T,ext_t>                  vT;

  using nt2::unit::args;
  const std::size_t NR = args("samples", NT2_NB_RANDOM_TEST);
  const double ulpd = args("ulpd",  12.5);

  typedef typename nt2::meta::as_integer<vT>::type ivT;
  typedef typename nt2::meta::as_integer<T>::type iT;
  const T min2_0 = args("min2_0", T(0));
  const T max2_0 = args("max2_0", T(100));
  std::cout << "Argument samples #0 chosen in range: [" << min2_0 << ",  " << max2_0 << "]" << std::endl;
  NT2_CREATE_BUF(a0,T, NR, min2_0, max2_0);
  const iT min2_1 = args("min2_1", iT(-10));
  const iT max2_1 = args("max2_1", iT(10));
  std::cout << "Argument samples #1 chosen in range: [" << min2_1 << ",  " << max2_1 << "]" << std::endl;
  NT2_CREATE_BUF(a1,iT, NR, min2_1, max2_1);

  std::vector<T> ref(NR);
  for(std::size_t i=0; i!=NR; ++i)
    ref[i] = T(::cephes_powl(a0[i],a1[i]));

  NT2_COVER_ULP_EQUAL(nt2::tag::pow_, ((vT, a0))((ivT, a1)), ref, ulpd);

}

NT2_TEST_CASE_TPL(pow_2,  NT2_SIMD_REAL_TYPES)
{
  using boost::simd::native;
  typedef BOOST_SIMD_DEFAULT_EXTENSION  ext_t;
  typedef native<T,ext_t>                  vT;

  using nt2::unit::args;
  const std::size_t NR = args("samples", NT2_NB_RANDOM_TEST);
  const double ulpd = args("ulpd", 2);

  typedef typename nt2::meta::as_integer<T>::type iT;
  typedef typename nt2::meta::as_integer<vT>::type viT;
  const T min1_0 = args("min1_0", T(0));
  const T max1_0 = args("max1_0", T(100));
  std::cout << "Argument samples #0 chosen in range: [" << min1_0 << ",  " << max1_0 << "]" << std::endl;
  NT2_CREATE_BUF(a0,T, NR, min1_0, max1_0);
  const iT min1_1 = args("min1_1", iT(-10));
  const iT max1_1 = args("max1_1", iT(10));
  std::cout << "Argument samples #1 chosen in range: [" << min1_1 << ",  " << max1_1 << "]" << std::endl;
  NT2_CREATE_BUF(a1,iT, NR, min1_1, max1_1);

  std::vector<T> ref(NR);
  for(std::size_t i=0; i!=NR; ++i)
    ref[i] = ::cephes_powl(a0[i],(long double)a1[i]);

  NT2_COVER_ULP_EQUAL(nt2::tag::pow_, ((vT, a0))((viT, a1)), ref, ulpd);
}