#include "cryptonote_config.h"
#include "common/gyuanx.h"
#include "epee/int-util.h"
#include <limits>
#include <vector>
#include <boost/lexical_cast.hpp>
#include <cfenv>

#include "gnode_rules.h"

namespace gnodes {

// TODO(gyuanx): Move to gyuanx_economy, this will also need access to gyuanx::exp2
uint64_t get_staking_requirement(cryptonote::network_type m_nettype, uint64_t height, uint8_t hf_version)
{
  if (m_nettype == cryptonote::TESTNET || m_nettype == cryptonote::FAKECHAIN)
      return COIN * 100;

  // For devnet we use the 10% of mainnet requirement at height (650k + H) so that we follow
  // (proportionally) whatever staking changes happen on mainnet.  (The 650k is because devnet
  // launched at ~600k mainnet height, so this puts it a little ahead).
  if (m_nettype == cryptonote::DEVNET)
      return get_staking_requirement(cryptonote::MAINNET, 600000 + height, hf_version) / 10;

  if (hf_version >= cryptonote::network_version_16_pulse)
    return 1000'000000000000;

  if (hf_version >= cryptonote::network_version_13_enforce_checkpoints)
  {
    // TODO: after HF16 we can remove excess elements here: we need to keep the first one higher
    // than the HF16 fork height, but can delete everything above that (which probably will mean
    // removing 688244 and above).
    constexpr int64_t heights[] = {
        85,
        90,
        95,
    };

    constexpr int64_t lsr[] = {
        204'380815527,
        193'319724305,
        200'564443912,
    };

    assert(static_cast<int64_t>(height) >= heights[0]);
    constexpr uint64_t LAST_HEIGHT      = heights[gyuanx::array_count(heights) - 1];
    constexpr uint64_t LAST_REQUIREMENT = lsr    [gyuanx::array_count(lsr) - 1];
    if (height >= LAST_HEIGHT)
        return LAST_REQUIREMENT;

    size_t i = 0;
    for (size_t index = 1; index < gyuanx::array_count(heights); index++)
    {
      if (heights[index] > static_cast<int64_t>(height))
      {
        i = (index - 1);
        break;
      }
    }

    int64_t H      = height;
    int64_t result = lsr[i] + (H - heights[i]) * ((lsr[i + 1] - lsr[i]) / (heights[i + 1] - heights[i]));
    return static_cast<uint64_t>(result);
  }

  uint64_t hardfork_height = 101;
  if (height < hardfork_height) height = hardfork_height;

  uint64_t height_adjusted = height - hardfork_height;
  uint64_t base = 0, variable = 0;
  std::fesetround(FE_TONEAREST);
  if (hf_version >= cryptonote::network_version_11_infinite_staking)
  {
    base     = 15000 * COIN;
    variable = (25007.0 * COIN) / gyuanx::exp2(height_adjusted/129600.0);
  }
  else
  {
    base      = 10000 * COIN;
    variable  = (35000.0 * COIN) / gyuanx::exp2(height_adjusted/129600.0);
  }

  uint64_t result = base + variable;
  return result;
}

uint64_t portions_to_amount(uint64_t portions, uint64_t staking_requirement)
{
  uint64_t hi, lo, resulthi, resultlo;
  lo = mul128(staking_requirement, portions, &hi);
  div128_64(hi, lo, STAKING_PORTIONS, &resulthi, &resultlo);
  return resultlo;
}

bool check_gnode_portions(uint8_t hf_version, const std::vector<uint64_t>& portions)
{
  if (portions.size() > MAX_NUMBER_OF_CONTRIBUTORS) return false;

  uint64_t reserved = 0;
  for (auto i = 0u; i < portions.size(); ++i)
  {
    const uint64_t min_portions = get_min_node_contribution(hf_version, STAKING_PORTIONS, reserved, i);
    if (portions[i] < min_portions) return false;
    reserved += portions[i];
  }

  return reserved <= STAKING_PORTIONS;
}

crypto::hash generate_request_stake_unlock_hash(uint32_t nonce)
{
  crypto::hash result   = {};
  char const *nonce_ptr = (char *)&nonce;
  char *hash_ptr        = result.data;
  static_assert(sizeof(result) % sizeof(nonce) == 0, "The nonce should be evenly divisible into the hash");
  for (size_t i = 0; i < sizeof(result) / sizeof(nonce); ++i)
  {
    memcpy(hash_ptr, nonce_ptr, sizeof(nonce));
    hash_ptr += sizeof(nonce);
  }

  assert(hash_ptr == (char *)result.data + sizeof(result));
  return result;
}

uint64_t get_locked_key_image_unlock_height(cryptonote::network_type nettype, uint64_t node_register_height, uint64_t curr_height)
{
  uint64_t blocks_to_lock = staking_num_lock_blocks(nettype);
  uint64_t result         = curr_height + (blocks_to_lock / 2);
  return result;
}

static uint64_t get_min_node_contribution_pre_v11(uint64_t staking_requirement, uint64_t total_reserved)
{
  return std::min(staking_requirement - total_reserved, staking_requirement / MAX_NUMBER_OF_CONTRIBUTORS);
}

uint64_t get_max_node_contribution(uint8_t version, uint64_t staking_requirement, uint64_t total_reserved)
{
  if (version >= cryptonote::network_version_16_pulse)
    return (staking_requirement - total_reserved) * config::MAXIMUM_ACCEPTABLE_STAKE::num
      / config::MAXIMUM_ACCEPTABLE_STAKE::den;
  return std::numeric_limits<uint64_t>::max();
}

uint64_t get_min_node_contribution(uint8_t version, uint64_t staking_requirement, uint64_t total_reserved, size_t num_contributions)
{
  if (version < cryptonote::network_version_11_infinite_staking)
    return get_min_node_contribution_pre_v11(staking_requirement, total_reserved);

  const uint64_t needed = staking_requirement - total_reserved;
  assert(MAX_NUMBER_OF_CONTRIBUTORS > num_contributions);
  if (MAX_NUMBER_OF_CONTRIBUTORS <= num_contributions) return UINT64_MAX;

  const size_t num_contributions_remaining_avail = MAX_NUMBER_OF_CONTRIBUTORS - num_contributions;
  return needed / num_contributions_remaining_avail;
}

uint64_t get_min_node_contribution_in_portions(uint8_t version, uint64_t staking_requirement, uint64_t total_reserved, size_t num_contributions)
{
  uint64_t atomic_amount = get_min_node_contribution(version, staking_requirement, total_reserved, num_contributions);
  uint64_t result        = (atomic_amount == UINT64_MAX) ? UINT64_MAX : (get_portions_to_make_amount(staking_requirement, atomic_amount));
  return result;
}

uint64_t get_portions_to_make_amount(uint64_t staking_requirement, uint64_t amount, uint64_t max_portions)
{
  uint64_t lo, hi, resulthi, resultlo;
  lo = mul128(amount, max_portions, &hi);
  if (lo > UINT64_MAX - (staking_requirement - 1))
    hi++;
  lo += staking_requirement-1;
  div128_64(hi, lo, staking_requirement, &resulthi, &resultlo);
  return resultlo;
}

static bool get_portions_from_percent(double cur_percent, uint64_t& portions) {
  if(cur_percent < 0.0 || cur_percent > 100.0) return false;

  // Fix for truncation issue when operator cut = 100 for a pool Service Node.
  if (cur_percent == 100.0)
  {
    portions = STAKING_PORTIONS;
  }
  else
  {
    portions = (cur_percent / 100.0) * (double)STAKING_PORTIONS;
  }

  return true;
}

bool get_portions_from_percent_str(std::string cut_str, uint64_t& portions) {

  if(!cut_str.empty() && cut_str.back() == '%')
  {
    cut_str.pop_back();
  }

  double cut_percent;
  try
  {
    cut_percent = boost::lexical_cast<double>(cut_str);
  }
  catch(...)
  {
    return false;
  }

  return get_portions_from_percent(cut_percent, portions);
}

} // namespace gnodes


//***************************************************************************
//* Copyright (c) 2015 Saint Petersburg State University
//* Copyright (c) 2011-2014 Saint Petersburg Academic University
//* All Rights Reserved
//* See file LICENSE for details.
//***************************************************************************

#include "kmer_coverage_model.hpp"

#include "utils/logger/logger.hpp"
#include "utils/verify.hpp"
#include "math/xmath.h"
#include "math/smooth.hpp"

#include <boost/math/special_functions/zeta.hpp>
#include <boost/math/distributions/normal.hpp>
#include <boost/math/distributions/skew_normal.hpp>
#include <boost/math/distributions/geometric.hpp>
#include <boost/math/distributions/pareto.hpp>

#include <nlopt/nlopt.hpp>

#include <vector>

#include <cstring>
#include <cstdint>
#include <cstddef>
#include <cmath>

namespace coverage_model {

using std::isfinite;

static const size_t MaxCopy = 10;

static double dzeta(double x, double p) {
    return pow(x, -p - 1) / boost::math::zeta(p + 1);
}

static double perr(size_t i, double scale, double shape) {
    return pow((1 + shape * ((double) (i - 1)) / scale), -1.0 / shape) -
           pow((1 + shape * ((double) i) / scale), -1.0 / shape);
}

static double pgood(size_t i, double zp, double u, double sd, double shape,
                    double* mixprobs = NULL) {
    double res = 0;

    for (unsigned copy = 0; copy < MaxCopy; ++copy) {
        boost::math::skew_normal snormal((copy + 1) * u, sd * sqrt(copy + 1), shape);
        // res += (mixprobs ? mixprobs[copy] : dzeta(copy + 1, zp)) * (boost::math::cdf(snormal, i + 1) - boost::math::cdf(snormal, i));
        res += (mixprobs ? mixprobs[copy] : dzeta(copy + 1, zp)) * boost::math::pdf(snormal, i);
    }

    return res;
}

class CovModelLogLike {
    const std::vector<size_t>& cov;

public:
    CovModelLogLike(const std::vector<size_t>& cov)
            : cov(cov) {}

    int getN() const { return 7; };

private:

    double eval_(const double* x) const {
        double zp = x[0], p = x[1], shape = x[2], u = x[3], sd = x[4], scale = x[5], shape2 = x[6];

        if (zp <= 1 || shape <= 0 || sd <= 0 || p < 1e-9 || p > 1 - 1e-9 || u <= 0 || scale <= 0 ||
            !isfinite(zp) || !isfinite(shape) || !isfinite(sd) || !isfinite(p) || !isfinite(u) ||
            !isfinite(scale) || !isfinite(shape2))
            return +std::numeric_limits<double>::infinity();

        std::vector<double> kmer_probs(cov.size());

        // Error
        for (size_t i = 0; i < kmer_probs.size(); ++i)
            kmer_probs[i] += p * perr(i + 1, scale, shape);

        // Good
        for (size_t i = 0; i < kmer_probs.size(); ++i)
            kmer_probs[i] += (1 - p) * pgood(i + 1, zp, u, sd, shape2);

        double res = 0;
        for (size_t i = 0; i < kmer_probs.size(); ++i)
            res += (double) (cov[i]) * log(kmer_probs[i]);

        return -res;
    }
};

struct CovModelLogLikeEMData {
    const std::vector<size_t>& cov;
    const std::vector<double>& z;
};

static double CovModelLogLikeEM(unsigned, const double* x, double*, void* data) {
    double zp = x[0], shape = x[1], u = x[2], sd = x[3], scale = x[4], shape2 = x[5];

    // INFO("Entry: " << x[0] << " " << x[1] << " " << x[2] << " " << x[3] << " " << x[4]);

    if (zp <= 1 || shape <= 0 || sd <= 0 || u <= 0 || scale <= 0 ||
        !isfinite(zp) || !isfinite(shape) || !isfinite(sd) || !isfinite(u) ||
        !isfinite(scale) || !isfinite(shape2))
        return -std::numeric_limits<double>::infinity();

    const std::vector<size_t>& cov = static_cast<CovModelLogLikeEMData*>(data)->cov;
    const std::vector<double>& z = static_cast<CovModelLogLikeEMData*>(data)->z;

    std::vector<double> kmer_probs(cov.size(), 0);

    // Error
    for (size_t i = 0; i < kmer_probs.size(); ++i) {
        if (cov[i] == 0)
            continue;

        kmer_probs[i] += z[i] * log(perr(i + 1, scale, shape));
    }

    // Good
    // Pre-compute mixing probabilities
    std::vector<double> mixprobs(MaxCopy, 0);
    for (unsigned copy = 0; copy < MaxCopy; ++copy)
        mixprobs[copy] = dzeta(copy + 1, zp);

    // Compute the density
    for (size_t i = 0; i < kmer_probs.size(); ++i) {
        if (cov[i] == 0)
            continue;

        double val = log(pgood(i + 1, zp, u, sd, shape2, &mixprobs[0]));
        if (!isfinite(val))
            val = -1000.0;
        kmer_probs[i] += (1 - z[i]) * val;
    }

    double res = 0;
    for (size_t i = 0; i < kmer_probs.size(); ++i)
        res += (double) (cov[i]) * kmer_probs[i];

    // INFO("f: " << res);
    return res;
}

static std::vector<double> EStep(const std::vector<double>& x,
                                 double p, size_t N) {
    double zp = x[0], shape = x[1], u = x[2], sd = x[3], scale = x[4], shape2 = x[5];

    std::vector<double> res(N);
    for (size_t i = 0; i < N; ++i) {
        double pe = p * perr(i + 1, scale, shape);
        res[i] = pe / (pe + (1 - p) * pgood(i + 1, zp, u, sd, shape2));
        if (!isfinite(res[i]))
            res[i] = 1.0;
    }

    return res;
}

// Estimate the coverage mean by finding the max past the
// first valley.
size_t KMerCoverageModel::EstimateValley() const {
    // Smooth the histogram
    std::vector<size_t> scov;
    math::Smooth3RS3R(scov, cov_);

    size_t Valley = scov[0];

    // Start finding the valley
    size_t Idx = 1;
    while (scov[Idx] < Valley && Idx < scov.size()) {
        Valley = scov[Idx];
        Idx += 1;
    }
    Idx -= 1;

    INFO("Kmer coverage valley at: " << Idx);

    return Idx;
}

void KMerCoverageModel::Fit() {
    CHECK_FATAL_ERROR(cov_.size() > 10, "Invalid kmer coverage histogram, make sure that the coverage is indeed uniform");

    // Find the minimal coverage point using smoothed histogram.
    Valley_ = EstimateValley();

    // First estimate of coverage is the first maximum after the valley.
    MaxCov_ = Valley_ + 1;
    size_t MaxHist = cov_[MaxCov_];
    for (size_t i = Valley_ + 1; i < cov_.size(); ++i) {
        if (cov_[i] > MaxHist) {
            MaxHist = cov_[i];
            MaxCov_ = i;
        }
    }
    INFO("K-mer histogram maximum: " << MaxCov_);

    // Refine the estimate via median
    size_t AfterValley = 0, SecondValley = std::min(2 * MaxCov_ - Valley_, cov_.size());
    for (size_t i = Valley_ + 1; i < SecondValley; ++i)
        AfterValley += cov_[i];

    size_t ccov = 0;
    for (size_t i = Valley_ + 1; i < SecondValley; ++i) {
        if (ccov > AfterValley / 2) {
            MaxCov_ = std::max(i, MaxCov_);
            break;
        }
        ccov += cov_[i];
    }

    if (MaxCov_ - Valley_ < 3)
        WARN("Too many erroneous kmers, the estimates might be unreliable");

    std::vector<size_t> mvals(1 + MaxCov_ - Valley_);
    mvals[0] = cov_[MaxCov_];
    size_t tmadcov = mvals[0];
    for (size_t i = 1; i < std::min(MaxCov_ - Valley_, cov_.size() - MaxCov_); ++i) {
        mvals[i] = cov_[MaxCov_ + i] + cov_[MaxCov_ - i];
        tmadcov += mvals[i];
    }
    size_t madcov = 0;
    double CovSd = sqrt((double) (5 * MaxCov_));
    for (size_t i = 0; i < MaxCov_ - Valley_; ++i) {
        if (madcov > tmadcov / 2) {
            CovSd = (double) i;
            break;
        }
        madcov += mvals[i];
    }
    CovSd *= 1.4826;
    INFO("Estimated median coverage: " << MaxCov_ << ". Coverage mad: " << CovSd);

    // Estimate error probability as ratio of kmers before the valley.
    size_t BeforeValley = 0, Total = 0;
    double ErrorProb = 0;
    for (size_t i = 0; i < cov_.size(); ++i) {
        if (i <= Valley_)
            BeforeValley += cov_[i];
        Total += cov_[i];
    }
    ErrorProb = (double) BeforeValley / (double) Total;
    // Allow some erroneous / good kmers.
    ErrorProb = std::min(1 - 1e-3, ErrorProb);
    ErrorProb = std::max(1e-3, ErrorProb);

    TRACE("Total: " << Total << ". Before: " << BeforeValley);
    TRACE("p: " << ErrorProb);

    std::vector<double> x = {3.0, 3.0, (double) MaxCov_, CovSd, 1.0, 0.0},
        lb = {0.0, 0.0, 0.0, (double) (MaxCov_ - Valley_), 0.0, -6.0},
        ub = {2000.0, 2000.0, (double) (2 * MaxCov_), (double) SecondValley, 2000.0, 6.0};

    INFO("Fitting coverage model");
    // Ensure that there will be at least 2 iterations.
    double PrevErrProb = 2;
    const double ErrProbThr = 1e-8;
    auto GoodCov = cov_;
    GoodCov.resize(std::min(cov_.size(), 5 * MaxCopy * MaxCov_ / 4));
    converged_ = true;
    unsigned it = 1;
    while (fabs(PrevErrProb - ErrorProb) > ErrProbThr) {
        // Recalculate the vector of posterior error probabilities
        std::vector<double> z = EStep(x, ErrorProb, GoodCov.size());

        // Recalculate the probability of error
        PrevErrProb = ErrorProb;
        ErrorProb = 0;
        for (size_t i = 0; i < GoodCov.size(); ++i)
            ErrorProb += z[i] * (double) GoodCov[i];
        ErrorProb /= (double) Total;

        bool LastIter = fabs(PrevErrProb - ErrorProb) <= ErrProbThr;

        nlopt::opt opt(nlopt::LN_NELDERMEAD, 6);
        CovModelLogLikeEMData data = {GoodCov, z};
        opt.set_max_objective(CovModelLogLikeEM, &data);
        if (!LastIter)
            opt.set_maxeval(5 * 6 * it);
        opt.set_xtol_rel(1e-8);
        opt.set_ftol_rel(1e-8);

        double fMin;
        nlopt::result Results = nlopt::FAILURE;
        try {
            Results = opt.optimize(x, fMin);
        } catch (nlopt::roundoff_limited&) {
        }

        VERBOSE_POWER_T2(it, 1, "... iteration " << it);
        TRACE("Results: ");
        TRACE("Converged: " << Results << " " << "F: " << fMin);

        double zp = x[0], shape = x[1], u = x[2], sd = x[3], scale = x[4], shape2 = x[5];
        TRACE("zp: " << zp << " p: " << ErrorProb << " shape: " << shape << " u: " << u << " sd: " << sd <<
                     " scale: " << scale << " shape2: " << shape2);

        it += 1;
    }

    double delta = x[5] / sqrt(1 + x[5] * x[5]);
    mean_coverage_ = x[2] + x[3] * delta * sqrt(2 / M_PI);
    sd_coverage_ = x[3] * sqrt(1 - 2 * delta * delta / M_PI);
    INFO("Fitted mean coverage: " << mean_coverage_ << ". Fitted coverage std. dev: " << sd_coverage_);

    // Now let us check whether we have sane results
    for (size_t i = 0; i < x.size(); ++i)
        if (!isfinite(x[i])) {
            converged_ = false;
            break;
        }

    if (!isfinite(ErrorProb))
        converged_ = false;

    // See, if we can deduce proper threshold

    // First, check whether initial estimate of Valley was sane.
    ErrorThreshold_ = 0;
    if (converged_ && Valley_ > x[2] && x[2] > 2) {
        Valley_ = (size_t) math::round(x[2] / 2.0);
        WARN("Valley value was estimated improperly, reset to " << Valley_);
    }

    // If the model converged, then use it to estimate the thresholds.
    if (converged_) {
        std::vector<double> z = EStep(x, ErrorProb, GoodCov.size());

        INFO("Probability of erroneous kmer at valley: " << z[Valley_]);
        converged_ = false;
        for (size_t i = 0; i < z.size(); ++i)
            if (z[i] > strong_probability_threshold_) //0.999
                LowThreshold_ = std::min(i + 1, Valley_);
            else if (z[i] < probability_threshold_) {//0.05?
                ErrorThreshold_ = std::max(i + 1, Valley_);
                converged_ = true;
                break;
            }

#if 0
        for (size_t i = 0; i < z.size(); ++i) {
            double zp = x[0], shape = x[1], u = x[2], sd = x[3], scale = x[4], shape2 = x[5];
            double pe = ErrorProb * perr(i + 1, scale, shape);
            double pg = (1 - ErrorProb) * pgood(i + 1, zp, u, sd, shape2);

            fprintf(stderr, "%e %e %e %e\n", pe, pg, z[i], perr(i + 1, scale, shape));
        }
#endif
    }

    // See, if we have sane ErrorThreshold_ and go down to something convervative, if not.
    if (converged_) {
        INFO("Preliminary threshold calculated as: " << ErrorThreshold_);
        ErrorThreshold_ = (Valley_ < mean_coverage_ ?
                           std::min(Valley_ + (size_t) (mean_coverage_ - (double) Valley_) / 2, ErrorThreshold_) :
                           Valley_);
        INFO("Threshold adjusted to: " << ErrorThreshold_);
    } else {
        ErrorThreshold_ = Valley_;
        LowThreshold_ = 1;
        WARN("Failed to determine erroneous kmer threshold. Threshold set to: " << ErrorThreshold_);
    }

    // Now the bonus: estimate the genome size!
    GenomeSize_ = 0;
    for (size_t i = ErrorThreshold_ - 1; i < GoodCov.size(); ++i)
        GenomeSize_ += GoodCov[i];
    GenomeSize_ /= 2;

    INFO("Estimated genome size (ignoring repeats): " << GenomeSize_);
}

}


//
// Created by Alex Beccaro on 18/01/18.
//

#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#include "../../src/problems/1-50/15/problem15.hpp"

BOOST_AUTO_TEST_SUITE( Problem15 )

    BOOST_AUTO_TEST_CASE( Example ) {
        auto res = problems::problem15::solve(2, 2);
        BOOST_CHECK_EQUAL(res, 6);
    }

    BOOST_AUTO_TEST_CASE( Solution ) {
        auto res = problems::problem15::solve();
        BOOST_CHECK_EQUAL(res, 137846528820);
    }

BOOST_AUTO_TEST_SUITE_END()


#ifndef TVMTL_MATRIX_UTILS_HPP
#define TVMTL_MATRIX_UTILS_HPP

#include <cmath>
#include <complex>
#include <iostream>

#include <Eigen/Dense>
#include <unsupported/Eigen/MatrixFunctions>
#include <unsupported/Eigen/KroneckerProduct>

namespace tvmtl {

template <typename MatrixType>
    void SolveTriangularSylvester(const MatrixType& A, const MatrixType& B, const MatrixType& C, MatrixType& result)
    {
/*	Eigen::eigen_assert(A.rows() == A.cols());
	Eigen::eigen_assert(A.isUpperTriangular());
	Eigen::eigen_assert(B.rows() == B.cols());
	Eigen::eigen_assert(B.isUpperTriangular());
	Eigen::eigen_assert(C.rows() == A.rows());
	Eigen::eigen_assert(C.cols() == B.rows());
  */
      typedef typename MatrixType::Index Index;
      typedef typename MatrixType::Scalar Scalar;

      Index m = A.rows();
      Index n = B.rows();
      MatrixType X(m, n);

      for (Index i = m - 1; i >= 0; --i) {
          for (Index j = 0; j < n; ++j) {
	        // Compute AX = \sum_{k=i+1}^m A_{ik} X_{kj}
		Scalar AX;
		if (i == m - 1) {
		    AX = 0;
		}
		else {
		    Eigen::Matrix<Scalar,1,1> AXmatrix = A.row(i).tail(m-1-i) * X.col(j).tail(m-1-i);
		    AX = AXmatrix(0,0);
		}

		// Compute XB = \sum_{k=1}^{j-1} X_{ik} B_{kj}
		Scalar XB;
		if (j == 0) {
		    XB = 0;
		}
		else {
		    Eigen::Matrix<Scalar,1,1> XBmatrix = X.row(i).head(j) * B.col(j).head(j);
		    XB = XBmatrix(0,0);
		}

		X(i,j) = (C(i,j) - AX - XB) / (A(i,i) + B(j,j));
	}
    }
result = X;
}

template <typename DerivedX, typename DerivedY, typename DerivedZ>
void MatrixRootFrechetDerivative(const Eigen::MatrixBase<DerivedX>& X, const Eigen::MatrixBase<DerivedY>& E, Eigen::MatrixBase<DerivedZ>& result){

    // Matrix Parameters
    typedef Eigen::internal::traits<DerivedX> Traits;
    typedef typename Traits::Scalar Scalar;
    static const int Rows = Traits::RowsAtCompileTime, Cols = Traits::ColsAtCompileTime;
    static const int Options = DerivedX::Options;
    static const int MaxRows = Traits::MaxRowsAtCompileTime, MaxCols = Traits::MaxColsAtCompileTime;

    // Switch to complex arithmetic
    typedef std::complex<Scalar> ComplexScalar;
    typedef Eigen::Matrix<ComplexScalar, Rows, Cols, Options, MaxRows, MaxCols> ComplexMatrix;

    ComplexMatrix CX = X.template cast<ComplexScalar>();
    ComplexMatrix CE = E.template cast<ComplexScalar>();
    ComplexMatrix CResult;

    // Complex Schur Decomposition
    const Eigen::ComplexSchur<ComplexMatrix> SchurOfX(CX);
    ComplexMatrix T = SchurOfX.matrixT();
    ComplexMatrix U = SchurOfX.matrixU();

    ComplexMatrix sqrtT;
    CE = U.adjoint()*CE*U;

    Eigen::MatrixSquareRootTriangular<ComplexMatrix>(T).compute(sqrtT);
    SolveTriangularSylvester(sqrtT, sqrtT, CE, CResult);

    CResult = U * CResult * U.adjoint();

    result = CResult.real();
}

template <typename DerivedX, typename DerivedY, typename DerivedZ>
void MatrixLogarithmFrechetDerivative(const Eigen::MatrixBase<DerivedX>& X, const Eigen::MatrixBase<DerivedY>& E, Eigen::MatrixBase<DerivedZ>& result){

    // Matrix Parameters
    typedef Eigen::internal::traits<DerivedX> Traits;
    typedef typename Traits::Scalar Scalar;
    static const int Rows = Traits::RowsAtCompileTime, Cols = Traits::ColsAtCompileTime;
    static const int Options = DerivedX::Options;
    static const int MaxRows = Traits::MaxRowsAtCompileTime, MaxCols = Traits::MaxColsAtCompileTime;

    // Switch to complex arithmetic
    typedef std::complex<Scalar> ComplexScalar;
    typedef Eigen::Matrix<ComplexScalar, Rows, Cols, Options, MaxRows, MaxCols> ComplexMatrix;

    ComplexMatrix CX = X.template cast<ComplexScalar>();
    ComplexMatrix CE = E.template cast<ComplexScalar>();
    ComplexMatrix CResult;

    // Order of the Pade approximant
    // If this is changed, we also need new weights and nodes
    const int m = 7;

    // Complex Schur Decomposition
    const Eigen::ComplexSchur<ComplexMatrix> SchurOfX(CX);
    ComplexMatrix T = SchurOfX.matrixT();
    ComplexMatrix U = SchurOfX.matrixU();

    //Compute the number of square roots
    int s = 0;
    const int smax = 20;
    const double theta7 = 2.88e-1;
    double rho = theta7 + 1.0;

    Eigen::Matrix<ComplexScalar, Rows, 1> D;
    D = T.diagonal();

    #ifdef TVMTL_MATRIX_UTILS_DEBUG_VERBOSE
	std::cout << "Diagonal of Schur Decomposition: \n" << D << std::endl;
    #endif

    while(rho > theta7 && s < smax){
	D=D.cwiseSqrt();
	#ifdef TVMTL_MATRIX_UTILS_DEBUG_VERBOSE
	    std::cout << "D: " << D << std::endl;
	#endif
	++s;
	rho = (D - Eigen::Matrix<ComplexScalar, Rows, 1>::Constant(1)).cwiseAbs().maxCoeff();
	 #ifdef TVMTL_MATRIX_UTILS_DEBUG_VERBOSE
	    std::cout << "rho: " << rho << std::endl;
	 #endif
    }

    #ifdef TVMTL_MATRIX_UTILS_DEBUG
	std::cout << "Number of square roots  for dlog estimation: " << s << std::endl;
    #endif

    ComplexMatrix sqrtT;
    CE = U.adjoint()*CE*U;

    for(int k=0; k<s; k++){
	Eigen::MatrixSquareRootTriangular<ComplexMatrix>(T).compute(sqrtT);
	T=sqrtT;
	SolveTriangularSylvester(T, T, CE, CE);
    }

    const double nodes[]   = { 0.0254460438286207377369051579760744L, 0.1292344072003027800680676133596058L,
            0.2970774243113014165466967939615193L, 0.5000000000000000000000000000000000L,
            0.7029225756886985834533032060384807L, 0.8707655927996972199319323866403942L,
            0.9745539561713792622630948420239256L };
    const double weights[] = { 0.0647424830844348466353057163395410L, 0.1398526957446383339507338857118898L,
              0.1909150252525594724751848877444876L, 0.2089795918367346938775510204081633L,
              0.1909150252525594724751848877444876L, 0.1398526957446383339507338857118898L,
              0.0647424830844348466353057163395410L };

    ComplexMatrix TminusI = T - ComplexMatrix::Identity(T.rows(), T.rows());
    CResult.setZero();

    for (int i = 0; i < m; ++i){
  	ComplexMatrix IplusBetaTm = ComplexMatrix::Identity(T.rows(), T.rows()) + nodes[i] * TminusI;
  	ComplexMatrix X = IplusBetaTm.template triangularView< Eigen::Upper >().solve(CE).transpose();
  	CResult += weights[i] * (IplusBetaTm.template triangularView< Eigen::Upper >().transpose().solve(X)).transpose();
    }

    CResult = std::pow(2.0,s) * U * CResult * U.adjoint();

    result = CResult.real();
}

template <typename DerivedX, typename DerivedY>
void KroneckerDLog(const Eigen::MatrixBase<DerivedX>& X, Eigen::MatrixBase<DerivedY>& Result){

    typedef Eigen::internal::traits<DerivedX> Traits;
    typedef typename Traits::Scalar Scalar;
    static const int Rows = Traits::RowsAtCompileTime;

    DerivedX E, PartialDiff;
    DerivedY R;

    for (int i = 0; i < Rows; i++) {
    	for (int j = 0; j < Rows; j++) {
	    E = DerivedX::Zero();
	    E(i,j) = 1.0;
	    MatrixLogarithmFrechetDerivative(X, E, PartialDiff);
	    PartialDiff.transposeInPlace();
	    R.row(i*Rows+j) = Eigen::Map<Eigen::VectorXd>(PartialDiff.data(), PartialDiff.size());
    	}
    }
    Result = R;
}

template <typename DerivedX, typename DerivedY>
void KroneckerDSqrt2(const Eigen::MatrixBase<DerivedX>& X, Eigen::MatrixBase<DerivedY>& Result){

    typedef Eigen::internal::traits<DerivedX> Traits;
    typedef typename Traits::Scalar Scalar;
    static const int Rows = Traits::RowsAtCompileTime;

    DerivedX E, PartialDiff;
    DerivedY R;

    for (int i = 0; i < Rows; i++) {
    	for (int j = 0; j < Rows; j++) {
	    E = DerivedX::Zero();
	    E(i,j) = 1.0;
	    MatrixRootFrechetDerivative(X, E, PartialDiff);
	    PartialDiff.transposeInPlace();
	    R.row(i*Rows+j) = Eigen::Map<Eigen::VectorXd>(PartialDiff.data(), PartialDiff.size());
    	}
    }
    Result = R;
}

template <typename DerivedX, typename DerivedY>
void KroneckerDSqrt(const Eigen::MatrixBase<DerivedX>& X, Eigen::MatrixBase<DerivedY>& Result){

    DerivedX Xsqrt = X.sqrt();
    DerivedY R = Eigen::kroneckerProduct(DerivedX::Identity(),Xsqrt) + Eigen::kroneckerProduct(Xsqrt.transpose(), DerivedX::Identity());
    Result = R.inverse();
}

} // end namespace tvmtl

#endif


/**
 * Copyright 2021-2022 Huawei Technologies Co., Ltd
 *
 * 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 "plugin/device/cpu/kernel/lstsq_cpu_kernel.h"
#include <Eigen/Dense>
#include "plugin/device/cpu/hal/device/cpu_device_address.h"
#include "kernel/common_utils.h"

namespace mindspore {
namespace kernel {
namespace {
constexpr size_t kLstsqInputsNum = 2;
constexpr size_t kLstsqOutputsNum = 1;
constexpr size_t kXDimNum = 2;
constexpr size_t kADimNum_1 = 1;
constexpr size_t kADimNum_2 = 2;
}  // namespace

void LstsqCpuKernelMod::InitKernel(const CNodePtr &kernel_node) {
  MS_EXCEPTION_IF_NULL(kernel_node);
  input_0_shape_ = AnfAlgo::GetInputDeviceShape(kernel_node, 0);
  input_1_shape_ = AnfAlgo::GetInputDeviceShape(kernel_node, 1);
  if (input_0_shape_.size() != kXDimNum) {
    MS_LOG(EXCEPTION) << "The input x tensor's rank must be 2 for 'Lstsq' Op, but x tensor's rank is "
                      << input_0_shape_.size();
  }
  if (input_1_shape_.size() != kADimNum_2 && input_1_shape_.size() != kADimNum_1) {
    MS_LOG(EXCEPTION) << "The input a tensor's rank must be 2 or 1 for 'Lstsq' Op, but a tensor's rank is "
                      << input_1_shape_.size();
  }
  if (input_0_shape_[0] != input_1_shape_[0]) {
    MS_LOG(EXCEPTION) << "The length of x_dim[0]: " << input_0_shape_[0]
                      << " is not equal to the length of a_dims[0]: " << input_1_shape_[0] << ".";
  }
  dtype_0_ = AnfAlgo::GetInputDeviceDataType(kernel_node, 0);
  dtype_1_ = AnfAlgo::GetInputDeviceDataType(kernel_node, 1);
  if (dtype_0_ != dtype_1_) {
    MS_LOG(EXCEPTION) << "For Lstsq input's dtypes are not the same.";
  }
}

bool LstsqCpuKernelMod::Launch(const std::vector<kernel::AddressPtr> &inputs, const std::vector<kernel::AddressPtr> &,
                               const std::vector<kernel::AddressPtr> &outputs) {
  CHECK_KERNEL_INPUTS_NUM(inputs.size(), kLstsqInputsNum, kernel_name_);
  CHECK_KERNEL_OUTPUTS_NUM(outputs.size(), kLstsqOutputsNum, kernel_name_);
  if (dtype_0_ == kNumberTypeFloat16) {
    LaunchKernel<float, float16>(inputs, outputs);
  } else if (dtype_0_ == kNumberTypeFloat32) {
    LaunchKernel<float, float>(inputs, outputs);
  } else if (dtype_0_ == kNumberTypeFloat64) {
    LaunchKernel<double, double>(inputs, outputs);
  } else {
    MS_LOG(EXCEPTION) << "Unsupported input data type.";
  }
  return true;
}

template <typename T1, typename T2>
void LstsqCpuKernelMod::LaunchKernel(const std::vector<AddressPtr> &inputs, const std::vector<AddressPtr> &outputs) {
  auto input_0_addr = reinterpret_cast<T2 *>(inputs[0]->addr);
  auto input_1_addr = reinterpret_cast<T2 *>(inputs[1]->addr);
  auto output_addr = reinterpret_cast<T2 *>(outputs[0]->addr);
  size_t m = input_0_shape_[0];
  size_t n = input_0_shape_[1];
  size_t k = 0;
  if (input_1_shape_.size() == kADimNum_1) {
    k = 1;
  } else {
    k = input_1_shape_[1];
  }

  typedef Eigen::Matrix<T1, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> MartixXd;
  MartixXd A(m, n);
  MartixXd B(m, k);
  for (size_t i = 0; i < m * n; i++) {
    A.data()[i] = static_cast<T1>(input_0_addr[i]);
  }
  for (size_t i = 0; i < m * k; i++) {
    B.data()[i] = static_cast<T1>(input_1_addr[i]);
  }
  MartixXd result;
  if (m >= n) {
    result = A.colPivHouseholderQr().solve(B);
  } else {
    MartixXd A_Transpose = A.transpose();
    MartixXd temp = A * A_Transpose;
    MartixXd tempI = temp.inverse();
    MartixXd x = A_Transpose * tempI;
    MartixXd output = x * B;
    result = output;
  }
  for (size_t i = 0; i < n; i++)
    for (size_t j = 0; j < k; j++) {
      *(output_addr + i * k + j) = static_cast<T2>(result(i, j));
    }
}

MS_KERNEL_FACTORY_REG(NativeCpuKernelMod, Lstsq, LstsqCpuKernelMod);
}  // namespace kernel
}  // namespace mindspore


#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <vector>
#include <random>
#include <iterator>
#include <Eigen/Dense>
#include <Eigen/Sparse>
#include <Eigen/IterativeLinearSolvers>
#include <Eigen/SparseCholesky>

#include "SVDPackage.h"
#include "Pest.h"
#include "utilities.h"
#include "covariance.h"
#include "FileManager.h"

using namespace std;

//---------------------------------------
//Mat constructors
//---------------------------------------

Mat::Mat(string filename)
{
	string ext = filename.substr(filename.find_last_of(".") + 1);
	pest_utils::upper_ip(ext);
	if (ext == "PST")
		throw runtime_error("Mat::Mat() error: cannot instantiate a Mat with PST");
	else if ((ext == "JCO") || (ext == "JCB"))
		from_binary(filename);
	else if (ext == "MAT")
		from_ascii(filename);
	else
		throw runtime_error("Mat::Mat() error: only .jco/.jcb or .mat\
							 files can be used to instatiate a Mat");

}

Mat::Mat(vector<string> _row_names, vector<string> _col_names,
	Eigen::SparseMatrix<double> _matrix)
{
	row_names = _row_names;
	col_names = _col_names;
	assert(row_names.size() == _matrix.rows());
	assert(col_names.size() == _matrix.cols());
	matrix = _matrix;
	mattype = MatType::SPARSE;
}

Mat::Mat(vector<string> _row_names, vector<string> _col_names,
	Eigen::SparseMatrix<double>* _matrix)
{
	row_names = _row_names;
	col_names = _col_names;
	assert(row_names.size() == _matrix->rows());
	assert(col_names.size() == _matrix->cols());
	matrix = *_matrix;
	mattype = MatType::SPARSE;
}

Mat::Mat(vector<string> _row_names, vector<string> _col_names,
	Eigen::SparseMatrix<double> _matrix,MatType _mattype)
{
	row_names = _row_names;
	col_names = _col_names;
	assert(row_names.size() == _matrix.rows());
	assert(col_names.size() == _matrix.cols());
	matrix = _matrix;
	mattype = _mattype;
}

void Mat::update_sets()
{
	row_set.clear();
	//row_set.emplace(row_names.begin(), row_names.end());
	row_set = set<string>(row_names.begin(), row_names.end());
	col_set.clear();
	//col_set.emplace(col_names.begin(), col_names.end());
	col_set = set<string>(col_names.begin(), col_names.end());
}

const Eigen::SparseMatrix<double>* Mat::e_ptr()
{
	const Eigen::SparseMatrix<double>* ptr = &matrix;
	return ptr;
}

const vector<string>* Mat::rn_ptr()
{
	const vector<string>* ptr = &row_names;
	return ptr;
}

const vector<string>* Mat::cn_ptr()
{
	const vector<string>* ptr = &col_names;
	return ptr;
}

Mat Mat::identity()
{
	Eigen::SparseMatrix<double> i(nrow(), ncol());
	i.setZero();
	i.setIdentity();
	return Mat(*rn_ptr(),*cn_ptr(),i);
}

Mat Mat::zero()
{
	Eigen::SparseMatrix<double> i(nrow(), ncol());
	i.setZero();
	return Mat(*rn_ptr(), *cn_ptr(), i);
}

const Eigen::SparseMatrix<double>* Mat::U_ptr()
{
	if (U.rows() == 0)
	{
		SVD();
	}
	const Eigen::SparseMatrix<double>* ptr = &U;
	return ptr;
}

const Eigen::SparseMatrix<double>* Mat::V_ptr()
{
	if (V.rows() == 0)
	{
		SVD();
	}
	const Eigen::SparseMatrix<double>* ptr = &V;
	return ptr;
}

const Eigen::VectorXd* Mat::s_ptr()
{
	if (s.size() == 0)
	{
		SVD();
	}
	const Eigen::VectorXd* ptr = &s;
	return ptr;
}

Mat Mat::get_U()
{
	if (U.rows() == 0) SVD();
	vector<string> u_col_names;
	stringstream ss;
	for (int i = 0; i < nrow(); i++)
	{
		ss.clear();
		ss.str(string());
		ss << "left_sing_vec_";
		ss << i + 1;
		u_col_names.push_back(ss.str());
	}
	return Mat(row_names, u_col_names, U);
}

Mat Mat::get_V()
{
	if (V.rows() == 0) SVD();
	vector<string> v_col_names;
	stringstream ss;
	for (int i = 0; i < ncol(); i++)
	{
		ss.clear();
		ss.str(string());
		ss << "right_sing_vec_";
		ss << i + 1;
		v_col_names.push_back(ss.str());
	}
	return Mat(col_names, v_col_names, V);
}

Mat Mat::get_s()
{
	if (V.rows() == 0) SVD();
	vector<string> s_names;
	vector<Eigen::Triplet<double>> triplet_list;
	stringstream ss;
	for (int i = 0; i < s.size(); i++)
	{
		ss.clear();
		ss.str(string());
		ss << "sing_val_";
		ss << i + 1;
		s_names.push_back(ss.str());
		triplet_list.push_back(Eigen::Triplet<double>(i, i, s[i]));
	}
	Eigen::SparseMatrix<double> s_mat(s.size(),s.size());
	s_mat.setZero();
	s_mat.setFromTriplets(triplet_list.begin(), triplet_list.end());
	return Mat(s_names, s_names, s_mat);
}

Mat Mat::transpose()
{
	return Mat(col_names, row_names, matrix.transpose());
}

Mat Mat::T()
{
	return Mat(col_names, row_names, matrix.transpose());
}

void Mat::transpose_ip()
{
	if (mattype != MatType::DIAGONAL)
	{
		matrix = matrix.transpose();
		vector<string> temp = row_names;
		row_names = col_names;
		col_names = temp;
	}
}

Mat Mat::inv(bool echo)
{
	Logger* log = new Logger();
	log->set_echo(echo);
	//inv_ip(log);
	Mat new_mat = inv(log);
	delete log;
	return new_mat;
}

Mat Mat::inv(Logger* log)
{
	if (nrow() != ncol()) throw runtime_error("Mat::inv() error: only symmetric positive definite matrices can be inverted with Mat::inv()");
	if (mattype == MatType::DIAGONAL)
	{
		log->log("inverting diagonal matrix in place");
		log->log("extracting diagonal");
		Eigen::VectorXd diag = matrix.diagonal().eval();
		log->log("inverting diagonal");
		log->log("building triplets");
		vector<Eigen::Triplet<double>> triplet_list;
		for (int i = 0; i != diag.size(); ++i)
		{
			triplet_list.push_back(Eigen::Triplet<double>(i, i, 1.0 / diag[i]));
		}
		Eigen::SparseMatrix<double> inv_mat;
		inv_mat.conservativeResize(triplet_list.size(), triplet_list.size());
		inv_mat.setZero();
		log->log("setting matrix from triplets");
		inv_mat.setFromTriplets(triplet_list.begin(), triplet_list.end());
		return Mat(row_names, col_names, inv_mat);
	}
	//Eigen::ConjugateGradient<Eigen::SparseMatrix<double>> solver;
	Eigen::SimplicialLDLT<Eigen::SparseMatrix<double>> solver;
	solver.compute(matrix);
	Eigen::SparseMatrix<double> I(nrow(), nrow());
	I.setIdentity();
	Eigen::SparseMatrix<double> inv_mat = solver.solve(I);
	return Mat(row_names, col_names, inv_mat);
}

void Mat::inv_ip(bool echo)
{
	ofstream flog("Mat.log");
	PerformanceLog pfm(flog);
	inv_ip(pfm);
	return;
}

void Mat::inv_ip(PerformanceLog& pfm)
{
	if (nrow() != ncol()) throw runtime_error("Mat::inv() error: only symmetric positive definite matrices can be inverted with Mat::inv()");
	if (mattype == MatType::DIAGONAL)
	{
		pfm.log_event("inverting diagonal matrix in place");
		Eigen::VectorXd diag = matrix.diagonal().eval();
		vector<Eigen::Triplet<double>> triplet_list;
		for (int i = 0; i != diag.size(); ++i)
		{
			triplet_list.push_back(Eigen::Triplet<double>(i, i, 1.0/diag[i]));
		}
		//log->log("resizeing matrix to size " + triplet_list.size());
		//matrix.conservativeResize(triplet_list.size(),triplet_list.size());
		matrix.setZero();
		matrix.setFromTriplets(triplet_list.begin(),triplet_list.end());
		//Eigen::SparseMatrix<double> inv_mat(triplet_list.size(), triplet_list.size());
		//inv_mat.setZero();
		//inv_mat.setFromTriplets(triplet_list.begin(), triplet_list.end());
		//matrix = inv_mat;
		//cout << "diagonal inv_ip()" << endl;
		/*matrix.resize(triplet_list.size(), triplet_list.size());
		matrix.setZero();
		matrix.setFromTriplets(triplet_list.begin(),triplet_list.end());*/
		return;
	}

	//Eigen::ConjugateGradient<Eigen::SparseMatrix<double>> solver;
	pfm.log_event("inverting non-diagonal matrix in place");
	Eigen::SimplicialLDLT<Eigen::SparseMatrix<double>> solver;
	solver.compute(matrix);
	Eigen::SparseMatrix<double> I(nrow(), nrow());
	I.setIdentity();
	//Eigen::SparseMatrix<double> inv_mat = solver.solve(I);
	//matrix = inv_mat;
	matrix = solver.solve(I);
	//cout << "full inv_ip()" << endl;
	/*matrix.setZero();
	matrix = solver.solve(I);*/

}

void Mat::SVD()
{
	Eigen::JacobiSVD<Eigen::MatrixXd> svd_fac(matrix, Eigen::DecompositionOptions::ComputeFullU |
		Eigen::DecompositionOptions::ComputeFullV);
	s = svd_fac.singularValues();
	U = svd_fac.matrixU().sparseView();
	V = svd_fac.matrixV().sparseView();
}

//---------------------------------------
//Mat operator
//--------------------------------------

ostream& operator<< (ostream &os, Mat mat)
{
	cout << "row names : ";
	for (auto &name : mat.get_row_names())
		cout << name << ',';
	cout << endl << "col names : ";
	for (auto &name : mat.get_col_names())
		cout << name << ',';
	cout << endl;
	cout << *mat.e_ptr();
	return os;
}

//-----------------------------------------
//Mat IO
//-----------------------------------------

void Mat::to_ascii(const string &filename)
{
	ofstream out(filename);
	if (!out.good())
	{
		throw runtime_error("Mat::to_ascii() error: cannot open " + filename + "\
													 to write ASCII matrix");
	}
	out << setw(6) << nrow() << setw(6) << ncol() << setw(6) << icode << endl;
	out << matrix.toDense() << endl;
	if (icode == 1)
	{
		out<< "* row and column names" << endl;
		for (auto &name : row_names)
			out << pest_utils::lower_cp(name) << endl;

	}
	else
	{
		out << "* row names" << endl;
		for (auto &name : row_names)
			out << pest_utils::lower_cp(name) << endl;
		out << "* column names" << endl;
		for (auto &name : col_names)
			out << pest_utils::lower_cp(name) << endl;
	}
	out.close();
}

void Mat::from_file(const string &filename)
{
	stringstream ss;
	string ext = filename.substr(filename.find_last_of(".") + 1);
	pest_utils::upper_ip(ext);
	if ((ext == "JCB") || (ext == "JCO"))
	{
		from_binary(filename);
	}
	else if ((ext == "MAT") || (ext == "COV"))
	{
		from_ascii(filename);
	}
	else if (ext == "CSV")
	{
		from_csv(filename);
	}
	else
	{
		ss << "Mat::from_file() error: unrecognized extension'" << ext << "', should be JCB, JCO, MAT or CSV";
		throw runtime_error(ss.str());
	}

}

void Mat::from_csv(const string &filename)
{
	ifstream csv(filename);
	if (!csv.good())
		throw runtime_error("Mat::from_csv() error: cannot open " + filename + " \
												to read csv matrix");

	//process the header
	//any missing header labels will be marked to ignore those columns later
	string line;
	if (!getline(csv, line))
		throw runtime_error("error reading header (first) line from csv file :");
	pest_utils::strip_ip(line);
	pest_utils::upper_ip(line);
	pest_utils::tokenize(line, col_names, ",", false);
	col_names.erase(col_names.begin()); //drop the index label
	vector<Eigen::Triplet<double>> triplet_list;
	double val;

	//read a csv file to an Ensmeble
	int lcount = 0, irow = 0;
	//vector<vector<double>> vectors;
	vector<string> tokens;
	string row_name;

	while (getline(csv, line))
	{
		pest_utils::strip_ip(line);
		tokens.clear();
		pest_utils::tokenize(line, tokens, ",", false);
		if (tokens[tokens.size() - 1].size() == 0)
			tokens.pop_back();

		try
		{
			pest_utils::convert_ip(tokens[0], row_name);
		}
		catch (exception &e)
		{
			stringstream ss;
			ss << "error converting token '" << tokens[0] << "' to <int> run_id on line " << lcount << ": " << line << endl << e.what();
			throw runtime_error(ss.str());
		}
		tokens.erase(tokens.begin()); //drop the row name
		if (tokens.size() != col_names.size())
		{
			stringstream ss;
			ss << "Matrix.from_csv() error: wrong number of entries on line " << lcount << " , expecting " << col_names.size() << ", found " << tokens.size() << endl;
			throw runtime_error(ss.str());
		}
		row_names.push_back(pest_utils::upper_cp(row_name));
		for (int j = 0; j < col_names.size(); j++)
		{
			try
			{
				val = pest_utils::convert_cp<double>(tokens[j]);
			}
			catch (exception &e)
			{
				stringstream ss;
				ss << "error converting token '" << tokens[j] << "' to double for " << col_names[j] << " on line " << lcount << " : " << e.what();
				throw runtime_error(ss.str());
			}
			if (val != 0.0)
				triplet_list.push_back(Eigen::Triplet<double>(irow, j, val));
		}

	lcount++;
	irow++;
	}
	matrix.resize(row_names.size(), col_names.size());
	matrix.setFromTriplets(triplet_list.begin(), triplet_list.end());

}

void Mat::from_triplets(const vector<string> &_row_names, const vector<string> &_col_names, const vector<Eigen::Triplet<double>> &triplets)
{
	matrix.resize(_row_names.size(), _col_names.size());
	//matrix.setZero();
	matrix.setFromTriplets(triplets.begin(), triplets.end());
	//cout << matrix.cols() << endl;
	row_names = _row_names;
	col_names = _col_names;

}

void Mat::from_ascii(const string &filename)
{
	ifstream in(filename);
	if (!in.good())
		throw runtime_error("Mat::from_ascii() error: cannot open " + filename + " \
												to read ASCII matrix");
	int nrow = -999, ncol = -999;
	if (in >> nrow >> ncol >> icode){}
	else
		throw runtime_error("Mat::from_ascii() error reading nrow ncol icode from first line\
							 of ASCII matrix file: " + filename);

	vector<Eigen::Triplet<double>> triplet_list;
	double val;
	int irow = 0, jcol = 0;
	for (int inode = 0; inode < nrow*ncol;inode++)
	{
		if (in >> val)
		{
			if (val != 0.0)
				triplet_list.push_back(Eigen::Triplet<double>(irow,jcol,val));
			jcol++;
			if (jcol >= ncol)
			{
				irow++;
				jcol = 0;
			}
		}
		else
		{
			string i_str = to_string(inode);
			throw runtime_error("Mat::from_ascii() error reading entry number "+i_str+" from\
								 ASCII matrix file: "+filename);
		}
	}

	string header;
	//read the newline char
	getline(in, header);
	if (!getline(in,header))
		throw runtime_error("Mat::from_ascii() error reading row/col description\
							 line from ASCII matrix file: " + filename);
	pest_utils::upper_ip(header);
	string name;
	if (icode == 1)
	{
		if (nrow != ncol)
			throw runtime_error("Mat::from_ascii() error: nrow != ncol for icode type 1 ASCII matrix file:" + filename);
		if((header.find("ROW") == string::npos) || (header.find("COLUMN") == string::npos))
			throw runtime_error("Mat::from_ascii() error: expecting row and column names header instead\
								 of:" + header + " in ASCII matrix file: " + filename);
		try
		{
			row_names = read_namelist(in, nrow);
		}
		catch (exception &e)
		{
			throw runtime_error("Mat::from_ascii() error reading row/column names from ASCII matrix file: " + filename + "\n" + e.what());
		}
		if ((nrow != row_names.size()) || (ncol != row_names.size()))
			throw runtime_error("Mat::from_ascii() error: number of row/col names does not match matrix dimensions");
		col_names = row_names;
	}
	else
	{
		if(header.find("ROW") == string::npos)
			throw runtime_error("Mat::from_ascii() error: expecting row names header instead of:" + header + " in ASCII matrix file: " + filename);
		try
		{
			row_names = read_namelist(in, nrow);
		}
		catch (exception &e)
		{
			throw runtime_error("Mat::from_ascii() error reading row names from ASCII matrix file: " + filename + "\n" + e.what());
		}
		if (!getline(in, header))
		{
			throw runtime_error("Mat::from_ascii() error reading column name descriptor from ASCII matrix file: " + filename);
		}
		pest_utils::upper_ip(header);
		if (header.find("COLUMN") == string::npos)
			throw runtime_error("Mat::from_ascii() error: expecting column names header instead of:" + header + " in ASCII matrix file: " + filename);
		try
		{
			col_names = read_namelist(in, ncol);
		}
		catch (exception &e)
		{
			throw runtime_error("Mat::from_ascii() error reading column names from ASCII matrix file: " + filename + "\n" + e.what());
		}
		if (nrow != row_names.size())
			throw runtime_error("Mat::from_ascii() error: nrow != row_names.size() in ASCII matrix file: " + filename);

		if(ncol != col_names.size())
			throw runtime_error("Mat::from_ascii() error: ncol != col_names.size() in ASCII matrix file: " + filename);
	}
	in.close();

	Eigen::SparseMatrix<double> new_matrix(nrow, ncol);
	new_matrix.setZero();  // initialize all entries to 0
	new_matrix.setFromTriplets(triplet_list.begin(), triplet_list.end());

	matrix = new_matrix;
}

vector<string> Mat::read_namelist(ifstream &in, int &nitems)
{
	vector<string> names;
	string name;
	for (int i = 0; i < nitems; i++)
	{
		if (!getline(in, name))
		{
			string i_str = to_string(i);
			throw runtime_error("Mat::read_namelist() error reading name for entry " + i_str);
		}
		if (name.find("*") != string::npos)
		{
			string i_str = to_string(i);
			throw runtime_error("Mat::read_namelist() error: '*' found in item name: " + name+", item number: "+i_str);
		}
		pest_utils::strip_ip(name);
		pest_utils::upper_ip(name);
		if (find(names.begin(), names.end(), name) != names.end())
			throw runtime_error("Mat::read_namelist() error: duplicate name: " + name + " found in name list");
		names.push_back(name);
	}
	return names;
}

void Mat::to_binary(const string &filename)
{
	pest_utils::save_binary_orgfmt(filename, row_names, col_names, matrix);
//	ofstream jout(filename, ios::out | ios::binary);
//	int n_par = col_names.size();
//	int n_obs_and_pi = row_names.size();
//	int n;
//	int tmp;
//	double data;
//	char par_name[12];
//	char obs_name[20];
//
//	// write header
//	tmp = -n_par;
//	jout.write((char*)&tmp, sizeof(tmp));
//	tmp = -n_obs_and_pi;
//	jout.write((char*)&tmp, sizeof(tmp));
//
//	//write number nonzero elements in jacobian (includes prior information)
//	n = matrix.nonZeros();
//	jout.write((char*)&n, sizeof(n));
//
//	//write matrix
//	n = 0;
//	map<string, double>::const_iterator found_pi_par;
//	map<string, double>::const_iterator not_found_pi_par;
//
//	Eigen::SparseMatrix<double> matrix_T(matrix);
//	matrix_T.transpose();
//	for (int icol = 0; icol<matrix.outerSize(); ++icol)
//	{
//		for (Eigen::SparseMatrix<double>::InnerIterator it(matrix_T, icol); it; ++it)
//		{
//			data = it.value();
//			n = it.row() + 1 + it.col() * matrix_T.rows();
//			jout.write((char*) &(n), sizeof(n));
//			jout.write((char*) &(data), sizeof(data));
//		}
//	}
//	//save parameter names
//	for (vector<string>::const_iterator b = col_names.begin(), e = col_names.end();
//		b != e; ++b) {
//		string l = pest_utils::lower_cp(*b);
//		pest_utils::string_to_fortran_char(l, par_name, 12);
//		jout.write(par_name, 12);
//	}
//
//	//save observation and Prior information names
//	for (vector<string>::const_iterator b = row_names.begin(), e = row_names.end();
//		b != e; ++b) {
//		string l = pest_utils::lower_cp(*b);
//		pest_utils::string_to_fortran_char(l, obs_name, 20);
//		jout.write(obs_name, 20);
//	}
//	//save observation names (part 2 prior information)
//	jout.close();
}

void Mat::to_binary_new(const string &filename)
{
	pest_utils::save_binary_extfmt(filename, row_names, col_names, matrix);
//	ofstream jout(filename, ios::out | ios::binary);
//	int n_par = col_names.size();
//	int n_obs_and_pi = row_names.size();
//	int n;
//	int tmp;
//	double data;
//	char par_name[200];
//	char obs_name[200];
//
//	// write header
//	tmp = n_par;
//	jout.write((char*)&tmp, sizeof(tmp));
//	tmp = n_obs_and_pi;
//	jout.write((char*)&tmp, sizeof(tmp));
//
//	//write number nonzero elements in jacobian (includes prior information)
//	n = matrix.nonZeros();
//	jout.write((char*)&n, sizeof(n));
//
//	//write matrix
//	n = 0;
//	map<string, double>::const_iterator found_pi_par;
//	map<string, double>::const_iterator not_found_pi_par;
//
//	Eigen::SparseMatrix<double> matrix_T(matrix);
//	matrix_T.transpose();
//	for (int icol = 0; icol<matrix.outerSize(); ++icol)
//	{
//		for (Eigen::SparseMatrix<double>::InnerIterator it(matrix_T, icol); it; ++it)
//		{
//			data = it.value();
//			n = it.row() - 1;
//			jout.write((char*) &(n), sizeof(n));
//			n = it.col() - 1;
//			jout.write((char*) &(n), sizeof(n));
//
//			jout.write((char*) &(data), sizeof(data));
//		}
//	}
//	//save parameter names
//	for (vector<string>::const_iterator b = col_names.begin(), e = col_names.end();
//		b != e; ++b) {
//		string l = pest_utils::lower_cp(*b);
//		pest_utils::string_to_fortran_char(l, par_name, 200);
//		jout.write(par_name, 200);
//	}
//
//	//save observation and Prior information names
//	for (vector<string>::const_iterator b = row_names.begin(), e = row_names.end();
//		b != e; ++b) {
//		string l = pest_utils::lower_cp(*b);
//		pest_utils::string_to_fortran_char(l, obs_name, 200);
//		jout.write(obs_name, 200);
//	}
//	//save observation names (part 2 prior information)
//	jout.close();
}

void Mat::from_binary(const string &filename)
{
	pest_utils::read_binary(filename, row_names, col_names, matrix);
}

//-----------------------------------------
//Maninpulate the shape and ordering of Mats
//-----------------------------------------

Mat Mat::leftCols(const int idx)
{
	vector<string> cnames;
	vector<string> base_cnames = *cn_ptr();
	for (int i = 0; i < idx; i++)
		cnames.push_back(base_cnames[i]);
	return Mat(row_names,cnames,matrix.leftCols(idx));
}

Mat Mat::rightCols(const int idx)
{
	vector<string> cnames;
	vector<string> base_cnames = *cn_ptr();
	for (int i = ncol() - idx; i < ncol(); i++)
		cnames.push_back(base_cnames[i]);
	return Mat(row_names,cnames,matrix.rightCols(idx));
}

Mat Mat::get(const vector<string> &new_row_names, const vector<string> &new_col_names, bool update)
{
	//check that every row and col name is listed
	if (new_row_names.size() == 0) throw runtime_error("Mat::get() error: new_row_names is empty");
	if (new_col_names.size() == 0) throw runtime_error("Mat::get() error: new_col_names is empty");
	vector<string> row_not_found;

	if (update)
		update_sets();
	set<string>::iterator end = row_set.end();
	for (auto &n : new_row_names)
		if (row_set.find(n) == end)
			row_not_found.push_back(n);

	vector<string> col_not_found;
	//set<string> col_set(col_names.begin(), col_names.end());
	end = col_set.end();
	for (auto &n : new_col_names)
		if (col_set.find(n) == end)
			col_not_found.push_back(n);
	if (row_not_found.size() != 0)
	{
		cout << "Mat::get() error: the following row names were not found:" << endl;
		for (auto &name : row_not_found)
			cout << name << ",";
		cout << endl;
	}

	if (col_not_found.size() != 0)
	{
		cout << "Mat::get() error: the following col names were not found:" << endl;
		for (auto &name : col_not_found)
			cout << name << ",";
		cout << endl;
	}

	if ((row_not_found.size() != 0) || (col_not_found.size() != 0))
	{
		throw runtime_error("Mat::get() error: atleast one row or col name not found in Mat::get()");
	}

	int nrow = new_row_names.size();
	int ncol = new_col_names.size();
	int irow_new;
	int icol_new;

	unordered_map<string, int> row_name2new_index_map;
	unordered_map<string, int> col_name2new_index_map;

	// Build mapping of parameter names to column number in new matrix to be returned
	icol_new = 0;
	for (vector<string>::const_iterator b = new_col_names.begin(), e = new_col_names.end();
		b != e; ++b, ++icol_new) {
		col_name2new_index_map[(*b)] = icol_new;
	}

	// Build mapping of observation names to row  number in new matrix to be returned
	irow_new = 0;
	for (vector<string>::const_iterator b = new_row_names.begin(), e = new_row_names.end();
		b != e; ++b, ++irow_new) {
		row_name2new_index_map[(*b)] = irow_new;
	}

	unordered_map<string, int>::const_iterator found_col;
	unordered_map<string, int>::const_iterator found_row;
	unordered_map<string, int>::const_iterator not_found_col_map = col_name2new_index_map.end();
	unordered_map<string, int>::const_iterator not_found_row_map = row_name2new_index_map.end();

	const string *row_name;
	const string *col_name;
	std::vector<Eigen::Triplet<double> > triplet_list;
	for (int icol = 0; icol<matrix.outerSize(); ++icol)
	{
		for (Eigen::SparseMatrix<double>::InnerIterator it(matrix, icol); it; ++it)
		{
			col_name = &col_names[it.col()];
			row_name = &row_names[it.row()];
			found_col = col_name2new_index_map.find(*col_name);
			found_row = row_name2new_index_map.find(*row_name);
			if (found_col != not_found_col_map && found_row != not_found_row_map)
			{
				triplet_list.push_back(Eigen::Triplet<double>(found_row->second, found_col->second, it.value()));
			}
		}
	}
	//if (triplet_list.size() == 0)
		//throw runtime_error("Mat::get() error: triplet list is empty");

	Eigen::SparseMatrix<double> new_matrix(nrow, ncol);
	new_matrix.setZero();
	if (triplet_list.size() > 0)
		new_matrix.setFromTriplets(triplet_list.begin(), triplet_list.end());
	return Mat(new_row_names,new_col_names,new_matrix,this->mattype);
}

Mat Mat::extract(const vector<string> &extract_row_names, const vector<string> &extract_col_names)
{
	Mat new_mat;
	if ((extract_row_names.size() == 0) && (extract_col_names.size() == 0))
		throw runtime_error("Mat::extract() error: extract_rows and extract_cols both empty");
	else if (extract_row_names.size() == 0)
	{
		new_mat = get(row_names, extract_col_names);
		drop_cols(extract_col_names);
	}
	else if (extract_col_names.size() == 0)
	{
		new_mat = get(extract_row_names, col_names);
		drop_rows(extract_row_names);
	}
	else
	{
		new_mat = get(extract_row_names, extract_col_names);
		drop_rows(extract_row_names);
		drop_cols(extract_col_names);
	}
	return new_mat;
}

Mat Mat::extract(const string &extract_row_name, const vector<string> &extract_col_names)
{
	vector<string> extract_row_names;
	extract_row_names.push_back(extract_row_name);
	return extract(extract_row_names, extract_col_names);
}
Mat Mat::extract(const vector<string> &extract_row_names, const string &extract_col_name)
{
	vector<string> extract_col_names;
	extract_col_names.push_back(extract_col_name);
	return extract(extract_row_names, extract_col_names);
}

bool Mat::isdiagonal()
{
	if (mattype == MatType::DIAGONAL)
		return true;
	return false;
}

void Mat::drop_cols(const vector<string> &drop_col_names)
{
	vector<string> missing_col_names;
	const set<string> snames(col_names.begin(), col_names.end());
	set<string>::const_iterator send = snames.end();
	for (auto &name : drop_col_names)
	{
		//if (find(col_names.begin(), col_names.end(), name) == col_names.end())
		if (snames.find(name) == send)
			missing_col_names.push_back(name);
	}

	if (missing_col_names.size() != 0)
	{
		cout << "Mat::drop_cols() error: the following drop_col_names were not found:" << endl;
		for (auto &name : drop_col_names)
			cout << name << ',';
		cout << endl;
		throw runtime_error("Mat::drop_cols() error: atleast one drop col name not found");
	}
	vector<string> new_col_names;
	if (drop_col_names.size() == 0)
		new_col_names = col_names;
	else
	{
		const set<string> snames(drop_col_names.begin(), drop_col_names.end());
		set<string>::const_iterator send = snames.end();
		for (auto &name : col_names)
		{
			//if (find(drop_col_names.begin(), drop_col_names.end(), name) == drop_col_names.end())
			if (snames.find(name) == send)
				new_col_names.push_back(name);
		}
	}
	Mat new_mat = get(row_names, new_col_names);
	matrix = new_mat.get_matrix();
	col_names = new_col_names;
	mattype = new_mat.get_mattype();
}

void Mat::drop_rows(const vector<string> &drop_row_names)
{

	vector<string> missing_row_names;
	const set<string> snames(row_names.begin(), row_names.end());
	set<string>::const_iterator send = snames.end();

	for (auto &name : drop_row_names)
	{
		//if (find(row_names.begin(), row_names.end(), name) == row_names.end())
		if (snames.find(name) == send)
			missing_row_names.push_back(name);
	}

	if (missing_row_names.size() != 0)
	{
		cout << "Mat::drop_rows() error: the following drop_row_names were not found:" << endl;
		for (auto &name : drop_row_names)
			cout << name << ',';
		cout << endl;
		throw runtime_error("Mat::drop_rows() error: atleast one drop row name not found");
	}

	vector<string> new_row_names;
	if (drop_row_names.size() == 0)
		new_row_names = row_names;
	else
	{
		const set<string> snames(drop_row_names.begin(), drop_row_names.end());
		set<string>::const_iterator send = snames.end();
		for (auto &name : row_names)
		{
			//if (find(drop_row_names.begin(), drop_row_names.end(), name) == drop_row_names.end())
			if (snames.find(name) == send)
				new_row_names.push_back(name);
		}
	}
	if (new_row_names.size() == 0)
		matrix = Eigen::SparseMatrix<double>();
	else
	{
		Mat new_mat = get(new_row_names, col_names);
		matrix = new_mat.get_matrix();
		mattype = new_mat.get_mattype();
	}
	row_names = new_row_names;

}

//-----------------------------------------
//covariance matrices
//-----------------------------------------
Covariance::Covariance(string filename)
{
	mattype = MatType::SPARSE;
	string ext = filename.substr(filename.find_last_of(".") + 1);
	vector<string> empty;
	pest_utils::upper_ip(ext);
	if (ext == "PST")
		throw runtime_error("Cov::Cov() error: cannot instantiate a cov with PST");
	else if (ext == "MAT")
		from_ascii(filename);
	else if (ext == "UNC")
		from_uncertainty_file(filename, empty);
	else
		throw runtime_error("Cov::Cov() error: only .unc or .mat\
														 files can be used to instatiate a Cov");
}

Covariance::Covariance(vector<string> &names)
{
	row_names = names;
	col_names = names;
	icode = 1;
	mattype = MatType::SPARSE;
}

Covariance::Covariance()
{
	icode = 1;
	mattype = MatType::SPARSE;
}

Covariance::Covariance(vector<string> _names, Eigen::SparseMatrix<double> _matrix, Mat::MatType _mattype)
{
	if ((_names.size() != _matrix.rows()) || (_names.size() != _matrix.cols()))
		throw runtime_error("Covariance::Covariance() error: names.size() does not match matrix dimensions");
	Eigen::SparseMatrix<double> test = _matrix;
	matrix = _matrix;
	row_names = _names;
	col_names = _names;
	icode = 1;
	mattype =_mattype;
}

Covariance::Covariance(Mat _mat)
{
	if (_mat.get_row_names() != _mat.get_col_names())
		throw runtime_error("Cov::Cov() error instantiating Covariance from Mat: row_names != col_names");
	row_names = _mat.get_row_names();
	col_names = _mat.get_col_names();
	matrix = _mat.get_matrix();
	icode = 1;
	mattype = _mat.get_mattype();
}

void Covariance::from_diagonal(Covariance &other)
{
	row_names = other.get_row_names();
	col_names = other.get_col_names();
	if (other.get_mattype() == Mat::MatType::DIAGONAL)
	{
		matrix = other.get_matrix();
	}
	else
	{
		Eigen::MatrixXd temp = other.e_ptr()->diagonal().asDiagonal();
		Eigen::SparseMatrix<double> temp2 = temp.sparseView();
		matrix = temp2;
	}

}
Covariance Covariance::diagonal(double val)
{
	vector<Eigen::Triplet<double>> triplet_list;
	for (int i = 0; i != nrow(); i++)
		triplet_list.push_back(Eigen::Triplet<double>(i, i, val));
	Eigen::SparseMatrix<double> i(nrow(), ncol());
	i.setZero();
	i.setFromTriplets(triplet_list.begin(), triplet_list.end());
	return Covariance(*rn_ptr(), i);
}

string Covariance::try_from(Pest &pest_scenario, FileManager &file_manager, bool is_parcov, bool forgive_missing)
{
	stringstream how;
	stringstream ss;
	string cov_fname;
	vector<string> ordered_names;
	if (is_parcov)
	{
		cov_fname = pest_scenario.get_pestpp_options().get_parcov_filename();
		ordered_names = pest_scenario.get_ctl_ordered_adj_par_names();
	}
	else
	{
		cov_fname = pest_scenario.get_pestpp_options().get_obscov_filename();
		ordered_names = pest_scenario.get_ctl_ordered_nz_obs_names();
	}
	if (!cov_fname.empty())
	{
		string ext = cov_fname.substr(cov_fname.size() - 3, 3);
		pest_utils::upper_ip(ext);
		if (ext == "UNC")
		{
			try
			{
				from_uncertainty_file(cov_fname, ordered_names);
				how << "from unc file " << cov_fname;
			}
			catch (exception &e)
			{
				ss << "Cov::try_from() error reading uncertainty file " << cov_fname << " :" << e.what();
				throw runtime_error(ss.str());
			}
		}
		else
		{
			try
			{
				Mat::from_file(cov_fname);
				how << " from file " << cov_fname;
			}
			catch (exception &e)
			{
				ss << "Cov:try_from() error reading from file " << cov_fname << " :" << e.what();
				throw runtime_error(ss.str());
			}
		}
	}
	else
	{
		if (is_parcov)
		{
			from_parameter_bounds(pest_scenario, file_manager.rec_ofstream());
			how << "from parameter bounds, using par_sigma_range " << pest_scenario.get_pestpp_options().get_par_sigma_range();
		}
		else
		{
			from_observation_weights(pest_scenario, file_manager.rec_ofstream());
			how << "from observation weights";
		}
		}
	//check that the parcov matrix has the right parameter names
	vector<string> missing;
	if ((is_parcov) && (cov_fname.size() > 0))
	{
		set<string> parcov_names(row_names.begin(), row_names.end());
		const ParameterRec *prec;
		for (auto &pname : pest_scenario.get_ctl_ordered_par_names())
		{
			prec = pest_scenario.get_ctl_parameter_info().get_parameter_rec_ptr(pname);
			if ((prec->tranform_type == ParameterRec::TRAN_TYPE::LOG) ||
				(prec->tranform_type == ParameterRec::TRAN_TYPE::NONE))
			{
				if (parcov_names.find(pname) == parcov_names.end())
				{
					missing.push_back(pname);
				}
			}
		}
		if (missing.size() > 0)
		{
			ofstream& frec = file_manager.rec_ofstream();
			frec << "...Note: parcov missing the following " << missing.size() << " adjustable parameters:" << endl;
			int i = 0;
			for (auto& pname : missing)
			{
				frec << ',' << pname;
				i++;
				if (i > 10)
				{
					frec << endl;
					i = 0;
				}
			}
			frec << endl;
			if (forgive_missing)
			{
				cout << "WARNING: " << missing.size() << " adjustable parameters missing from parcov, continuing..." << endl;
			}
			else
			{
				ss.str("");
				ss << "parcov missing " << missing.size() << "adjustable parameters, see rec file for listing";
				throw PestError(ss.str());
			}
		}

	}
	if ((!is_parcov) && (cov_fname.size() > 0))
	{
		double weight;
		set<string> cov_names(row_names.begin(), row_names.end());
		for (auto &oname : pest_scenario.get_ctl_ordered_obs_names())
		{
			weight = pest_scenario.get_ctl_observation_info().get_weight(oname);
			if (weight == 0.0)
				continue;
			if (cov_names.find(oname) == cov_names.end())
			{
				missing.push_back(oname);
			}

		}
		if (missing.size() > 0)
		{
			ofstream& frec = file_manager.rec_ofstream();
			frec << "...Note: obscov missing the following " << missing.size() << " non-zero weighted obs:" << endl;
			int i = 0;
			for (auto& name : missing)
			{
				frec << ',' << name;
				i++;
				if (i > 10)
				{
					frec << endl;
					i = 0;
				}
			}
			frec << endl;
			if (forgive_missing)
			{
				cout << "WARNING: " << missing.size() << " non-zero weighted observations missing from obscov, continuing..." << endl;
			}
			else
			{
				ss.str("");
				ss << "obscov missing " << missing.size() << "non-zero weighted observations, see rec file for listing";
				throw PestError(ss.str());
			}
		}
	}

	vector<string> extra;
	set<string> allowed_names(ordered_names.begin(), ordered_names.end());
	ordered_names.clear();

	for (auto name : row_names)
	{
		if (allowed_names.find(name) == allowed_names.end())
			extra.push_back(name);
	}
	if (extra.size() > 0)
	{
		stringstream ss;
		ss << "WARNING: Cov::try_from(): " << extra.size() << " extra elements in covariance matrix being drop - these are probably for fixed parameters and/or zero-weight observations";
		//for (auto name : extra)
		//	ss << " " << name;
		//throw PestError(ss.str());
		file_manager.rec_ofstream() << ss.str() << endl << endl;
		//cout << "WARNING: " << extra.size() << " unrecognized elements in covariance matrix being dropped, see .rec file for listing" << endl;
		drop(extra);
	}
	return how.str();
}

Covariance Covariance::get(const vector<string> &other_names, bool update)
{
	Covariance new_cov(Mat::get(other_names, other_names, update));
	return new_cov;
}

Covariance Covariance::extract(vector<string> &extract_names)
{
	Covariance new_cov(Mat::extract(extract_names, extract_names));
	return new_cov;
}

void Covariance::drop(vector<string> &drop_names)
{
	drop_rows(drop_names);
	drop_cols(drop_names);
}

void Covariance::from_uncertainty_file(const string &filename, vector<string> &ordered_names)
{
	ifstream in(filename);
	if (!in.good())
	{
		throw runtime_error("Cov::from_uncertainty_file() error: cannot open " + filename + " to read uncertainty file: "+filename);
	}
	mattype = MatType::DIAGONAL;
	vector<Eigen::Triplet<double>> triplet_list;
	vector<string> names;
	string line,name,word;
	double val;
	vector<string> tokens;
	int irow=0, jcol=0;

	while (getline(in, line))
	{
		if (line.substr(0, 1).find("#") != string::npos)
		{
			continue;
		}
		pest_utils::upper_ip(line);
		//if this is the start of some block
		if (line.find("START") != string::npos)
		{
			if (line.find("STANDARD_DEVIATION") != string::npos)
			{
				while (true)
				{
					if (!getline(in, line))
						throw runtime_error("Cov::from_uncertainty_file() error:EOF encountered while reading standard_deviation block\
							from uncertainty file:" + filename);
					pest_utils::upper_ip(line);
					if (line.find("END") != string::npos)
					{
						break;
					}

					tokens.clear();
					pest_utils::tokenize(line, tokens);
					pest_utils::convert_ip(tokens[1], val);
					name = tokens[0];
					double std_mlt = 1.0;
					if (name == "STD_MULTIPLIER")
					{
						std_mlt = val;
						continue;
					}
					if (find(names.begin(), names.end(), name) != names.end())
						throw runtime_error(name + " listed more than once in uncertainty file:" + filename);
					names.push_back(tokens[0]);
					triplet_list.push_back(Eigen::Triplet<double>(irow, jcol, val * std_mlt));
					irow++, jcol++;
				}

			}
			else if (line.find("COVARIANCE_MATRIX") != string::npos)
			{
				string cov_filename = "none";
				double var_mult = 1.0;
				string start_par = "", end_par = "";
				string par_list_file = "";
				while (true)
				{
					if (!getline(in, line))
						throw runtime_error("Cov::from_uncertainty_file() error:EOF encountered while reading covariance_matrix block\
							from uncertainty file:" + filename);
					// keep line in original case to preserve filename
					line.erase(remove(line.begin(), line.end(), '\"'), line.end());
					line.erase(remove(line.begin(), line.end(), '\''), line.end());
					string upper_line = line;
					pest_utils::upper_ip(upper_line);
					if (upper_line.find("END") != string::npos) break;

					tokens.clear();
					pest_utils::tokenize(line, tokens);
					word = pest_utils::upper_cp(tokens[0]);
					if (word.find("FILE") != string::npos)
						cov_filename = tokens[1];
					else if (word.find("VARIANCE_MULTIPLIER") != string::npos)
						pest_utils::convert_ip(tokens[1], var_mult);
					else if (word.find("FIRST_PARAMETER") != string::npos)
						start_par = pest_utils::upper_cp(tokens[1]);
					else if (word.find("LAST_PARAMETER") != string::npos)
						end_par = pest_utils::upper_cp(tokens[1]);
					else if (word.find("PARAMETER_LIST_FILE"))
						par_list_file = tokens[1];
					else
						throw runtime_error("Cov::from_uncertainty_file() error:unrecognized token:" + tokens[0] + " in covariance matrix block in uncertainty file:" + filename);
				}

				if ((start_par.size() > 0) && (end_par.size() == 0))
					throw runtime_error("Cov::from_uncertainty_file() error: 'FIRST PARAMETER' passed but 'LAST_PARAMETER' was not");
				if ((start_par.size() == 0) && (end_par.size() > 0))
					throw runtime_error("Cov::from_uncertainty_file() error: 'LAST PARAMETER' passed but 'FIRST_PARAMETER' was not");

				//read the covariance matrix
				Covariance cov;
				cov.from_ascii(cov_filename);

				if ((start_par.size() > 0) && (end_par.size() > 0))
				{
					if (par_list_file.size() > 0)
						throw runtime_error("Cov::from_uncertainty_file() error: both 'PARAMETER_LIST_FILE' AND 'FIRST_PARAMETER'/'LAST_PARAMETER' supplied");
					if (ordered_names.size() == 0)
						throw runtime_error("Cov::from_uncertainty_file() error: ordered_names arg req for first_par/last_par option");
					vector<string>::iterator first, last;
					first = find(ordered_names.begin(), ordered_names.end(), start_par);
					if (first == ordered_names.end())
						throw runtime_error("Cov::from_uncertainty_file() error: couldn't find 'FIRST_PARAMETER' " + start_par);
					last = find(ordered_names.begin(), ordered_names.end(), end_par);
					if (last == ordered_names.end())
						throw runtime_error("Cov::from_uncertainty_file() error: couldn't find 'LAST_PARAMETER' " + end_par);
					vector<string> ordered_names_matrix(first, last+1);
					if (ordered_names_matrix.size() != cov.col_names.size())
					{
						stringstream ss;
						ss << "Cov::from_uncertainty_file() error: number of elements in covariance matrix " << cov_filename << " (" << cov.col_names.size();
						ss << ") different from number of elements between 'FIRST_PARAMETER' and 'LAST_PARAMETER' (" << ordered_names_matrix.size() << ")";
						throw runtime_error(ss.str());
					}
					cov.row_names = ordered_names_matrix;
					cov.col_names = ordered_names_matrix;
				}
				else if (par_list_file.size() > 0)
				{
					vector<string> par_names;
					try
					{
						par_names = pest_utils::read_onecol_ascii_to_vector(par_list_file);
					}
					catch (...)
					{
						throw runtime_error("Cov::from_uncertainty_file() error reading 'PARAMETER_LIST_FILE' " + par_list_file);
					}
					if (par_names.size() != cov.col_names.size())
					{
						stringstream ss;
						ss << "Cov::from_uncertainty_file() error: number of elements in covariance matrix " << cov_filename;
						ss << " (" << cov.col_names.size() << ") different from number of elements in 'PARAMETER_LIST_FILE' (" << par_names.size() << ")";
						throw runtime_error(ss.str());
					}
					cov.row_names = par_names;
					cov.col_names = par_names;
				}

				//check that the names in the covariance matrix are not already listed
				vector<string> dup_names;
				for (auto &name : cov.get_row_names())
				{
					if (find(names.begin(), names.end(), name) != names.end())
						dup_names.push_back(name);
					else
						names.push_back(name);
				}
				if (dup_names.size() != 0)
				{
					cout << "the following names from covariance matrix file " << cov_filename << " have already be found in uncertainty file " << filename << endl;
					for (auto &name : dup_names)
						cout << name << ',';
					cout << endl;
					throw runtime_error("Cov::from_uncertainty_file() error:atleast one name in covariance matrix " + cov_filename + " is already listed in uncertainty file: " + filename);
				}

				//build triplets from the covariance matrix
				int start_irow = irow;
				Eigen::SparseMatrix<double> cov_matrix = cov.get_matrix();
				for (int icol = 0; icol < cov_matrix.outerSize(); ++icol)
				{
					for (Eigen::SparseMatrix<double>::InnerIterator it(cov_matrix, icol); it; ++it)
					{
						triplet_list.push_back(Eigen::Triplet<double>(start_irow + it.row(), jcol, var_mult * it.value()));
						irow++;
					}
					jcol++;
					irow = start_irow;
				}
				mattype = MatType::SPARSE;
				irow = jcol;
			}
			else
				throw runtime_error("Cov::from_uncertainty_file() error:unrecognized block:" + line + " in uncertainty file:" + filename);
		}
	}

	Eigen::SparseMatrix<double> new_matrix(names.size(), names.size());
	new_matrix.setZero();  // initialize all entries to 0
	new_matrix.setFromTriplets(triplet_list.begin(), triplet_list.end());
	//cout << new_matrix.diagonal() << endl;
	matrix = new_matrix;
	row_names = names;
	col_names = names;
}

void Covariance::from_parameter_bounds(ofstream& frec, const vector<string> &par_names,const ParameterInfo &par_info,
	map<string, double>& par_std, double sigma_range)
{
	matrix.resize(0, 0);
	row_names.clear();
	col_names.clear();
	vector<Eigen::Triplet<double>> triplet_list;
	const ParameterRec* par_rec;
	int i = 0;
	double upper, lower;
	for (auto par_name : par_names)
	{
		pest_utils::upper_ip(par_name);
		par_rec = par_info.get_parameter_rec_ptr(par_name);
		if ((par_rec->tranform_type == ParameterRec::TRAN_TYPE::FIXED) || (par_rec->tranform_type == ParameterRec::TRAN_TYPE::TIED))
		{
			continue;
		}
		if (par_std.find(par_name) != par_std.end())
		{
			triplet_list.push_back(Eigen::Triplet<double>(i, i, pow(par_std[par_name], 2.0)));
			row_names.push_back(par_name);
			col_names.push_back(par_name);
			i++;
		}
		else
		{
			upper = par_rec->ubnd;
			lower = par_rec->lbnd;
			if (par_rec->tranform_type == ParameterRec::TRAN_TYPE::LOG)
			{
				upper = log10(upper);
				lower = log10(lower);
			}
			row_names.push_back(par_name);
			col_names.push_back(par_name);
			//double temp = pow((upper - lower) / 4.0,2.0);
			triplet_list.push_back(Eigen::Triplet<double>(i, i, pow((upper - lower) / sigma_range, 2.0)));
			i++;
		}
	}
	if (triplet_list.size() > 0)
	{
		matrix.resize(row_names.size(), row_names.size());
		matrix.setFromTriplets(triplet_list.begin(), triplet_list.end());
	}
	else
	{
		throw runtime_error("Cov::from_parameter_bounds() error:Error loading covariance from parameter bounds: no non-fixed/non-tied parameters found");
	}
	mattype = Mat::MatType::DIAGONAL;
}

void Covariance::from_parameter_bounds(Pest &pest_scenario, ofstream& frec)
{
	map<string, double> par_std = pest_scenario.get_ext_file_double_map("parameter data external", "standard_deviation");
	if (par_std.size() > 0)
	{
		frec << "Note: the following parameters have 'standard_deviation' defined - this will be used" << endl;
		frec << "      instead of bounds for the prior parameter covariance matrix : " << endl;
		vector<string> remove;
		for (auto pname : pest_scenario.get_ctl_ordered_par_names())
		{
			if (par_std.find(pname) != par_std.end())
			{
				if (par_std[pname] <= 0.0)
				{
					frec << "Warning: parameter " << pname << " 'standard_deviation' less than or equal to zero, using bounds instead" << endl;
					remove.push_back(pname);
				}
				else
					frec << pname << ' ' << par_std[pname] << endl;
			}

		}
		for (auto r : remove)
			par_std.erase(r);
	}

	from_parameter_bounds(frec, pest_scenario.get_ctl_ordered_par_names(), pest_scenario.get_ctl_parameter_info(),
		par_std,pest_scenario.get_pestpp_options().get_par_sigma_range());
}

void Covariance::from_parameter_bounds(const string &pst_filename, ofstream& frec)
{
	ifstream ipst(pst_filename);
	if (!ipst.good()) throw runtime_error("Cov::from_parameter_bounds() error opening pst file: " + pst_filename);
	Pest pest_scenario;
	pest_scenario.process_ctl_file(ipst, pst_filename);
	from_parameter_bounds(pest_scenario, frec);
}

void Covariance::from_observation_weights(const string &pst_filename, ofstream& frec)
{
	ifstream ipst(pst_filename);
	if (!ipst.good()) throw runtime_error("Cov::from_observation_weights() error opening pst file: " + pst_filename);
	Pest pest_scenario;
	pest_scenario.process_ctl_file(ipst, pst_filename);
	from_observation_weights(pest_scenario,frec);

}

void Covariance::from_observation_weights(ofstream& frec, const vector<string>& obs_names, const ObservationInfo& obs_info,
	const vector<string>& pi_names, const PriorInformation* pi, map<string,double>& obs_std)
{
	matrix.resize(0, 0);
	row_names.clear();
	col_names.clear();
	vector<Eigen::Triplet<double>> triplet_list;
	const ObservationRec* obs_rec;
	int i = 0;
	double weight = 0;
	for (auto obs_name : obs_names)
	{
		pest_utils::upper_ip(obs_name);
		obs_rec = obs_info.get_observation_rec_ptr(obs_name);
		if (obs_std.find(obs_name) != obs_std.end())
		{
			triplet_list.push_back(Eigen::Triplet<double>(i, i, pow(obs_std[obs_name], 2)));
			row_names.push_back(obs_name);
			col_names.push_back(obs_name);
			i++;
		}
		else
		{
			obs_rec = obs_info.get_observation_rec_ptr(obs_name);
			weight = obs_rec->weight;
			if (weight <= 0.0)
				weight = 1.0e+60;
			else
				weight = pow(1.0 / obs_rec->weight, 2.0);
			triplet_list.push_back(Eigen::Triplet<double>(i, i, weight));
			row_names.push_back(obs_name);
			col_names.push_back(obs_name);
			i++;
		}
	}

	/*PriorInformation::const_iterator pi_iter;
	PriorInformation::const_iterator not_pi_iter = pi->end();

	for (auto pi_name : pi_names)
	{
		pi_iter = pi->find(pi_name);
		if (pi_iter != not_pi_iter)
		{
			weight = pi_iter->second.get_weight();
			if (weight <= 0.0) weight = 1.0e-30;
			triplet_list.push_back(Eigen::Triplet<double>(i, i, pow(1.0 / weight, 2.0)));
			row_names.push_back(pi_name);
			col_names.push_back(pi_name);
			i++;
		}
	}*/
	if (row_names.size() > 0)
	{
		matrix.resize(row_names.size(), row_names.size());
		matrix.setFromTriplets(triplet_list.begin(), triplet_list.end());
	}
	else
	{
		throw runtime_error("Cov::from_observation_weights() error:Error loading covariance from obs weights: no non-zero weighted obs found");
	}
	mattype = Mat::MatType::DIAGONAL;
	/*if (mattype == Mat::MatType::DIAGONAL)
		cout << "diagonal" << endl;*/
}

void Covariance::from_observation_weights(Pest &pest_scenario, ofstream& frec)
{
	map<string, double> obs_std = pest_scenario.get_ext_file_double_map("observation data external", "standard_deviation");
	vector<string> remove;
	if (obs_std.size() > 0)
	{
		frec << "Note: the following observations have 'standard_deviation' defined - this will be used" << endl;
		frec << "      instead of weight for the observation noise covariance matrix : " << endl;
		for (auto oname : pest_scenario.get_ctl_ordered_obs_names())
		{
			if (obs_std.find(oname) != obs_std.end())
			{
				if (obs_std[oname] <= 0.0)
				{
					frec << "Warning: observation " << oname << " 'standard_deviation' less than or equal to zero, using weight" << endl;
					remove.push_back(oname);
				}
				else
					frec << oname << ' ' << obs_std[oname] << endl;
			}
		}
		for (auto r : remove)
			obs_std.erase(r);
	}

	from_observation_weights(frec, pest_scenario.get_ctl_ordered_obs_names(), pest_scenario.get_ctl_observation_info(),
		pest_scenario.get_ctl_ordered_pi_names(), pest_scenario.get_prior_info_ptr(), obs_std);

}

void Covariance::to_uncertainty_file(const string &filename)
{
	ofstream out(filename);
	if (!out.good())
	{
		throw runtime_error("Cov::to_uncertainty_file() error opening file: " + filename + " to write an uncertainty file");
	}

	//check if diagonal, write stdevs
	if (mattype == Mat::MatType::DIAGONAL)
	{
		Eigen::VectorXd vec(matrix.diagonal());
		out << "START STANDARD_DEVIATION" << endl;
		int i=0;
		for (vector<string>::iterator name = row_names.begin(); name != row_names.end(); ++name, i++)
		{
			out << "  " << setw(20) << left << *name << "  " << setw(20) << left << vec(i) << endl;
		}
		out << "END STANDARD_DEVIATION" << endl;
		out.close();
	}
	else
	{
		out << "START COVARIANCE_MATRIX" << endl;
		out << "  file emu_cov.mat" << endl;
		out << " variance multiplier 1.0" << endl;
		out << "END COVARIANCE_MATRIX" << endl;
		out.close();
		to_ascii("emu_cov.mat");
	}
}

void Covariance::cholesky()
{
	Eigen::SimplicialLLT<Eigen::SparseMatrix<double>> llt;
	lower_cholesky = llt.matrixL();
}

vector<Eigen::VectorXd> Covariance::draw(int ndraws)
{
	throw runtime_error("Covariance::draw() not implemented");
}

vector<double> Covariance::standard_normal(default_random_engine gen)
{
	normal_distribution<double> stanard_normal(0.0, 1.0);
	vector<double> sn_vec;
	for (auto &name : row_names)
	{
		sn_vec.push_back(stanard_normal(gen));
	}
	return sn_vec;
}


#ifndef HTLP_HPP_
#define HTLP_HPP_

#include <NTL/ZZ.h>
#include <assert.h>
#include <openssl/sha.h>
#include <vector>
#include <sstream>

#include "Puzzle.hpp"

#ifndef RSA_
#define RSA_
typedef struct RSA
{
    NTL::ZZ p;
    NTL::ZZ q;
} RSA;
#endif

class HTLP
{
protected:
    const bool cheeting_mode_;
    RSA rsa_;
    NTL::ZZ n_;
    NTL::ZZ n_square_;
    NTL::ZZ g_;
    NTL::ZZ h_;
    NTL::ZZ lambda_;
    const long T_;
    const long kappa_;
    long prime_len_;
    const long modulus_len_;
    NTL::ZZ trapdoor_;
    RSA GenerateRSAModulus(const long modulus_len);

public:
    HTLP(const long modulus_len, const long T, const long kappa);
    HTLP(const NTL::ZZ &n, const NTL::ZZ &g, const NTL::ZZ &h, const long T, const long kappa);
    HTLP(const long modulus_len, const long T, const long kappa, bool cheeting_mode);

    NTL::ZZ HashToElement(const std::string str);
    NTL::ZZ HashToPrime(const NTL::ZZ &g, const NTL::ZZ &h);

    NTL::ZZ GenerateProof(const long k, const long gamma, std::vector<NTL::ZZ> &C, NTL::ZZ &l);

    NTL::ZZ GenerateJacobiOne();
    NTL::ZZ GenerateRandomExponent()
    {
        return RandomBnd(n_ / 2);
    }
    NTL::ZZ GenerateRandomElement()
    {
        return RandomBnd(n_);
    }

    NTL::ZZ n()
    {
        return n_;
    }
    NTL::ZZ n_square()
    {
        return n_square_;
    }
    NTL::ZZ g()
    {
        return g_;
    }
    NTL::ZZ h()
    {
        return h_;
    }
    long T()
    {
        return T_;
    }

};

#endif


#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics.hpp>
#include <iostream>

using namespace boost::accumulators;

int main()
{
  accumulator_set<double, features<tag::mean, tag::variance>> acc;
  acc(8);
  acc(9);
  acc(10);
  acc(11);
  acc(12);
  std::cout << mean(acc) << '\n';
  std::cout << variance(acc) << '\n';
}


#ifndef __solver_cod_h__
#define __solver_cod_h__

#include <Eigen/Dense>
#include <vector>
#include <iostream>
#include "weighted_hqp/givens.hpp"

namespace hcod{
    class Cod{
        public:
            Cod(const Eigen::MatrixXd &A, const double &THR);
            ~Cod(){};

        private:
            void calc_decomposition();
            void computation();

        public:
            Eigen::MatrixXd getW(){
                return W_permute_;
            }
            Eigen::MatrixXd getL(){
                return L_permute_;
            }
            Eigen::MatrixXd getQ(){
                return Q_;
            }
            Eigen::MatrixXd getE(){
                return E_;
            }
            int getRank(){
                return rankA_;
            }

        private:
           Eigen::MatrixXd A_, Q_, R_, E_, L_, W_, L_permute_, W_permute_ ;
           double THR_;
           int rankA_;
           Givens* givens_t;

    };
}

#endif


#pragma once

#include <boost/multiprecision/cpp_int.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/algorithm/hex.hpp>

namespace Ethereum{namespace ABI{

typedef boost::multiprecision::cpp_dec_float_100 decimal_t;
typedef boost::multiprecision::uint256_t uint256_t;
typedef boost::multiprecision::int256_t int256_t;

}}


#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#include "../jngen.h"

BOOST_AUTO_TEST_SUITE(graph)

BOOST_AUTO_TEST_CASE(output) {
    setMod().reset();

    Graph g;
    g.addEdge(0, 2);
    g.addEdge(1, 3);
    g.addEdge(0, 3);
    g.addEdge(0, 3);
    g.addEdge(1, 2);
    g.addEdge(2, 1);

    std::ostringstream ss;
    ss << g.printN().printM().add1() << std::endl;

    BOOST_TEST(ss.str() == "4 6\n1 3\n2 4\n1 4\n1 4\n2 3\n3 2\n");
}

BOOST_AUTO_TEST_CASE(weights_and_labelling) {
    setMod().reset();

    rnd.seed(123);

    Graph g = Graph::random(10, 20);

    g.setVertexWeights(Array::random(g.n(), 100));
    g.setEdgeWeights(Arrayf::random(g.m(), 1.5, 1.8));

    g.setVertexWeight(5, "five");
    g.setVertexWeight(8, "eight");
    g.addEdge(5, 8, "5-8");

    g.shuffle();

    std::stringstream ss;
    ss << g.printN().printM() << std::endl;

    int v1, v2;
    int v5 = -1, v8 = -1;
    std::string s;
    ss >> v1 >> v2;
    BOOST_TEST(v1 == g.n());
    BOOST_TEST(v2 == g.m());

    for (int i = 0; i < g.n(); ++i) {
        ss >> s;
        if (s == "five") {
            v5 = i;
        } else if (s == "eight") {
            v8 = i;
        }
    }

    BOOST_CHECK(v5 != -1 && v8 != -1);

    int count = 0;
    for (int i = 0; i < g.m(); ++i) {
        ss >> v1 >> v2 >> s;
        if (s == "5-8") {
            ++count;
            BOOST_CHECK( (v1 == v5 && v2 == v8) || (v1 == v8 && v2 == v5) );
        }
    }

    BOOST_TEST(count == 1);
}

template<typename T>
void generateWithTraitsMask(T&& generator, const std::string& name, int mask) {
    if (mask&(1<<0)) generator.allowAntiparallel();
    if (mask&(1<<1)) generator.allowLoops();
    if (mask&(1<<2)) generator.allowMulti();
    if (mask&(1<<3)) generator.connected();
    if (mask&(1<<4)) generator.directed();
    if (mask&(1<<5)) generator.acyclic();
    try {
        generator.g();
    } catch (jngen::Exception) {
        // directed acyclic cycle
        if (name == "cycle" && (mask & ((1<<4) | (1<<5)))) {
            return;
        }

        // connected empty graph
        if (name == "empty" && (mask & (1<<3))) {
            return;
        }

        throw;

        /*
        // left here for debug purposes
        std::cerr << name << ": ";
        for (int i = 0; i < 6; ++i) {
            if (mask&(1<<i)) {
                std::cerr << "+";
            } else {
                std::cerr << "-";
            }
        }
        std::cerr << std::endl;
        */
    }
}

BOOST_AUTO_TEST_CASE(various_traits) {
    BOOST_CHECK(true);

    for (int mask = 0; mask < (1<<6); ++mask) {
        generateWithTraitsMask(Graph::random(10, 15), "random", mask);
        generateWithTraitsMask(
                Graph::randomStretched(10, 15, 5, 5), "randomStretched", mask);
        generateWithTraitsMask(Graph::complete(10), "complete", mask);
        generateWithTraitsMask(Graph::cycle(10), "cycle", mask);
        generateWithTraitsMask(Graph::empty(10), "empty", mask);
    }
}

BOOST_AUTO_TEST_CASE(empty_output) {
    setMod().reset();

    std::stringstream ss;

    ss << Graph::random(10, 0).g().printN().printM().add1() << std::endl;
    BOOST_TEST(ss.str() == "10 0\n");
    ss.str("");

    ss << Graph::random(10, 0).g().printN().add1() << std::endl;
    BOOST_TEST(ss.str() == "10\n");
    ss.str("");

    Graph g = Graph::random(10, 0);
    g.setVertexWeights(Array::random(g.n(), 10, 20));
    ss << g.printN().printM() << std::endl;
    auto s = ss.str();
    s.pop_back();
    BOOST_TEST(s != "\n");
    ss.str("");

    g = Graph::empty(5);
    g.setVertexWeights(Array::id(5));
    ss << g << std::endl;
    BOOST_TEST(ss.str() == "0 1 2 3 4\n");
    ss.str("");

    g = Graph::empty(0);
    ss << g.printN().printM() << std::endl;
    BOOST_TEST(ss.str() == "0 0\n");
    ss.str("");
}

BOOST_AUTO_TEST_SUITE_END()


//
// Created by Alex Beccaro on 18/01/18.
//

#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#include "../../src/problems/1-50/3/problem3.hpp"

BOOST_AUTO_TEST_SUITE( Problem3 )

    BOOST_AUTO_TEST_CASE( Example ) {
        auto res = problems::problem3::solve(13195);
        BOOST_CHECK_EQUAL(res, 29);
    }

    BOOST_AUTO_TEST_CASE( Solution ) {
        auto res = problems::problem3::solve();
        BOOST_CHECK_EQUAL(res, 6857);
    }

BOOST_AUTO_TEST_SUITE_END()


//
// Copyright 2020 Debabrata Mandal <mandaldebabrata123@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
//

#include <boost/gil.hpp>
#include <boost/gil/extension/io/png.hpp>
#include <boost/gil/io/read_image.hpp>

#include <iostream>

using namespace boost::gil;

// Explains how to use the histogram class and some of its features
// that can be applied for a variety of tasks.

// See also:
// histogram_equalization.cpp - Regular Histogram Equalization
// adaptive_he.cpp - Adaptive Histogram Equalization
// histogram_matching.cpp - Reference-based histogram computation

int main()
{
   // Create a histogram class. Use uint or unsigned short as the default axes type in most cases.
    histogram<unsigned char> h;

    // Fill histogram with GIL images (of any color space)
    gray8_image_t g;
    read_image("test_adaptive.png", g, png_tag{});

    fill_histogram
    (
        view(g), // Input image view
        h,       // Histogram to be filled
        1,       // Histogram bin widths
        false,   // Specify whether to accumulate over the values already present in h (default = false)
        true,    // Specify whether to have a sparse (true) or continuous histogram (false) (default = true)
        false,   // Specify if image mask is to be specified
        {{}},    // Mask as a 2D vector. Used only if prev argument specified
        {0},     // Lower limit on the values in histogram (default numeric_limit::min() on axes)
        {255},   // Upper limit on the values in histogram (default numeric_limit::max() on axes)
        true     // Use specified limits if this is true (default is false)
    );

    // Normalize the histogram
    h.normalize();

    // Get a cumulative histogram from the histogram
    auto h2 = cumulative_histogram(h);

    return 0;
}


#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/fusion/include/std_pair.hpp>

#include <iostream>
#include <string>
#include <complex>

namespace client{
  template <typename Iterator>
    bool parse_complex(Iterator first, Iterator last, std::complex<double>& c){
      using boost::spirit::qi::double_;
      using boost::spirit::qi::_1;
      using boost::spirit::qi::phrase_parse;
      using boost::spirit::ascii::space;
      using boost::phoenix::ref;

      double rN = 0.0;
      double iN = 0.0;
      bool r = phrase_parse(first, last,
          (
            '(' >> double_[ref(rN) = _1]
                >> -(',' >> double_[ref(iN) = _1]) >> ')'
            | double_[ref(rN) = _1]
          ),
          space);

      if(!r || first != last)
        return false;
      c = std::complex<double>(rN, iN);
      return r;
    }

  template <typename OutputIterator>
    bool generate_complex(OutputIterator sink, std::complex<double> const& c){
      using boost::spirit::karma::eps;
      using boost::spirit::karma::double_;
      using boost::spirit::karma::_1;
      using boost::spirit::karma::generate;

      return generate(sink,
          (
            eps(c.imag() != 0) << '(' << double_[_1 = c.real()] << ", " << double_[_1 = c.imag()] << ')'
            | double_[_1 = c.real()]
          )
      );
    }
}

int main(){
  std::string str;
  while(getline(std::cin, str)){
    if(str.empty() || str[0] == 'q' || str[0] == 'Q')
      break;

    std::complex<double> c;
    if(client::parse_complex(str.begin(), str.end(), c)){
      std::string generated;
      std::back_insert_iterator<std::string> sink(generated);
      if(!client::generate_complex(sink, c)){
        std::cout << "Generating failed\n";
      }
      else{
        std::cout << "Generated: " << generated << std::endl;
      }
    }
    else{
      std::cout << "Parsing failed\n";
    }
  }

  return 0;
}


//==================================================================================================
/*!
  @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_REM_PIO2_CEPHES_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_REM_PIO2_CEPHES_HPP_INCLUDED

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

  /*!
    @ingroup group-trigonometric
    This function object returns the Computes the remainder modulo \f$\pi/2\f$ with cephes algorithm,
     and the angle quadrant between 0 and 3.

     This is a quick version accurate if the input is in \f$[-20\pi,20\pi]\f$.

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

    @par Note

      Using `std::tie(n, r) = rem_pio2_cephes(x)` is similar to
      `n = div(inearbyint, x, Pio_2<T>())` and `r = remainder(x, Pio_2<T>())`

    @par Example:

      @snippet rem_pio2_cephes.cpp rem_pio2_cephes

    @par Possible output:

      @snippet rem_pio2_cephes.txt rem_pio2_cephes

  **/
  std::pair<IEEEValue, IEEEValue> rem_pio2_cephes(IEEEValue const& x);
} }
#endif

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

#endif


//==================================================================================================
/**
  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)
**/
//==================================================================================================
#include <boost/simd/function/cbrt.hpp>
#include <boost/simd/function/saturated.hpp>
#include <boost/simd/pack.hpp>
#include <boost/simd/function/std.hpp>
#include <simd_test.hpp>

namespace bs = boost::simd;

template <typename T, std::size_t N, typename Env>
void test(Env& $)
{
  using p_t = bs::pack<T, N>;

  T a1[N], b[N], c[N];
  for(std::size_t i = 0; i < N; ++i)
  {
    a1[i] = (i%2) ? T(i) : T(-i);
    b[i] = bs::cbrt(a1[i]) ;
    c[i] = bs::std_(bs::cbrt)(a1[i]);
  }

  p_t aa1(&a1[0], &a1[0]+N);
  p_t bb (&b[0], &b[0]+N);
  p_t cc (&c[0], &c[0]+N);
  STF_ULP_EQUAL(bs::cbrt(aa1), bb,0.5);
  STF_ULP_EQUAL(bs::std_(bs::cbrt)(aa1), cc,0.5);
}

STF_CASE_TPL("Check cbrt on pack" , STF_IEEE_TYPES)
{
  static const std::size_t N = bs::pack<T>::static_size;

  test<T, N>($);
  test<T, N/2>($);
  test<T, N*2>($);
}


#include <cstdio>
#include <vector>
#include <cmath>
#include <boost/date_time/posix_time/posix_time.hpp>

#include "PhaseAFO.h"
#include "Integrator.h"

int main(int argc, char **argv)
{
  boost::posix_time::ptime run_start(boost::posix_time::microsec_clock::local_time());

  double dt = 0.0001;
  double save_dt = 0.001;

  double t_init = 0.0;
  double t_end = 10.0;

  Eigen::Vector2d y_init;
  y_init << 0.0, 10.0;

  afos::PhaseAFO my_afo;
  my_afo.initialize(1000.0, 1.);
  my_afo.input().sine(100.);
  euler_integration(my_afo, t_init, t_end,y_init,dt, save_dt);
  Eigen::VectorXd t = my_afo.t();
  Eigen::MatrixXd y = my_afo.y();

  //save file
  FILE *save_file = fopen("result.txt","w");
  for(int i=0; i<t.rows(); ++i)
    fprintf(save_file, "%f %f %f\n",t(i),y(0,i),y(1,i));
  fclose(save_file);

  boost::posix_time::ptime run_end(boost::posix_time::microsec_clock::local_time());
  boost::posix_time::time_duration run_duration = run_end - run_start;
  double cycle_duration = run_duration.total_microseconds()/1000000.0;
  printf("time taken: %f\n",cycle_duration);
  return 0;
}