// Copyright (c) 2012-2015 The FargoCoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "bloom.h"

#include "primitives/transaction.h"
#include "hash.h"
#include "script/script.h"
#include "script/standard.h"
#include "random.h"
#include "streams.h"

#include <math.h>
#include <stdlib.h>

#include <boost/foreach.hpp>

#define LN2SQUARED 0.4804530139182014246671025263266649717305529515945455
#define LN2 0.6931471805599453094172321214581765680755001343602552

using namespace std;

CBloomFilter::CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweakIn, unsigned char nFlagsIn) :
    /**
     * The ideal size for a bloom filter with a given number of elements and false positive rate is:
     * - nElements * log(fp rate) / ln(2)^2
     * We ignore filter parameters which will create a bloom filter larger than the protocol limits
     */
    vData(min((unsigned int)(-1  / LN2SQUARED * nElements * log(nFPRate)), MAX_BLOOM_FILTER_SIZE * 8) / 8),
    /**
     * The ideal number of hash functions is filter size * ln(2) / number of elements
     * Again, we ignore filter parameters which will create a bloom filter with more hash functions than the protocol limits
     * See https://en.wikipedia.org/wiki/Bloom_filter for an explanation of these formulas
     */
    isFull(false),
    isEmpty(false),
    nHashFuncs(min((unsigned int)(vData.size() * 8 / nElements * LN2), MAX_HASH_FUNCS)),
    nTweak(nTweakIn),
    nFlags(nFlagsIn)
{
}

// Private constructor used by CRollingBloomFilter
CBloomFilter::CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweakIn) :
    vData((unsigned int)(-1  / LN2SQUARED * nElements * log(nFPRate)) / 8),
    isFull(false),
    isEmpty(true),
    nHashFuncs((unsigned int)(vData.size() * 8 / nElements * LN2)),
    nTweak(nTweakIn),
    nFlags(BLOOM_UPDATE_NONE)
{
}

inline unsigned int CBloomFilter::Hash(unsigned int nHashNum, const std::vector<unsigned char>& vDataToHash) const
{
    // 0xFBA4C795 chosen as it guarantees a reasonable bit difference between nHashNum values.
    return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash) % (vData.size() * 8);
}

void CBloomFilter::insert(const vector<unsigned char>& vKey)
{
    if (isFull)
        return;
    for (unsigned int i = 0; i < nHashFuncs; i++)
    {
        unsigned int nIndex = Hash(i, vKey);
        // Sets bit nIndex of vData
        vData[nIndex >> 3] |= (1 << (7 & nIndex));
    }
    isEmpty = false;
}

void CBloomFilter::insert(const COutPoint& outpoint)
{
    CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
    stream << outpoint;
    vector<unsigned char> data(stream.begin(), stream.end());
    insert(data);
}

void CBloomFilter::insert(const uint256& hash)
{
    vector<unsigned char> data(hash.begin(), hash.end());
    insert(data);
}

bool CBloomFilter::contains(const vector<unsigned char>& vKey) const
{
    if (isFull)
        return true;
    if (isEmpty)
        return false;
    for (unsigned int i = 0; i < nHashFuncs; i++)
    {
        unsigned int nIndex = Hash(i, vKey);
        // Checks bit nIndex of vData
        if (!(vData[nIndex >> 3] & (1 << (7 & nIndex))))
            return false;
    }
    return true;
}

bool CBloomFilter::contains(const COutPoint& outpoint) const
{
    CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
    stream << outpoint;
    vector<unsigned char> data(stream.begin(), stream.end());
    return contains(data);
}

bool CBloomFilter::contains(const uint256& hash) const
{
    vector<unsigned char> data(hash.begin(), hash.end());
    return contains(data);
}

void CBloomFilter::clear()
{
    vData.assign(vData.size(),0);
    isFull = false;
    isEmpty = true;
}

void CBloomFilter::reset(unsigned int nNewTweak)
{
    clear();
    nTweak = nNewTweak;
}

bool CBloomFilter::IsWithinSizeConstraints() const
{
    return vData.size() <= MAX_BLOOM_FILTER_SIZE && nHashFuncs <= MAX_HASH_FUNCS;
}

bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx)
{
    bool fFound = false;
    // Match if the filter contains the hash of tx
    //  for finding tx when they appear in a block
    if (isFull)
        return true;
    if (isEmpty)
        return false;
    const uint256& hash = tx.GetHash();
    if (contains(hash))
        fFound = true;

    for (unsigned int i = 0; i < tx.vout.size(); i++)
    {
        const CTxOut& txout = tx.vout[i];
        // Match if the filter contains any arbitrary script data element in any scriptPubKey in tx
        // If this matches, also add the specific output that was matched.
        // This means clients don't have to update the filter themselves when a new relevant tx
        // is discovered in order to find spending transactions, which avoids round-tripping and race conditions.
        CScript::const_iterator pc = txout.scriptPubKey.begin();
        vector<unsigned char> data;
        while (pc < txout.scriptPubKey.end())
        {
            opcodetype opcode;
            if (!txout.scriptPubKey.GetOp(pc, opcode, data))
                break;
            if (data.size() != 0 && contains(data))
            {
                fFound = true;
                if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_ALL)
                    insert(COutPoint(hash, i));
                else if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_P2PUBKEY_ONLY)
                {
                    txnouttype type;
                    vector<vector<unsigned char> > vSolutions;
                    if (Solver(txout.scriptPubKey, type, vSolutions) &&
                            (type == TX_PUBKEY || type == TX_MULTISIG))
                        insert(COutPoint(hash, i));
                }
                break;
            }
        }
    }

    if (fFound)
        return true;

    BOOST_FOREACH(const CTxIn& txin, tx.vin)
    {
        // Match if the filter contains an outpoint tx spends
        if (contains(txin.prevout))
            return true;

        // Match if the filter contains any arbitrary script data element in any scriptSig in tx
        CScript::const_iterator pc = txin.scriptSig.begin();
        vector<unsigned char> data;
        while (pc < txin.scriptSig.end())
        {
            opcodetype opcode;
            if (!txin.scriptSig.GetOp(pc, opcode, data))
                break;
            if (data.size() != 0 && contains(data))
                return true;
        }
    }

    return false;
}

void CBloomFilter::UpdateEmptyFull()
{
    bool full = true;
    bool empty = true;
    for (unsigned int i = 0; i < vData.size(); i++)
    {
        full &= vData[i] == 0xff;
        empty &= vData[i] == 0;
    }
    isFull = full;
    isEmpty = empty;
}

CRollingBloomFilter::CRollingBloomFilter(unsigned int nElements, double fpRate) :
    b1(nElements * 2, fpRate, 0), b2(nElements * 2, fpRate, 0)
{
    // Implemented using two bloom filters of 2 * nElements each.
    // We fill them up, and clear them, staggered, every nElements
    // inserted, so at least one always contains the last nElements
    // inserted.
    nInsertions = 0;
    nBloomSize = nElements * 2;

    reset();
}

void CRollingBloomFilter::insert(const std::vector<unsigned char>& vKey)
{
    if (nInsertions == 0) {
        b1.clear();
    } else if (nInsertions == nBloomSize / 2) {
        b2.clear();
    }
    b1.insert(vKey);
    b2.insert(vKey);
    if (++nInsertions == nBloomSize) {
        nInsertions = 0;
    }
}

void CRollingBloomFilter::insert(const uint256& hash)
{
    vector<unsigned char> data(hash.begin(), hash.end());
    insert(data);
}

bool CRollingBloomFilter::contains(const std::vector<unsigned char>& vKey) const
{
    if (nInsertions < nBloomSize / 2) {
        return b2.contains(vKey);
    }
    return b1.contains(vKey);
}

bool CRollingBloomFilter::contains(const uint256& hash) const
{
    vector<unsigned char> data(hash.begin(), hash.end());
    return contains(data);
}

void CRollingBloomFilter::reset()
{
    unsigned int nNewTweak = GetRand(std::numeric_limits<unsigned int>::max());
    b1.reset(nNewTweak);
    b2.reset(nNewTweak);
    nInsertions = 0;
}


#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>

using namespace boost::gregorian;

int main()
{
  date d1{2014, 1, 1};
  date d2{2014, 2, 28};
  date_period dp{d1, d2};
  date_duration dd = dp.length();
  std::cout << dd.days() << '\n';
}


#include <iostream>
#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <string>
#include <Eigen/Dense>

#include "pointCloud.h"
#include "planeCloud.h"
#include "calibration.h"

//folder dump6 has good data in it

int main(int argc, char **argv)
{
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
    if(argc<3){
        std::cout<<"This node is intend to use offline"<<std::endl<<"provide a logfile (produced with the dumpernode) and the output calibration file you want"<<std::endl;
        std::cout<<"example:"<<std::endl<<"offlineCalibration <input log filename> <output calibration filename>"<<std::endl;
        return 0;
    }
    float c0,c1,c2,c3=0;
    c0=1.0f;
    if(argc==7){
        c3=atof(argv[3]);
        c2=atof(argv[4]);
        c1=atof(argv[5]);
        c0=atof(argv[6]);
        std::cout<<"polynomial coeffs: d^3*"<<c3<<" + d^2*"<<c2<<" + d*"<<c1<<" + "<<c0<<std::endl;
    }
    std::fstream log;
    log.open(argv[1]);
    string topic;
    int height;
    int width;
    log >> topic;
    log >> height;
    log >> width;
    Matrix3f k;
    log >> k(0,0);
    log >> k(0,1);
    log >> k(0,2);
    log >> k(1,0);
    log >> k(1,1);
    log >> k(1,2);
    log >> k(2,0);
    log >> k(2,1);
    log >> k(2,2);
    string filename;
    //calibrationMatrix c(480,640,8000,4,8);
    // int rows =240;
    // int cols = 320;
    // calibrationMatrix c(rows,cols,5000,2,4);

    int rows =480;
    int cols = 640;
    // calibrationMatrix c(rows,cols,2000,2,4);
    calibrationMatrix c(rows,cols,2000,4,8);

    //calibrationMatrix c(rows,cols,8000,4,64);
    while(!log.eof()){
        log>>filename;
        std::cout<< "opening "<<filename<<"\r"<<std::flush;
        cv::Mat data = cv::imread(filename,cv::IMREAD_ANYDEPTH);

        if(! data.data ) {
          cout <<  "Could not open or find the image" << std::endl ;
          return -1;
        }

        // cv::Mat img_cv_f;
        // data.convertTo(img_cv_f, CV_32FC1);
        // cv::normalize(img_cv_f, img_cv_f, 0, 1, cv::NORM_MINMAX, CV_32FC1);
        // cv::imshow("test",img_cv_f);
        // cv::waitKey(30);

        //std::cout << "calibration is" << k(0,0) << std::endl;

        pointCloud p(data,k);
        pcl::PointCloud<pcl::PointXYZ>* pcl = new pcl::PointCloud<pcl::PointXYZ>();
        pcl=p.pclCloud;

        //CENTER SQUARE CLOUD
        pcl::PointCloud<pcl::PointXYZ>* square= new pcl::PointCloud<pcl::PointXYZ>();
        calibration::computeCenterSquareCloud(data,k,square,cols/10,rows/10,c0,c1,c2,c3); //square will be 64x48
        std::cout << "center square has number of ponts: "  << square->size()<< std::endl;

        //CENTER PLANE
        Eigen::Vector4f centerModel;
        calibration::computerCenterPlane(square,centerModel,0.8);
        // planeCloud centerPlaneCloud;
        // centerPlaneCloud.model=centerModel;
        Eigen::Vector4f center;
        pcl::compute3DCentroid(*square,center);
        std::cout<<"CENTROID DISTANCE: "<<center[2]<<" ( "<<center.transpose()<<" )"<<std::endl;
        // centerPlaneCloud.com=center.head(3);

        //NORMALS
        pcl::PointCloud<pcl::PointXYZ>* p_normals = new pcl::PointCloud<pcl::PointXYZ>();
        pcl::copyPointCloud(*pcl, *p_normals);
        pcl::PointCloud<pcl::Normal>* normals= new pcl::PointCloud<pcl::Normal>();
        //calibration::computeNormals(p_normals,normals,100);

        //REJECTION
        Eigen::Vector3f ref;
        std::vector<bool> valid;
        ref<<centerModel[0],centerModel[1],centerModel[2];
        // pcl::PointCloud<pcl::PointXYZ> outFromNormalRejection;
        // pcl::PointCloud<pcl::PointXYZ>* tmpCloud = new pcl::PointCloud<pcl::PointXYZ>();
        // copyPointCloud(*pcl, *tmpCloud);
        // std::cout << "size of tmpcloud cloud is" << tmpCloud->size() << std::endl;
        //pcl::PointCloud<pcl::PointXYZ>* tmpCloud =p2;
        //calibration::pointrejection(&ref,0.7f,tmpCloud,normals,&outFromNormalRejection,&valid);

        //pointCloud outFromNormalRejectionCloud(&outFromNormalRejection);
        //ERROR PER POINT
        pcl::PointCloud<pcl::PointXYZ> error;
        calibration::computeErrorPerPoint(pcl,&error,center.head(3),ref,&valid);

        //COMPUTE CALIBRATION MATRIX
        calibration::computeCalibrationMatrix(*pcl,error,k,&valid,c);

        //CALIBRATE POINT CLOUD
        //pcl::PointCloud<pcl::PointXYZ> fixedCloud;
        //calibration::calibratePointCloudWithMultipliers(*cloudToCalibrate,fixedCloud,c,k);

        std::cout << "finished one loop clearing" << std::endl;

        error.clear();
        valid.clear();
        error.clear();
        p_normals->clear();
        delete p_normals;
        //tmpCloud->clear();
        //delete tmpCloud;
        //p2.cloud.clear();
        normals->clear();
        //outFromNormalRejection.clear();
        valid.clear();
        delete normals;

        square->clear();
        delete square;
        pcl->clear();
        delete pcl;
    }

    //fill the rest of the data in the calibration matrix that is missing
    // calibration::calibration_matrix_fill(c);
    //calibration::smooth_data(c);

    std::cout<<std::endl<<"saving "<<std::endl;
    c.dumpSensorImages();
    c.serialize(argv[2]);
    char nn[500];
    std::cout<<"saving nn"<<std::endl;
    sprintf(nn,"NN_%s",argv[2]);
    //c.serializeNN(nn);
    //calibrationMatrix* cc = c.downsample(2,2);
    //cc->serialize(argv[2]);

}


#pragma once

#include "vec3f.hxx"
#include "spectral.hxx"
#include "texture.hxx"
#include "sampler.hxx"
#include "ray.hxx"
#include "scene.hxx"

#include <numeric> // for iota on Windows
#include <boost/container/small_vector.hpp>

struct LambdaSelection
{
  Index3 indices;  // Wavelength bin indices.
  Spectral3 weights; // Monte-carlo weight comprising sensor sensitivity over selection probability.
  Wavelengths3 wavelengths; // The actual wavelengths
};

enum TransportType : char
{
  RADIANCE,
  IMPORTANCE
};

struct PathContext
{
  PathContext() = default;
  PathContext(const LambdaSelection &_lambdas, TransportType _transport, int pixel_index_)
    : lambda_idx(_lambdas.indices), wavelengths{_lambdas.wavelengths}, transport(_transport), pixel_index{pixel_index_}
  {}
  PathContext(const LambdaSelection &_lambdas, int pixel_index_ = -1)
    : PathContext{_lambdas, RADIANCE, pixel_index_}
  {}
  Index3 lambda_idx {};
  Wavelengths3 wavelengths {};
  TransportType transport { RADIANCE };
  int pixel_index = -1;
};

struct LambdaSelectionStrategy
{
  /* Stratified mode: Divide spectrum in N sections, where N is the number of simultaneously traced wavelengths.
   * Pick one wavelength from each section. In my case I pick the wavelength from the first section at random,
   * and take the others at a fixed offset equal to the stratum size. */
  static constexpr int strata_size = Color::NBINS / Spectral3::RowsAtCompileTime;
  static_assert(Color::NBINS == strata_size * Spectral3::RowsAtCompileTime, "Bin count must be multiple of number of simultaneously traced wavelengths");

  SpectralN lambda_weights;
  LambdaSelectionStrategy()
  {
    // One over the probability, that the wavelength is selected.
    lambda_weights.setConstant(strata_size);
  }

  static Index3 MakeIndices(int main_idx)
  {
    return Index3{main_idx, main_idx+strata_size, main_idx+2*strata_size};
  }

  static int PrimaryIndex(const Index3 &idx)
  {
    return idx[0];
  }

  static Wavelengths3 SampleWavelengthStrata(const Index3 &bin_indices, Sampler &sampler)
  {
    Wavelengths3 wl;
    for (int i=0; i<static_size<Spectral3>(); ++i)
    {
      auto bounds  = Color::GetWavelengthBinBounds(bin_indices[i]);
      wl[i] = bounds.first + sampler.GetRandGen().Uniform01()*(bounds.second-bounds.first);
    }
    return wl;
  }

  LambdaSelection WithWeights(Sampler &sampler) const
  {
    int main_idx = sampler.GetRandGen().UniformInt(0, strata_size-1); // TODO: support stratified sampling? But why when I have the shuffling thing down there.
    auto idx     = MakeIndices(main_idx);
    int rotation = sampler.GetRandGen().UniformInt(0, 2);
    std::swap(idx[0], idx[rotation]);
    auto weights = Take(lambda_weights, idx);
    return LambdaSelection{idx, weights, SampleWavelengthStrata(idx, sampler)};
  }
};

class LambdaSelectionStrategyShuffling
{
  /* Stratified mode: Divide spectrum in N sections, where N is the number of simultaneously traced wavelengths.
   * Pick one wavelength from each section. In my case I pick the wavelength from the first section at random,
   * and take the others at a fixed offset equal to the stratum size. */
  static constexpr int strata_size = Color::NBINS / Spectral3::RowsAtCompileTime;
  static_assert(Color::NBINS == strata_size * Spectral3::RowsAtCompileTime, "Bin count must be multiple of number of simultaneously traced wavelengths");
  std::array<int,strata_size> current_selection_permutation;
  int current_idx = strata_size;

  void Shuffle(Sampler &sampler)
  {
    RandomShuffle(
      current_selection_permutation.begin(),
      current_selection_permutation.end(),
      sampler.GetRandGen());
  }

public:
  LambdaSelectionStrategyShuffling()
  {
    // One over the probability, that the wavelength is selected.
    std::iota(current_selection_permutation.begin(), current_selection_permutation.end(), 0);
  }

  static constexpr int NUM_SAMPLES_REQUIRED = strata_size; // On average to make a full sweep across the spectrum, i.e. to have all wavelengths covered.

  static Index3 MakeIndices(int main_idx, Sampler &sampler)
  {
    Index3 ret{main_idx, main_idx+strata_size, main_idx+2*strata_size};
    // Permutation does nothing except when doing spectral rendering.
    // In this case I can simply use the wavelength of the first index because
    // it has equal chance of being in the R, G, or B stratum.
    // To render a prism, for instance, the first wavelength would be taken to
    // determine the index of refraction and the contribution of the other
    // two wavelengths would be zero'd out.
    RandomShuffle(ret.data(), ret.data()+ret.size(), sampler.GetRandGen());
    return ret;
  }

  static int PrimaryIndex(const Index3 &idx)
  {
    return idx[0];
  }

  LambdaSelection WithWeights(Sampler &sampler)
  {
    if (++current_idx >= current_selection_permutation.size())
    {
      Shuffle(sampler);
      current_idx = 0;
    }
    int lambda_idx = current_selection_permutation[current_idx];
    auto idx     = MakeIndices(lambda_idx, sampler);
    auto weights = Spectral3{strata_size};
    return LambdaSelection{idx, weights, LambdaSelectionStrategy::SampleWavelengthStrata(idx, sampler)};
  }
};

inline LambdaSelection SelectRgbPrimaryWavelengths()
{
  Index3 idx = Color::LambdaIdxClosestToRGBPrimaries();
  return {
    idx,
    Spectral3::Ones(),
    Take(Color::GetWavelengths(),idx)
  };
}

class PiecewiseConstantTransmittance
{
  static constexpr int PIECEWISE_STATIC_ALLOC_SIZE = 96;
  using Boundaries = boost::container::small_vector<float, PIECEWISE_STATIC_ALLOC_SIZE>;
  using Weights = boost::container::small_vector<Spectral3, PIECEWISE_STATIC_ALLOC_SIZE>;
  Boundaries boundaries;
  Weights weights;
public:
  void PushBack(float t, const Spectral3 &weight)
  {
    assert(boundaries.empty() || t>=boundaries.back());
    boundaries.push_back(t);
    weights.push_back(weight);
  }

  float End() const
  {
    assert(!boundaries.empty());
    return boundaries.back();
  }

  void Clear()
  {
    boundaries.clear();
    weights.clear();
  }

  // Lookup
  Spectral3 operator()(float t) const
  {
    auto it = std::lower_bound(boundaries.begin(), boundaries.end(), t);
    if (it != boundaries.end())
    {
      assert (t <= boundaries.back());
      return weights[it-boundaries.begin()];
    }
    else // t is larger than last element in the sequence
      return Spectral3::Zero();
  }
};

inline const Material& GetMaterialOf(const SurfaceInteraction &ia, const Scene &scene)
{
  return scene.GetMaterialOf(ia.hitid);
}

//inline const Medium& GetMediumOf(const SurfaceInteraction &ia, const Scene &scene)
//{
//  return *ASSERT_NOT_NULL(GetMaterialOf(ia, scene).medium);
//}

inline const Shader& GetShaderOf(const SurfaceInteraction &ia, const Scene &scene)
{
  return *ASSERT_NOT_NULL(GetMaterialOf(ia, scene).shader);
}

inline double BsdfCorrectionFactorPBRT(const Double3 &reverse_incident_dir, const SurfaceInteraction &intersection, const Double3 &exitant_dir, double clamp)
{
  double nom = std::abs(Dot(intersection.shading_normal, reverse_incident_dir)) * std::abs(Dot(intersection.normal, exitant_dir));
  //           '-------------------------^------------------------------------'   '------------------^--------------------------'
  //                                From shading correction.                                  The D-factor, which uses geometry normal. Often part of geometry factor.
  double denom = std::abs(Dot(intersection.normal, reverse_incident_dir)) * std::abs(Dot(intersection.shading_normal, exitant_dir));
  //           '-------------------------^-------------------------------------'   '------------------^-------------------------'
  //                                From shading correction                                   Cancel out "fake" D-factor, which uses the shading normal.
  //                                                                                Note the fake D-factor should be multiplied separately to cancel
  //                                                                                the corresponding 1/wr.ns term of specular BSDFs. (*)
  //  (*)  This is the important difference to my non-PBRT style correction. Here we cancel wr.ns of the specular BSDF exactly, whereas the other correction
  //  routine can let wr.ng / wr.ns grow a lot. Here we have  wr.ns / wr.ns * clamp(wr.ns * ....).
  //
  //  One more note: For photon mapping, Veach suggests to split particles. See pg. 160.
  return std::min(nom/(denom + Epsilon), clamp);
}

inline double DFactorPBRT(const SurfaceInteraction &intersection, const Double3 &exitant_dir)
{
  // By definition the factor is 1 for Volumes.
  // This one uses the shading normal. Use with PBRT style shading normal correction!
  return std::abs(Dot(intersection.shading_normal, exitant_dir));
}


// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
// Copyright (c) 2018-2021 The SorachanCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <main.h>
#include <rpc/bitcoinrpc.h>
#include <init.h>
#include <boost/filesystem.hpp>
#include <boost/iostreams/device/file.hpp>
#include <boost/iostreams/stream.hpp>
#include <ostream>
#include <thread> // CWaitforthread
#include <miner/diff.h>

double CRPCTable::GetDifficulty(const CBlockIndex *blockindex/* = nullptr */) noexcept {
    // Floating point number that is a multiple of the minimum difficulty,
    // minimum difficulty = 1.0.
    if (blockindex == nullptr) {
        if (block_info::pindexBest == nullptr)
            return 1.0;
        else
            blockindex = diff::spacing::GetLastBlockIndex(block_info::pindexBest, false);
    }

    int nShift = (blockindex->get_nBits() >> 24) & 0xff;
    double dDiff = (double)0x0000ffff / (double)(blockindex->get_nBits() & 0x00ffffff);
    while (nShift < 29) {
        dDiff *= 256.0;
        nShift++;
    }
    while (nShift > 29) {
        dDiff /= 256.0;
        nShift--;
    }

    return dDiff;
}

double CRPCTable::GetPoWMHashPS() noexcept {
    int nPoWInterval = 72;
    int64_t nTargetSpacingWorkMin = 30, nTargetSpacingWork = 30;

    CBlockIndex *pindex = block_info::pindexGenesisBlock;
    CBlockIndex *pindexPrevWork = block_info::pindexGenesisBlock;
    while (pindex) {
        if (pindex->IsProofOfWork()) {
            int64_t nActualSpacingWork = pindex->GetBlockTime() - pindexPrevWork->GetBlockTime();
            nTargetSpacingWork = ((nPoWInterval - 1) * nTargetSpacingWork + nActualSpacingWork + nActualSpacingWork) / (nPoWInterval + 1);
            nTargetSpacingWork = std::max(nTargetSpacingWork, nTargetSpacingWorkMin);
            pindexPrevWork = pindex;
        }

        pindex = pindex->set_pnext();
    }

    return GetDifficulty() * 4294.967296 / nTargetSpacingWork;
}

double CRPCTable::GetPoSKernelPS() noexcept {
    int nPoSInterval = 72;
    double dStakeKernelsTriedAvg = 0;
    int nStakesHandled = 0, nStakesTime = 0;

    CBlockIndex *pindex = block_info::pindexBest;
    CBlockIndex *pindexPrevStake = nullptr;
    while (pindex && nStakesHandled < nPoSInterval) {
        if (pindex->IsProofOfStake()) {
            dStakeKernelsTriedAvg += GetDifficulty(pindex) * 4294967296.0;
            nStakesTime += pindexPrevStake ? (pindexPrevStake->get_nTime() - pindex->get_nTime()) : 0;
            pindexPrevStake = pindex;
            nStakesHandled++;
        }

        pindex = pindex->set_pprev();
    }
    if (! nStakesHandled)
        return 0;

    return dStakeKernelsTriedAvg / nStakesTime;
}

json_spirit::Object CRPCTable::blockToJSON(const CBlock &block, const CBlockIndex *blockindex, bool fPrintTransactionDetail) {
    json_spirit::Object result;
    result.push_back(json_spirit::Pair("hash", block.GetPoHash().GetHex()));

    CMerkleTx txGen(block.get_vtx(0));
    txGen.SetMerkleBranch(&block);

    result.push_back(json_spirit::Pair("confirmations", (int)txGen.GetDepthInMainChain()));
    result.push_back(json_spirit::Pair("size", (int)::GetSerializeSize(block)));
    result.push_back(json_spirit::Pair("height", blockindex->get_nHeight()));
    result.push_back(json_spirit::Pair("version", block.get_nVersion()));
    result.push_back(json_spirit::Pair("merkleroot", block.get_hashMerkleRoot().GetHex()));
    result.push_back(json_spirit::Pair("mint", ValueFromAmount(blockindex->get_nMint())));
    result.push_back(json_spirit::Pair("time", (int64_t)block.GetBlockTime()));
    result.push_back(json_spirit::Pair("nonce", (uint64_t)block.get_nNonce()));
    result.push_back(json_spirit::Pair("bits", HexBits(block.get_nBits())));
    result.push_back(json_spirit::Pair("difficulty", GetDifficulty(blockindex)));
    result.push_back(json_spirit::Pair("blocktrust", util::leftTrim(blockindex->GetBlockTrust().GetHex(), '0')));
    result.push_back(json_spirit::Pair("chaintrust", util::leftTrim(blockindex->get_nChainTrust().GetHex(), '0')));
    if (blockindex->get_pprev())
        result.push_back(json_spirit::Pair("previousblockhash", blockindex->get_pprev()->GetBlockHash().GetHex()));
    if (blockindex->get_pnext())
        result.push_back(json_spirit::Pair("nextblockhash", blockindex->get_pnext()->GetBlockHash().GetHex()));

    result.push_back(json_spirit::Pair("flags", tfm::format("%s%s", blockindex->IsProofOfStake()? "proof-of-stake" : "proof-of-work", blockindex->GeneratedStakeModifier()? " stake-modifier": "")));
    result.push_back(json_spirit::Pair("proofhash", blockindex->IsProofOfStake()? blockindex->get_hashProofOfStake().GetHex() : blockindex->GetBlockHash().GetHex()));
    result.push_back(json_spirit::Pair("entropybit", (int)blockindex->GetStakeEntropyBit()));
    result.push_back(json_spirit::Pair("modifier", tfm::format("%016" PRIx64, blockindex->get_nStakeModifier())));
    result.push_back(json_spirit::Pair("modifierchecksum", tfm::format("%08x", blockindex->get_nStakeModifierChecksum())));

    json_spirit::Array txinfo;
    for (const CTransaction &tx: block.get_vtx()) {
        if (fPrintTransactionDetail) {
            CDataStream ssTx(SER_NETWORK, version::PROTOCOL_VERSION);
            ssTx << tx;
            std::string strHex = util::HexStr(ssTx.begin(), ssTx.end());

            txinfo.push_back(strHex);
        } else
            txinfo.push_back(tx.GetHash().GetHex());
    }

    result.push_back(json_spirit::Pair("tx", txinfo));
    if (block.IsProofOfStake())
        result.push_back(json_spirit::Pair("signature", util::HexStr(block.get_vchBlockSig().begin(), block.get_vchBlockSig().end())));

    return result;
}

json_spirit::Value CRPCTable::getbestblockhash(const json_spirit::Array &params, CBitrpcData &data) noexcept {
    if (data.fHelp() || params.size() != 0) {
        return data.JSONRPCSuccess(
            "getbestblockhash\n"
            "Returns the hash of the best block in the longest block chain.");
    }

    return data.JSONRPCSuccess(block_info::hashBestChain.GetHex());
}

json_spirit::Value CRPCTable::getblockcount(const json_spirit::Array &params, CBitrpcData &data) noexcept {
    if (data.fHelp() || params.size() != 0) {
        return data.JSONRPCSuccess(
            "getblockcount\n"
            "Returns the number of blocks in the longest block chain.",
            "(numeric) The current block count.\n",
            "\"getblockcount\", \"\"\n");
    }

    return data.JSONRPCSuccess(block_info::nBestHeight);
}

/*
class CWaitforthread {
private:
    mutable std::thread th;
public:
    CWaitforthread() noexcept : th([]{::Sleep(100);}) {}
    void wait() const noexcept {th.join();}
};

// waitforblock
*/

json_spirit::Value CRPCTable::getdifficulty(const json_spirit::Array &params, CBitrpcData &data) noexcept {
    if (data.fHelp() || params.size() != 0) {
        return data.JSONRPCSuccess(
            "getdifficulty\n"
            "Returns the difficulty as a multiple of the minimum difficulty.");
    }

    json_spirit::Object obj;
    obj.push_back(json_spirit::Pair("proof-of-work", GetDifficulty()));
    obj.push_back(json_spirit::Pair("proof-of-stake", GetDifficulty(diff::spacing::GetLastBlockIndex(block_info::pindexBest, true))));
    obj.push_back(json_spirit::Pair("search-interval", (int)block_info::nLastCoinStakeSearchInterval));
    return data.JSONRPCSuccess(obj);
}

json_spirit::Value CRPCTable::settxfee(const json_spirit::Array &params, CBitrpcData &data) noexcept {
    data.e = "amount";
    if (data.fHelp() || params.size() < 1 || params.size() > 1 || AmountFromValue(params[0], data) < block_params::MIN_TX_FEE) {
        return data.JSONRPCSuccess(
            "settxfee <amount>\n"
            "<amount> is a real and is rounded to the nearest " + strenc::FormatMoney(block_params::MIN_TX_FEE));
    }
    if(! data.fSuccess()) return data.JSONRPCError();

    data.e = "Transaction";
    block_info::nTransactionFee = CRPCTable::AmountFromValue(params[0], data);
    if(! data.fSuccess()) return data.JSONRPCError();
    block_info::nTransactionFee = (block_info::nTransactionFee / block_params::MIN_TX_FEE) * block_params::MIN_TX_FEE;  // round to minimum fee
    return data.JSONRPCSuccess(true);
}

json_spirit::Value CRPCTable::getrawmempool(const json_spirit::Array &params, CBitrpcData &data) {
    if (data.fHelp() || params.size() != 0) {
        return data.JSONRPCSuccess(
            "getrawmempool\n"
            "Returns all transaction ids in memory pool.");
    }

    std::vector<uint256> vtxid;
    CTxMemPool::mempool.queryHashes(vtxid);
    json_spirit::Array a;
    for(const uint256 &hash: vtxid)
        a.push_back(hash.ToString());

    return data.JSONRPCSuccess(a);
}

json_spirit::Value CRPCTable::getblockhash(const json_spirit::Array &params, CBitrpcData &data) noexcept {
    if (data.fHelp() || params.size() != 1) {
        return data.JSONRPCSuccess(
            "getblockhash <index>\n"
            "Returns hash of block in best-block-chain at <index>.");
    }

    json_spirit::json_flags status;
    int nHeight = params[0].get_int(status);
    if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
    if (nHeight < 0 || nHeight > block_info::nBestHeight)
        return data.runtime_error("Block number out of range.");

    CBlockIndex *pblockindex = block_transaction::manage::FindBlockByHeight(nHeight);
    return data.JSONRPCSuccess(pblockindex->get_phashBlock()->GetHex());
}

// SorachanCoin: QHASH65536
json_spirit::Value CRPCTable::getblockqhash(const json_spirit::Array &params, CBitrpcData &data) noexcept {
    if (data.fHelp() || params.size() != 1) {
        return data.JSONRPCSuccess(
            "getblockqhash <index>\n"
            "Returns QHASH65536 of block in best-block-chain at <index>.");
    }

    json_spirit::json_flags status;
    int nHeight = params[0].get_int(status);
    if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
    if (nHeight < 0 || nHeight > block_info::nBestHeight)
        return data.runtime_error("Block number out of range.");

    CBlockIndex *pblockindex = block_transaction::manage::FindBlockByHeight(nHeight);
    uint65536 tt = bitscrypt::scrypt_blockhash_65536((const uint8_t *)pblockindex);
    //uint65536 tt = 0;
    return data.JSONRPCSuccess(tt.GetHex());
}

// SorachanCoin: BLOCK_HASH_MODIFIER
json_spirit::Value CRPCTable::getblockmodifierhash(const json_spirit::Array &params, CBitrpcData &data) noexcept {
    if (data.fHelp() || params.size() != 1) {
        return data.JSONRPCSuccess(
            "getblockmodifierhash <index>\n"
            "Returns BLOCK_HASH_MODIFIER of block in best-block-chain at <index>.");
    }

    json_spirit::json_flags status;
    int nHeight = params[0].get_int(status);
    if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
    if (nHeight < 0 || nHeight > block_info::nBestHeight)
        return data.runtime_error("Block number out of range.");

    CBlockIndex *pblockindex = block_transaction::manage::FindBlockByHeight(nHeight);
    uint256 hash = pblockindex->GetBlockHash();

    BlockHeight::const_iterator mi = block_info::mapBlockLyraHeight.find(hash);
    if(mi==block_info::mapBlockLyraHeight.end()) {
        return data.runtime_error("Block number out of range.");
    }

    return data.JSONRPCSuccess((*mi).second.GetBlockModifierHash().GetHex());
}

json_spirit::Value CRPCTable::getblock(const json_spirit::Array &params, CBitrpcData &data) {
    if (data.fHelp() || params.size() < 1 || params.size() > 2) {
        return data.JSONRPCSuccess(
            "getblock <hash> [txinfo]\n"
            "txinfo optional to print more detailed tx info\n"
            "Returns details of a block with given block-hash.");
    }

    json_spirit::json_flags status;
    std::string strHash = params[0].get_str(status);
    if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
    uint256 hash(strHash);
    if (block_info::mapBlockIndex.count(hash) == 0)
        return data.JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");

    CBlock block;
    CBlockIndex *pblockindex = block_info::mapBlockIndex[hash];
    block.ReadFromDisk(pblockindex, true);
    bool fparam1 = false;
    if(params.size()>1) {
        fparam1 = params[1].get_bool(status);
        if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
    }
    return data.JSONRPCSuccess(blockToJSON(block, pblockindex, params.size() > 1 ? fparam1 : false));
}

json_spirit::Value CRPCTable::getblockbynumber(const json_spirit::Array &params, CBitrpcData &data) {
    if (data.fHelp() || params.size() < 1 || params.size() > 2) {
        return data.JSONRPCSuccess(
            "getblockbynumber <number> [txinfo]\n"
            "txinfo optional to print more detailed tx info\n"
            "Returns details of a block with given block-number.");
    }

    json_spirit::json_flags status;
    int nHeight = params[0].get_int(status);
    if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
    if (nHeight < 0 || nHeight > block_info::nBestHeight)
        return data.runtime_error("Block number out of range.");

    CBlock block;
    CBlockIndex *pblockindex = block_info::mapBlockIndex[block_info::hashBestChain];
    while (pblockindex->get_nHeight() > nHeight)
        pblockindex = pblockindex->set_pprev();

    pblockindex = block_info::mapBlockIndex[*pblockindex->get_phashBlock()];
    block.ReadFromDisk(pblockindex, true);
    bool fparam1 = false;
    if(params.size()>1) {
        fparam1 = params[1].get_bool(status);
        if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
    }
    return data.JSONRPCSuccess(blockToJSON(block, pblockindex, params.size() > 1 ? fparam1 : false));
}

static bool ExportBlock(const std::string &strBlockHash, const CDataStream &ssBlock) {
    fs::path pathDest = iofs::GetDataDir() / strBlockHash;
    if (fs::is_directory(pathDest))
        pathDest /= strBlockHash;

    try {
        boost::iostreams::stream_buffer<boost::iostreams::file_sink> buf(pathDest.string());
        std::ostream exportStream(&buf);
        exportStream << util::HexStr(ssBlock.begin(), ssBlock.end());
        exportStream.flush();

        logging::LogPrintf("Successfully exported block to %s\n", pathDest.string().c_str());
        return true;
    } catch(const boost::filesystem::filesystem_error &e) {
        logging::LogPrintf("error exporting the block data %s (%s)\n", pathDest.string().c_str(), e.what());
        return false;
    }
}

json_spirit::Value CRPCTable::dumpblock(const json_spirit::Array &params, CBitrpcData &data) {
    if (data.fHelp() || params.size() < 1 || params.size() > 2) {
        return data.JSONRPCSuccess(
            "dumpblock <hash> [destination]\n"
            "Returns serialized contents of a block with given block-hash.");
    }

    json_spirit::json_flags status;
    std::string strHash = params[0].get_str(status);
    if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
    uint256 hash(strHash);
    if (block_info::mapBlockIndex.count(hash) == 0)
        return data.JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");

    CBlock block;
    CBlockIndex *pblockindex = block_info::mapBlockIndex[hash];
    block.ReadFromDisk(pblockindex, true);
    CDataStream ssBlock(SER_NETWORK, version::PROTOCOL_VERSION);
    ssBlock << block;
    if (params.size() > 1) {
        json_spirit::json_flags status;
        std::string str = params[1].get_str(status);
        if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
        return data.JSONRPCSuccess(ExportBlock(str, ssBlock));
    }

    return data.JSONRPCSuccess(util::HexStr(ssBlock.begin(), ssBlock.end()));
}

json_spirit::Value CRPCTable::dumpblockbynumber(const json_spirit::Array &params, CBitrpcData &data) {
    if (data.fHelp() || params.size() < 1 || params.size() > 2) {
        return data.JSONRPCSuccess(
            "dumpblockbynumber <number>  [destination]\n"
            "Returns serialized contents of a block with given block-number.");
    }

    json_spirit::json_flags status;
    int nHeight = params[0].get_int(status);
    if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
    if (nHeight < 0 || nHeight > block_info::nBestHeight)
        return data.runtime_error("Block number out of range.");

    CBlock block;
    CBlockIndex *pblockindex = block_info::mapBlockIndex[block_info::hashBestChain];
    while (pblockindex->get_nHeight() > nHeight)
        pblockindex = pblockindex->set_pprev();

    pblockindex = block_info::mapBlockIndex[*pblockindex->get_phashBlock()];
    block.ReadFromDisk(pblockindex, true);
    CDataStream ssBlock(SER_NETWORK, version::PROTOCOL_VERSION);
    ssBlock << block;
    if (params.size() > 1) {
        json_spirit::json_flags status;
        std::string str = params[1].get_str(status);
        if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
        return data.JSONRPCSuccess(ExportBlock(str, ssBlock));
    }

    return data.JSONRPCSuccess(util::HexStr(ssBlock.begin(), ssBlock.end()));
}

// get information of sync-checkpoint
json_spirit::Value CRPCTable::getcheckpoint(const json_spirit::Array &params, CBitrpcData &data) {
    if (data.fHelp() || params.size() != 0) {
        return data.JSONRPCSuccess(
            "getcheckpoint\n"
            "Show info of synchronized checkpoint.\n");
    }

    json_spirit::Object result;
    result.push_back(json_spirit::Pair("synccheckpoint", Checkpoints::manage::getHashSyncCheckpoint().ToString().c_str()));

    CBlockIndex *pindexCheckpoint = block_info::mapBlockIndex[Checkpoints::manage::getHashSyncCheckpoint()];
    result.push_back(json_spirit::Pair("height", pindexCheckpoint->get_nHeight()));
    result.push_back(json_spirit::Pair("timestamp", util::DateTimeStrFormat(pindexCheckpoint->GetBlockTime()).c_str()));
    if (Checkpoints::checkpointMessage.Get_vchSig().size() != 0) {
        json_spirit::Object msgdata;
        CUnsignedSyncCheckpoint checkpoint;

        CDataStream sMsg(Checkpoints::checkpointMessage.Get_vchMsg(), SER_NETWORK, version::PROTOCOL_VERSION);
        sMsg >> checkpoint;

        json_spirit::Object parsed; // message version and data (block hash)
        parsed.push_back(json_spirit::Pair("version", checkpoint.Get_nVersion()));
        parsed.push_back(json_spirit::Pair("hash", checkpoint.Get_hashCheckpoint().GetHex().c_str()));
        msgdata.push_back(json_spirit::Pair("parsed", parsed));

        json_spirit::Object raw; // raw checkpoint message data
        raw.push_back(json_spirit::Pair("data", util::HexStr(Checkpoints::checkpointMessage.Get_vchMsg()).c_str()));
        raw.push_back(json_spirit::Pair("signature", util::HexStr(Checkpoints::checkpointMessage.Get_vchSig()).c_str()));
        msgdata.push_back(json_spirit::Pair("raw", raw));

        result.push_back(json_spirit::Pair("data", msgdata));
    }

    // Check that the block satisfies synchronized checkpoint
    if (Checkpoints::CheckpointsMode == Checkpoints::STRICT)
        result.push_back(json_spirit::Pair("policy", "strict"));
    if (Checkpoints::CheckpointsMode == Checkpoints::ADVISORY)
        result.push_back(json_spirit::Pair("policy", "advisory"));
    if (Checkpoints::CheckpointsMode == Checkpoints::PERMISSIVE)
        result.push_back(json_spirit::Pair("policy", "permissive"));
    if (map_arg::GetMapArgsCount("-checkpointkey"))
        result.push_back(json_spirit::Pair("checkpointmaster", true));

    return data.JSONRPCSuccess(result);
}


#include<iostream>
#include "mapdata.h++"
#include "statusbar.h++"
#include "hintbox.h++"
#include "pathfinder.h++"
#include <vector>
#include <cstdlib>
#include <ctime>
#include<sstream>
#include <algorithm>
#include <functional>
#include <memory>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/lambda/construct.hpp>
#include <boost/lambda/if.hpp>
#include <boost/lambda/algorithm.hpp>
#include "playerassets.h++"
#include<ext/functional>

#include <boost/thread/mutex.hpp>
#include <rexio/keyboard.h++>

#include "entity_types.h++"
#include "entity.h++"

using namespace std;
using namespace boost::lambda;
#if defined _GNU_SOURCE
using __gnu_cxx::compose2;
using __gnu_cxx::compose1;
#endif

Scr::Uint Distance(Scr::Position s, Scr::Position f)
{
	// a bit more complex formula than sqrt((Ax-Bx)^2+(Ay-By)^2), as
	// we are not in relam of Euclidean geometry when processing hex
	// board. (luckily no roots here: only basic arithmetic and logic)
	using namespace Scr;
	Sint i_result_x = s.col-f.col;
	Sint i_result_y = s.row-f.row;
	Uint result_x = abs(i_result_x);
	Uint result_y = abs(i_result_y);

	bool column_odd = s.col & 1;

	result_y+=// increment if expression evaluates true
		( ( ( f.col & 1 ) != column_odd ) // both odd or both even
		  and ( ( f.row < s.row ) == column_odd ) );

	Uint rx2 = (result_x>>1) + (result_x&1);// ceil(result_x/2.0);
	if (rx2<=result_y)
		result_y-=rx2;
	else
		result_y=0;
	return result_x+result_y;
}

// 	const std::string & GetHPDesc() const  // damage level
// 		{
// 			switch (hp)
// 			{
// 			case Nothing:
// 				static const string S1("OK");
// 				return S1;
// 			case LightlyDamaged:
// 				static const string S2("wounded");
// 				return S2;
// 			case HeavilyDamaged:
// 				static const string S3("heavily wounded");
// 				return S3;
// 			case Destroyed:
// 			default:
// 				static const string S4("mortally wounded");
// 				return S4;
// 			}
// 		}

const char MapData::Oil = 'o';
const char MapData::Road = '=';

MapData::MapField::MapField():resources(' '),terrain_type(' '),ZoI1(0)
{

}

void MapData::MapField::RemoveEntity()
{
	pE1.reset();
}

void MapData::MapField::SetZoI (PlayerAssets * ZoI)
{
	if (pE1.get() and ZoI!=ZoI1)
		if (not pE1->GetMorale())
	{// for entities w/o Morale: surrender to new player
		pE1->SetAllegiance(ZoI);
		ZoI->InsertEntity(pE1);
		if (ZoI1)
			ZoI1->RemoveEntity(pE1);
	}
	ZoI1=ZoI;

}

const std::string MapData::MapField::GetStatus() const
{
	if (pE1)
		return pE1->GetName() +
			((pE1->GetAllegiance())?
			 ( string(" (")+pE1->GetAllegiance()->GetName() + ')' ):(""))
			+ ((resources==MapData::Oil)?(", oilfield."):(""));
	if (resources==MapData::Oil)
		return "Oilfield here!";
	else
		return "Nothing worth looking:(";

}

HP MapData::MapField::Attack(int damage)
{
	if (not pE1)
		return Nothing;
	else
	{
		HP hp=pE1->AcceptAttack(damage);
		return hp;
	}
}

MapData::MapData(): Fields(boost::extents[0][0])
{
	// initialize as empty map (must manually fill it or randomize to
	// get anything playable
}

void MapData::Randomize(PlayerList & PL,int h, int w)
{
	// Function object, that creates roads
	class
	{
	private:
		auto_ptr<PathFinder> PF1;
	public:
		void operator() (MapData * pMD1,
						 const Scr::Position & P1,
						 const Scr::Position & P2)
			{
				using Scr::Position;
				if (!PF1.get())
					PF1.reset(new
							  PathFinder(
								  pMD1,P1,P2,
								  // "Can pass?" predicate
								  not bind(&SPE::get,
										   bind(&MapData::GetEntity,
												pMD1,
												bind(&Position::row,_1),
												bind(&Position::col,_1)
											   )),
								  // cost calculation (build as few
								  // roads as possible)
								  if_then_else_return(
									  bind(&MapData::GetTerrainType,
										   pMD1,
										   bind(&Position::row,_1),
										   bind(&Position::col,_1)
										  )==MapData::Road,1,8)));
				else
					PF1->ConditionalReset(P1,P2);
				// for each tile in path
				for_each(PF1->GetPath().begin(),PF1->GetPath().end(),
						 // set terrain type to road
						 bind(&MapData::SetTerrainType,
							  pMD1,
							  bind(&Position::row,_1),
							  bind(&Position::col,_1),
							  MapData::Road
							 ));
			}
// 		void operator() (MapData * ,
// 						 const Scr::Position &,
// 						 const Scr::Position &);
	}Road;
	unsigned int RNGState = time(0);
	Fields.resize(boost::extents[h][w]);
	using Scr::Position;
	// insert up to n interesting items;
	for ( unsigned int
			  i = 0,
			  n = max<unsigned>(1,(GetWidth()*GetHeight())/200) ;
		  i!= n ;
		  ++i )
	{
		Position P1(rand_r(&RNGState)%GetHeight(),
					rand_r(&RNGState)%GetWidth());
		Position P2(GetHeight()-P1.row-1,GetWidth()-P1.col-1);
		Fields[P1.row][P1.col].SetResources('o');
		Fields[P2.row][P2.col].SetResources('o');

		Road(this,P1,P2);

		InsertEntity(P1.row,P1.col,
					 SPE(new OilWell),0);
		InsertEntity(P2.row,P2.col,
					 SPE(new OilWell),0);
	}
	Position pla1start(3,3);
	Position pla2start(GetHeight()-4,GetWidth()-4);
	Road(this,pla1start, pla2start);

	turn = 1;
	PL1=PL;
	assert(PL1.size()==2);
	PlayerList::iterator pla2 = PL1.begin();
	PlayerList::iterator pla1 = pla2++;// pla1 contains first element,
									   // while pla2 is incremented
	// Destruction of HQ is key to winning the game!
	InsertEntity(pla1start.row, pla1start.col,
				 tr1::shared_ptr<Entity>(new HQ), pla1->get());

 	InsertEntity(pla2start.row, pla2start.col,
				 tr1::shared_ptr<Entity>(new HQ), pla2->get());

 	(*pla1)->SetColor(Scr::Fg::Green);
 	(*pla2)->SetColor(Scr::Fg::Cyan);
	active=PL1.begin();
	(*active)->TurnBegin();
}

Scr::Size MapData::GetSize()
{
	return Scr::Size(GetHeight(),GetWidth());
}
//! height of map
Scr::Uint MapData::GetHeight()
{
	return Fields.shape()[0];
}
//! width of map
Scr::Uint MapData::GetWidth()
{
	return Fields.shape()[1];
}

HP MapData::Attack(int y, int x,int damage)
{
	HP result(Fields[y][x].Attack(damage));
	if (result==Destroyed)
	{
		RemoveEntity(y,x);
	}
	return  result;
}

//! range of character influence
static const int zoi_size = 5;

template <typename F>
void MapData::ForEachEntityInZoI(Scr::Position p, F F1)
{
	using namespace Scr;
	SPE pE1;
	for_each_position(
		p, GetSize(), zoi_size,
		if_then( /* explicitly refer to the operator=, as it is overloaded*/
			bind ( (SPE &(SPE::*)(const SPE &))&SPE::operator=,
				   &pE1, bind (&MapData::GetEntity,this,
							   bind(&Position::row,_1),
							   bind(&Position::col,_1))
				)	// if there is an entity on
					// specific field
			,
			if_then(
				bind (&Entity::GetMorale,
					  bind(&SPE::get,&pE1))
					// and it has Morale
				and bind (&Entity::GetAllegiance,
						  bind(&SPE::get,&pE1))
					// and it is not neutral
				,	// then
				F1//finally: execute function
				)
			)
		);
}//void MapData::ForEachEntityInZoI(Scr::Position p, F F1)

void MapData::RecalculateZoI(int y, int x, PlayerAssets * ZoI)
{
	using namespace Scr;
	typedef vector < pair < Scr::Position, PlayerAssets * > > ZoIFinder;
	typedef ZoIFinder::value_type Tag;
	ZoIFinder ZF1;
	ZF1.reserve(zoi_size*zoi_size+zoi_size*2+1);
	// FIND ALL ENTITIES INFLUENCING SPECIFIC FIELD
	Position p(y,x);

	ForEachEntityInZoI(// add coordinates and their owner to ZF1
		p, bind(&ZoIFinder::push_back,&ZF1,
				bind(constructor<Tag>(), _1,// boost functor to
											// construct object on-the-fly
					 bind(&Entity::GetAllegiance,
						  bind(&SPE::get,// raw pointer required
										 // when referencing
										 // member function
							   bind (&MapData::GetEntity,this,
									 bind(&Position::row,_1),
									 bind(&Position::col,_1)))))));

	if (ZF1.empty())// if nothing found, just SetZoi :)
		Fields[y][x].SetZoI(ZoI);
	else
	{
		// otherwise SELECT CLOSEST ONE
		ZoIFinder::iterator i =
			min_element(ZF1.begin(),ZF1.end(),// strict weak ordering
											  // of map coordinates
											  // based on distance and ZoI
						  (bind(Distance,Position(y,x),bind(&Tag::first,_1))<<1)
						 +(bind(&Tag::second,_1)==ZoI)
					<	  (bind(Distance,Position(y,x),bind(&Tag::first,_2))<<1)
						 +(bind(&Tag::second,_2)==ZoI));
		// if two units have equal distance, old owner win (+Tag::second)

		// IT'S OWNER WILL NOW INFLUENCE THIS FIELD
		Fields[y][x].SetZoI(i->second);
	}
}
//void MapData::RecalculateZoI(int y, int x, PlayerAssets * ZoI)

void MapData::SetZoI(int y, int x, PlayerAssets * ZoI)
{
	if (GetZoI(y,x) and GetZoI(y,x)!=ZoI)
		RecalculateZoI(y,x,ZoI);
	else
		Fields[y][x].SetZoI(ZoI);
}

inline void MapData:: AcquireZoI (Scr::Position p)
{
	for_each_position(p,GetSize(),zoi_size,
					  bind(&MapData::SetZoI,this,
						   bind(&Scr::Position::row,_1),
						   bind(&Scr::Position::col,_1),
						   GetEntity(p.row,p.col)->GetAllegiance()));
}

inline void MapData:: VerifyZoI (Scr::Position p)
{
	using namespace Scr;
	ForEachEntityInZoI(p,bind(&MapData::AcquireZoI,this,_1));

}
void MapData:: InsertEntity
(int y, int x, std::tr1::shared_ptr<Entity> e,PlayerAssets * a)
	throw(Entity::IncorrectPosition)
{
	using namespace Scr;
	Position p(y,x);
	e->SetMap(this);
	e->SetPosition(p);
	e->SetAllegiance(a);
	Fields[y][x].InsertEntity(e);
	if (e->GetAllegiance())
	{
		a->InsertEntity(e);
		if (e->GetMorale())
			AcquireZoI(p);
	}
}

void MapData::RemoveEntity(int y, int x)
{
	using namespace Scr;
	const SPE pE1 = GetEntity(y,x);
	Fields[y][x].RemoveEntity();
	if (PlayerAssets * pPA1 = pE1->GetAllegiance())
	{
		pPA1->RemoveEntity(pE1);
		VerifyZoI(Position(y,x));
	}
}
// file-static space (that is not avail. outside of
// this file even if declared somewhere)
namespace {
typedef vector<Scr::Position> PositionSequence;
//! Strict weak ordering of Scr::Positions by their
//! position (for unique)
struct PosComp
{
	bool operator() (const Scr::Position&a,const Scr::Position&b)
		{
			return (a.col==b.col)?(a.row<b.row):(a.col<b.col);
		}
};
//! implements sort-unique-remove idiom
//! \param PS1 Reference to ector to transform
void UniquePositionSequence ( PositionSequence & PS1 )
{
	using namespace Scr;
	sort (PS1.begin(), PS1.end(),PosComp());
	PS1.erase ( unique (PS1.begin(), PS1.end()), PS1.end());
	// if want to replace vector w/ list, PS1.sort();PS1.unique() will do the job
}
}
void MapData::MoveEntity(PathFinder::Path::iterator from,
						 PathFinder::Path::iterator to)
{
	using namespace Scr;
	const SPE pE1 = GetEntity(from->row,from->col);
	Fields[from->row][from->col].RemoveEntity();
	Fields[to->row][to->col].InsertEntity(pE1);
	if (pE1->GetMorale())
	{

	// PS1 = list of fields, that will be out of our range after
	// movement
	PositionSequence PS1;
	do
		for_each_position(*from,GetSize(),zoi_size,
						  bind(&PositionSequence::push_back,&PS1,_1));
	while (to != from++);
	UniquePositionSequence(PS1);

	// For each position in sequence verify its allegiance
  	for_each(PS1.begin(), PS1.end(),
  			 bind(&MapData::RecalculateZoI,this,bind(&Position::row,_1),
  				  bind(&Position::col,_1),pE1->GetAllegiance()));
	}
}

void MapData::SwitchTurn()
{
	if (active!=PL1.end())
	{
		(*active)->TurnEnd();
		active++;
	}
	if (active==PL1.end())
	{
		active=PL1.begin();
		turn++;
	}
	if (active!=PL1.end())
		(*active)->TurnBegin();
}

const std::string MapData::GetPlayerName() const
{
	return (*active)->GetName();
}


#include "gpuOsSenseGadget.h"
#include "cuNDArray_operators.h"
#include "cuNDArray_elemwise.h"
#include "cuNDArray_blas.h"
#include "cuNDArray_utils.h"
#include "cuNDArray_reductions.h"
#include "GadgetMRIHeaders.h"
#include "b1_map.h"
#include "GPUTimer.h"
#include "vector_td_utilities.h"
#include "hoNDArray_fileio.h"
#include "ismrmrd/xml.h"
#include <boost/thread/mutex.hpp>
#include "cuNDArray_fileio.h"

namespace Gadgetron{

#define max_number_of_gpus 10
static boost::mutex _mutex[max_number_of_gpus];

gpuOsSenseGadget::gpuOsSenseGadget()
: gpuSenseGadget(),
  is_configured_(false)
, prepared_(false)
{
}

gpuOsSenseGadget::~gpuOsSenseGadget() {}

int gpuOsSenseGadget::process_config( ACE_Message_Block* mb )
{
	gpuSenseGadget::process_config(mb);
	number_of_iterations_ = number_of_iterations.value();

	exclusive_access_ = exclusive_access.value();
	lambda_ = lambda.value();
	alpha_ = alpha.value();
	coils_per_subset_ = coils_per_subset.value();
	kappa_ = kappa.value();

	damping_ = damping.value();
	// Get the Ismrmrd header
	//
	ISMRMRD::IsmrmrdHeader h;
	ISMRMRD::deserialize(mb->rd_ptr(),h);

	if (h.encoding.size() != 1) {
		GDEBUG("This Gadget only supports one encoding space\n");
		return GADGET_FAIL;
	}

	// Get the encoding space and trajectory description
	ISMRMRD::EncodingSpace e_space = h.encoding[0].encodedSpace;
	ISMRMRD::EncodingSpace r_space = h.encoding[0].reconSpace;
	ISMRMRD::EncodingLimits e_limits = h.encoding[0].encodingLimits;

	matrix_size_seq_ = uint64d2( r_space.matrixSize.x, r_space.matrixSize.y );

	if (!is_configured_) {

		if (h.acquisitionSystemInformation) {
			channels_ = h.acquisitionSystemInformation->receiverChannels ? *h.acquisitionSystemInformation->receiverChannels : 1;
		} else {
			channels_ = 1;
		}

		// Allocate encoding operator for non-Cartesian Sense
		E_ = boost::make_shared<osSenseOperator<cuNDArray<float_complext>,2,NFFTOperator<cuNDArray,float,2>>>();
		E_->set_coils_per_subset(coils_per_subset_);

		// Setup NLCG solver
		solver_.set_encoding_operator( E_ );

		solver_.set_output_mode( (output_convergence_) ? osMOMSolver<cuNDArray<float_complext>>::OUTPUT_VERBOSE : osMOMSolver<cuNDArray<float_complext>>::OUTPUT_SILENT );
		solver_.set_max_iterations( number_of_iterations_ );
		solver_.set_kappa(kappa_);
		TV_ = boost::shared_ptr<cuTvOperator<float_complext,3> >(new cuTvOperator<float_complext,3>);
		PICS_ = boost::shared_ptr<cuTvPicsOperator<float_complext,3> >(new cuTvPicsOperator<float_complext,3>);

		// Add "TV" regularization
		//

		if( lambda_ > 0.0 ){
			TV_->set_weight((1.0-alpha_)*lambda_);
			solver_.add_nonlinear_operator(TV_);
		}

		// Add "PICCS" regularization
		//

		if( alpha_ > 0.0 && lambda_ > 0.0 ){
			PICS_->set_weight(alpha_*lambda_);
			solver_.add_nonlinear_operator(PICS_);
		}

		solver_.set_beta(damping_);

		is_configured_ = true;
	}

	GDEBUG("gpuOsSenseGadget::end of process_config\n");

	return GADGET_OK;
}

int gpuOsSenseGadget::process(GadgetContainerMessage<ISMRMRD::ImageHeader> *m1, GadgetContainerMessage<GenericReconJob> *m2)
{
	// Is this data for this gadget's set/slice?
	//
	GDEBUG("Starting gpuOsSenseGadget\n");

	if( m1->getObjectPtr()->set != set_number_ || m1->getObjectPtr()->slice != slice_number_ ) {
		// No, pass it downstream...
		return this->next()->putq(m1);
	}

	//GDEBUG("gpuOsSenseGadget::process\n");
	//GPUTimer timer("gpuOsSenseGadget::process");

	if (!is_configured_) {
		GDEBUG("\nData received before configuration complete\n");
		return GADGET_FAIL;
	}

	GenericReconJob* j = m2->getObjectPtr();

	// Let's first check that this job has the required data...
	if (!j->csm_host_.get() || !j->dat_host_.get() || !j->tra_host_.get() || !j->dcw_host_.get()) {
		GDEBUG("Received an incomplete Sense job\n");
		return GADGET_FAIL;
	}

	unsigned int samples = j->dat_host_->get_size(0);
	unsigned int channels = j->dat_host_->get_size(1);
	unsigned int rotations = samples / j->tra_host_->get_number_of_elements();
	unsigned int frames = j->tra_host_->get_size(1)*rotations;

	if( samples%j->tra_host_->get_number_of_elements() ) {
		GDEBUG("Mismatch between number of samples (%d) and number of k-space coordinates (%d).\nThe first should be a multiplum of the latter.\n",
				samples, j->tra_host_->get_number_of_elements());
		return GADGET_FAIL;
	}

	boost::shared_ptr< cuNDArray<floatd2> > traj(new cuNDArray<floatd2> (j->tra_host_.get()));
	boost::shared_ptr< cuNDArray<float> > dcw(new cuNDArray<float> (j->dcw_host_.get()));
	sqrt_inplace(dcw.get());
	boost::shared_ptr< cuNDArray<float_complext> > csm(new cuNDArray<float_complext> (j->csm_host_.get()));
	boost::shared_ptr< cuNDArray<float_complext> > device_samples(new cuNDArray<float_complext> (j->dat_host_.get()));

	// Take the reconstruction matrix size from the regulariaztion image.
	// It could be oversampled from the sequence specified size...

	matrix_size_ = uint64d2( j->reg_host_->get_size(0), j->reg_host_->get_size(1) );

	cudaDeviceProp deviceProp;
	if( cudaGetDeviceProperties( &deviceProp, device_number_ ) != cudaSuccess) {
		GDEBUG( "\nError: unable to query device properties.\n" );
		return GADGET_FAIL;
	}

	unsigned int warp_size = deviceProp.warpSize;

	matrix_size_os_ =
			uint64d2(((static_cast<unsigned int>(std::ceil(matrix_size_[0]*oversampling_factor_))+warp_size-1)/warp_size)*warp_size,
					((static_cast<unsigned int>(std::ceil(matrix_size_[1]*oversampling_factor_))+warp_size-1)/warp_size)*warp_size);

	GDEBUG("Matrix size    : [%d,%d] \n", matrix_size_[0], matrix_size_[1]);
	GDEBUG("Matrix size OS : [%d,%d] \n", matrix_size_os_[0], matrix_size_os_[1]);

	std::vector<size_t> image_dims = to_std_vector(matrix_size_);
	image_dims.push_back(frames);

	E_->set_domain_dimensions(&image_dims);
	E_->set_codomain_dimensions(device_samples->get_dimensions().get());
	E_->set_csm(csm);
	E_->setup( matrix_size_, matrix_size_os_, kernel_width_ );
	E_->preprocess(traj.get());

	{
		auto precon = boost::make_shared<cuNDArray<float_complext>>(image_dims);
		fill(precon.get(),float_complext(1.0f));
		//solver_.set_preconditioning_image(precon);
	}
	reg_image_ = boost::shared_ptr< cuNDArray<float_complext> >(new cuNDArray<float_complext>(&image_dims));

	// These operators need their domain/codomain set before being added to the solver
	//

	//E_->set_dcw(dcw);
	GDEBUG("Prepared\n");

	// Expand the average image to the number of frames
	//

	{
		cuNDArray<float_complext> tmp(*j->reg_host_);
		*reg_image_ = expand( tmp, frames );
	}
	PICS_->set_prior(reg_image_);

	// Define preconditioning weights
	//

	//Apply weights
	//*device_samples *= *dcw;

	// Invoke solver
	//

	boost::shared_ptr< cuNDArray<float_complext> > result;
	{
		GDEBUG("Running NLCG solver\n");
		GPUTimer timer("Running NLCG solver");

		// Optionally, allow exclusive (per device) access to the solver
		// This may not matter much in terms of speed, but it can in terms of memory consumption
		//

		if( exclusive_access_ )
			_mutex[device_number_].lock();

		result = solver_.solve(device_samples.get());

		if( exclusive_access_ )
			_mutex[device_number_].unlock();
	}

	// Provide some info about the scaling between the regularization and reconstruction.
	// If it is not close to one, PICCS does not work optimally...
	//

	if( alpha_ > 0.0 ){
		cuNDArray<float_complext> gpureg(j->reg_host_.get());
		boost::shared_ptr< cuNDArray<float_complext> > gpurec = sum(result.get(),2);
		*gpurec /= float(result->get_size(2));
		float scale = abs(dot(gpurec.get(), gpurec.get())/dot(gpurec.get(),&gpureg));
		GDEBUG("Scaling factor between regularization and reconstruction is %f.\n", scale);
	}

	if (!result.get()) {
		GDEBUG("\nNon-linear conjugate gradient solver failed\n");
		return GADGET_FAIL;
	}

	/*
      static int counter = 0;
      char filename[256];
      sprintf((char*)filename, "recon_sb_%d.cplx", counter);
      write_nd_array<float_complext>( sbresult->to_host().get(), filename );
      counter++; */

	// If the recon matrix size exceeds the sequence matrix size then crop
	if( matrix_size_seq_ != matrix_size_ )
		*result = crop<float_complext,2>( (matrix_size_-matrix_size_seq_)>>1, matrix_size_seq_, *result );

	// Now pass on the reconstructed images
	//
	this->put_frames_on_que(frames,rotations,j,result.get(),channels);

	frame_counter_ += frames;
	m1->release();
	return GADGET_OK;
}

GADGET_FACTORY_DECLARE(gpuOsSenseGadget)
}


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

  @copyright 2015 NumScale SAS
  @copyright 2015 J.T.Lapreste

  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_SCALAR_ACSCD_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_SCALAR_ACSCD_HPP_INCLUDED

#include <boost/simd/function/definition/acscd.hpp>
#include <boost/simd/arch/common/generic/function/acscd.hpp>

#endif


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

#define BOOST_TEST_MODULE lpc_test

#include <string>

#include <boost/test/unit_test.hpp>
#include <boost/test/data/test_case.hpp>
#include <boost/test/data/monomorphic.hpp>

#include <nil/crypto3/algebra/curves/bls12.hpp>
#include <nil/crypto3/algebra/fields/arithmetic_params/bls12.hpp>
#include <nil/crypto3/algebra/curves/params/multiexp/bls12.hpp>
#include <nil/crypto3/algebra/curves/params/wnaf/bls12.hpp>
#include <nil/crypto3/algebra/random_element.hpp>

#include <nil/crypto3/math/algorithms/unity_root.hpp>
#include <nil/crypto3/math/domains/evaluation_domain.hpp>
#include <nil/crypto3/math/algorithms/make_evaluation_domain.hpp>

#include <nil/crypto3/zk/commitments/polynomial/lpc.hpp>
#include <nil/crypto3/zk/commitments/polynomial/fri.hpp>
#include <nil/crypto3/zk/snark/systems/plonk/placeholder/params.hpp>

using namespace nil::crypto3;
using namespace nil::crypto3::zk::snark;

namespace boost {
    namespace test_tools {
        namespace tt_detail {
            template<>
            struct print_log_value<nil::crypto3::math::polynomial<
                algebra::fields::detail::element_fp<algebra::fields::params<algebra::fields::bls12_base_field<381>>>>> {
                void operator()(std::ostream &,
                                const nil::crypto3::math::polynomial<algebra::fields::detail::element_fp<
                                    algebra::fields::params<algebra::fields::bls12_base_field<381>>>> &) {
                }
            };
        }    // namespace tt_detail
    }        // namespace test_tools
}    // namespace boost

template<typename FieldType, typename NumberType>
std::vector<math::polynomial<typename FieldType::value_type>> generate(NumberType degree) {
    typedef boost::random::independent_bits_engine<boost::random::mt19937,
                                                   FieldType::modulus_bits,
                                                   typename FieldType::value_type::integral_type>
        random_polynomial_generator_type;

    std::vector<math::polynomial<typename FieldType::value_type>> res;

    boost::random::random_device rd;     // Will be used to obtain a seed for the random number engine
    boost::random::mt19937 gen(rd());    // Standard mersenne_twister_engine seeded with rd()
    boost::random::uniform_int_distribution<> distrib(std::numeric_limits<int>::min(), std::numeric_limits<int>::max());

    random_polynomial_generator_type polynomial_element_gen;
    std::size_t height = 1;
    res.reserve(height);

    for (int i = 0; i < height; i++) {
        math::polynomial<typename FieldType::value_type> poly;
        for (int j = 0; j < degree; j++) {
            poly.push_back(typename FieldType::value_type(polynomial_element_gen()));
        }
        res.push_back(poly);
    }

    return res;
}

BOOST_AUTO_TEST_SUITE(lpc_performance_test_suite)

BOOST_AUTO_TEST_CASE(lpc_performance_test) {
    typedef algebra::curves::bls12<381> curve_type;
    typedef typename curve_type::scalar_field_type FieldType;

    typedef hashes::keccak_1600<256> merkle_hash_type;
    typedef hashes::keccak_1600<256> transcript_hash_type;

    typedef typename containers::merkle_tree<merkle_hash_type, 2> merkle_tree_type;

    constexpr static const std::size_t lambda = 40;
    constexpr static const std::size_t k = 1;

    constexpr static const std::size_t d = 1 << 24;

    constexpr static const std::size_t r = boost::static_log2<(d - k)>::value;
    constexpr static const std::size_t m = 2;

    typedef zk::commitments::fri<FieldType, merkle_hash_type, transcript_hash_type, m> fri_type;
    typedef zk::commitments::list_polynomial_commitment_params<merkle_hash_type, transcript_hash_type, lambda, r, m> lpc_params_type;
    typedef zk::commitments::list_polynomial_commitment<FieldType, lpc_params_type> lpc_type;
    typedef typename lpc_type::proof_type proof_type;

    constexpr static const std::size_t d_extended = d;
    std::size_t extended_log = boost::static_log2<d_extended>::value;
    std::vector<std::shared_ptr<math::evaluation_domain<FieldType>>> D =
        zk::commitments::detail::calculate_domain_set<FieldType>(extended_log, r);

    typename fri_type::params_type fri_params;

    math::polynomial<typename FieldType::value_type> q = {0, 0, 1};
    fri_params.r = r;
    fri_params.D = D;
    fri_params.q = q;
    fri_params.max_degree = d - 1;

    typedef boost::random::independent_bits_engine<boost::random::mt19937,
                                                   FieldType::modulus_bits,
                                                   typename FieldType::value_type::integral_type>
        random_polynomial_generator_type;

    std::vector<math::polynomial<typename FieldType::value_type>> res;

    boost::random::random_device rd;     // Will be used to obtain a seed for the random number engine
    boost::random::mt19937 gen(rd());    // Standard mersenne_twister_engine seeded with rd()
    boost::random::uniform_int_distribution<> distrib(std::numeric_limits<int>::min(), std::numeric_limits<int>::max());

    random_polynomial_generator_type polynomial_element_gen;
    std::size_t height = 1;
    res.reserve(height);

    for (int i = 0; i < height; i++) {
        math::polynomial<typename FieldType::value_type> poly;
        for (int j = 0; j < fri_params.max_degree + 1; j++) {
            poly.push_back(typename FieldType::value_type(polynomial_element_gen()));
        }
        merkle_tree_type tree = lpc_type::precommit(poly, D[0]); // phase_1: Commit

        // TODO: take a point outside of the basic domain
        std::vector<typename FieldType::value_type> evaluation_points = {
            algebra::fields::arithmetic_params<FieldType>::multiplicative_generator};

        std::array<std::uint8_t, 96> x_data {};
        zk::transcript::fiat_shamir_heuristic_sequential<transcript_hash_type> transcript(x_data);

        auto proof = lpc_type::proof_eval(evaluation_points, tree, poly, fri_params, transcript); // phase_2: Prove

        // verify
        zk::transcript::fiat_shamir_heuristic_sequential<transcript_hash_type> transcript_verifier(x_data);

        BOOST_CHECK(lpc_type::verify_eval(evaluation_points, proof, fri_params, transcript_verifier)); // phase_3: Verify
    }
}

BOOST_AUTO_TEST_SUITE_END()


#include "controller.h"
#include <Eigen/Core>
#include "hw_conf.h"
#include "comm_usb.h"
#include "utils.h"
#include <math.h>
#include "chprintf.h"

#define CLAMP_ZERO(num) num > 0 ? num : 0

using namespace Eigen;

Controller::Controller() :
    m_vel_p_gains(0.05, 0.05, 0.0001),
    m_vel_i_gains(0, 0, 0),
    m_vel_d_gains(0, 0, 0),
    m_vel_ff_gains(0.05, 0.05, 0.01),
    m_pos_p_gains(0.1, 0.1, 0.01),
    m_pos_i_gains(0, 0, 0),
    m_pos_d_gains(0, 0, 0),
    m_orientation(0, 0, 0),
    m_rotation(0, 0, 0)
{
    m_throttle = 0;
    m_enabled = false;
    m_imu = new IMU();
    m_md1 = new MotorDriver(&MOTOR_DRIVER_1_PWM_DEV, MOTOR_DRIVER_1_FWD_CH, MOTOR_DRIVER_1_REV_CH);
    m_md2 = new MotorDriver(&MOTOR_DRIVER_2_PWM_DEV, MOTOR_DRIVER_2_FWD_CH, MOTOR_DRIVER_2_REV_CH);
    m_md3 = new MotorDriver(&MOTOR_DRIVER_3_PWM_DEV, MOTOR_DRIVER_3_FWD_CH, MOTOR_DRIVER_3_REV_CH);
    m_md4 = new MotorDriver(MOTOR_DRIVER_4_PWM_DEV, MOTOR_DRIVER_4_FWD_CH, MOTOR_DRIVER_4_REV_CH);
    //m_orientation(0) = Utils::atan2(-m_imu->GetVector(VECTOR_ACCELEROMETER)(1), m_imu->GetVector(VECTOR_ACCELEROMETER)(2)) * 180 / M_PI;
    //m_orientation(1) = Utils::atan2(m_imu->GetVector(VECTOR_ACCELEROMETER)(0), m_imu->GetVector(VECTOR_ACCELEROMETER)(2)) * 180 / M_PI;
}

Controller::~Controller()
{
}

void Controller::Update()
{
    //double alpha = 0.05;
    //double dt = 0.001;
    //m_orientation(0) = (1 - alpha) * (m_orientation(0) + m_imu->GetVector(VECTOR_GYROSCOPE)(0) * 180 / M_PI * dt) + alpha * Utils::atan2(-m_imu->GetVector(VECTOR_ACCELEROMETER)(1), m_imu->GetVector(VECTOR_ACCELEROMETER)(2)) * 180 / M_PI;
    //m_orientation(1) = (1 - alpha) * (m_orientation(1) + m_imu->GetVector(VECTOR_GYROSCOPE)(1) * 180 / M_PI * dt) + alpha * Utils::atan2(m_imu->GetVector(VECTOR_ACCELEROMETER)(0), m_imu->GetVector(VECTOR_ACCELEROMETER)(2)) * 180 / M_PI;
    //m_rotation = m_imu->GetVector(VECTOR_GYROSCOPE);
    m_imu->Update();
    m_orientation = m_imu->GetOrientation();
    m_rotation = m_imu->GetRotationRate();
    if (fabs(m_orientation(0)) > 50 || fabs(m_orientation(1)) > 50)
    {
        Disable();
    }
    Vector3f setpoint(0, 0, 0);
    double errX = setpoint(0) - m_orientation(0); // pitch error
    double errY = setpoint(1) - m_orientation(1); // roll error
    double errZ = setpoint(2) - m_orientation(2); // yaw error
    setpoint(0) = errX * m_pos_p_gains(0);
    setpoint(1) = errY * m_pos_p_gains(1);
    setpoint(2) = 0.0;//errZ * pos_p_gains(2);
    velocityLoopUpdate(setpoint);
}

Vector3f Controller::GetOrientation()
{
    return m_orientation;
}

Vector3f Controller::GetRotationRate()
{
    return m_rotation;
}

void Controller::SetThrottle(double throttle)
{
    m_throttle = throttle;
}

void Controller::Enable()
{
    m_enabled = true;
}

void Controller::Disable()
{
    m_enabled = false;
    m_throttle = 0;
}

void Controller::velocityLoopUpdate(Vector3f setpoint)
{
    double errX = setpoint(0) - m_rotation(0); // pitch error
    double errY = setpoint(1) - m_rotation(1); // roll error
    double errZ = setpoint(2) - m_rotation(2); // yaw error
    double pitchOut = errX * m_vel_p_gains(0) + setpoint(0) * m_vel_ff_gains(0);
    double rollOut = errY * m_vel_p_gains(1) + setpoint(1) * m_vel_ff_gains(1);
    double yawOut = errZ * m_vel_p_gains(2) + setpoint(2) * m_vel_ff_gains(2);
    if (m_enabled)
    {
        //m_md1->Set(0.5);
        //m_md2->Set(-0.5);
        //m_md3->Set(0.5);
        //m_md4->Set(-0.5);
        m_md1->Set(m_throttle - pitchOut + rollOut + yawOut);
        m_md2->Set(-(m_throttle - pitchOut - rollOut - yawOut));
        m_md3->Set(m_throttle + pitchOut - rollOut + yawOut);
        m_md4->Set(-(m_throttle + pitchOut + rollOut - yawOut));
    }
    else
    {
        m_md1->Set(0.0);
        m_md2->Set(0.0);
        m_md3->Set(0.0);
        m_md4->Set(0.0);
    }
}


///////////////////////////////////////////////////////////////////////////////
// BSD 3-Clause License
//
// Copyright (C) 2021, University of Edinburgh
// Copyright note valid unless otherwise stated in individual files.
// All rights reserved.
///////////////////////////////////////////////////////////////////////////////

#ifndef CROCODDYL_CORE_RESIDUAL_BASE_HPP_
#define CROCODDYL_CORE_RESIDUAL_BASE_HPP_

#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>

#include "crocoddyl/core/fwd.hpp"
#include "crocoddyl/core/state-base.hpp"
#include "crocoddyl/core/data-collector-base.hpp"

namespace crocoddyl {

/**
 * @brief Abstract class for residual models
 *
 * In Crocoddyl, a residual model defines a vector function \f$\mathbf{r}(\mathbf{x}, \mathbf{u})\mathbb{R}^{nr}\f$
 * where `nr` describes its dimension in the Euclidean space. This function depends on the state point
 * \f$\mathbf{x}\in\mathcal{X}\f$, which lies in the state manifold described with a `nq`-tuple, its velocity
 * \f$\dot{\mathbf{x}}\in T_{\mathbf{x}}\mathcal{X}\f$ that belongs to the tangent space with `nv` dimension, and the
 * control input \f$\mathbf{u}\in\mathbb{R}^{nu}\f$. The residual function can used across cost and constraint models.
 *
 * The main computations are carring out in `calc` and `calcDiff` routines. `calc` computes the residual vector
 * and `calcDiff` computes the Jacobians of the residual function.
 * Additionally, it is important remark that `calcDiff()` computes the Jacobians using the latest stored values by
 * `calc()`. Thus, we need to run first `calc()`.
 *
 * \sa `StateAbstractTpl`, `calc()`, `calcDiff()`, `createData()`
 */
template <typename _Scalar>
class ResidualModelAbstractTpl {
 public:
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW

  typedef _Scalar Scalar;
  typedef MathBaseTpl<Scalar> MathBase;
  typedef ResidualDataAbstractTpl<Scalar> ResidualDataAbstract;
  typedef StateAbstractTpl<Scalar> StateAbstract;
  typedef DataCollectorAbstractTpl<Scalar> DataCollectorAbstract;
  typedef typename MathBase::VectorXs VectorXs;
  typedef typename MathBase::MatrixXs MatrixXs;

  /**
   * @brief Initialize the residual model
   *
   * @param[in] state        State of the system
   * @param[in] nr           Dimension of residual vector
   * @param[in] nu           Dimension of control vector
   * @param[in] q_dependent  Define if the residual function depends on q (default true)
   * @param[in] v_dependent  Define if the residual function depends on v (default true)
   * @param[in] u_dependent  Define if the residual function depends on u (default true)
   */
  ResidualModelAbstractTpl(boost::shared_ptr<StateAbstract> state, const std::size_t nr, const std::size_t nu,
                           const bool q_dependent = true, const bool v_dependent = true,
                           const bool u_dependent = true);

  /**
   * @copybrief ResidualModelAbstractTpl()
   *
   * The default `nu` value is obtained from `StateAbstractTpl::get_nv()`.
   *
   * @param[in] state        State of the system
   * @param[in] nr           Dimension of residual vector
   * @param[in] q_dependent  Define if the residual function depends on q (default true)
   * @param[in] v_dependent  Define if the residual function depends on v (default true)
   * @param[in] u_dependent  Define if the residual function depends on u (default true)
   */
  ResidualModelAbstractTpl(boost::shared_ptr<StateAbstract> state, const std::size_t nr, const bool q_dependent = true,
                           const bool v_dependent = true, const bool u_dependent = true);
  virtual ~ResidualModelAbstractTpl();

  /**
   * @brief Compute the residual vector
   *
   * @param[in] data  Residual data
   * @param[in] x     State point \f$\mathbf{x}\in\mathbb{R}^{ndx}\f$
   * @param[in] u     Control input \f$\mathbf{u}\in\mathbb{R}^{nu}\f$
   */
  virtual void calc(const boost::shared_ptr<ResidualDataAbstract>& data, const Eigen::Ref<const VectorXs>& x,
                    const Eigen::Ref<const VectorXs>& u);

  /**
   * @brief Compute the Jacobian of the residual vector
   *
   * It computes the Jacobian the residual function. It assumes that `calc()` has been run first.
   *
   * @param[in] data  Residual data
   * @param[in] x     State point \f$\mathbf{x}\in\mathbb{R}^{ndx}\f$
   * @param[in] u     Control input \f$\mathbf{u}\in\mathbb{R}^{nu}\f$
   */
  virtual void calcDiff(const boost::shared_ptr<ResidualDataAbstract>& data, const Eigen::Ref<const VectorXs>& x,
                        const Eigen::Ref<const VectorXs>& u);

  /**
   * @brief Create the residual data
   *
   * The default data contains objects to store the values of the residual vector and their Jacobians.
   * However, it is possible to specialized this function if we need to create additional data, for instance, to avoid
   * dynamic memory allocation.
   *
   * @param data  Data collector
   * @return the residual data
   */
  virtual boost::shared_ptr<ResidualDataAbstract> createData(DataCollectorAbstract* const data);

  /**
   * @copybrief calc()
   *
   * @param[in] data  Residual data
   * @param[in] x     State point
   */
  void calc(const boost::shared_ptr<ResidualDataAbstract>& data, const Eigen::Ref<const VectorXs>& x);

  /**
   * @copybrief calcDiff()
   *
   * @param[in] data  Residual data
   * @param[in] x     State point
   */
  void calcDiff(const boost::shared_ptr<ResidualDataAbstract>& data, const Eigen::Ref<const VectorXs>& x);

  /**
   * @brief Return the state
   */
  const boost::shared_ptr<StateAbstract>& get_state() const;

  /**
   * @brief Return the dimension of the residual vector
   */
  std::size_t get_nr() const;

  /**
   * @brief Return the dimension of the control input
   */
  std::size_t get_nu() const;

  /**
   * @brief Return true if the residual function depends on q
   */
  bool get_q_dependent() const;

  /**
   * @brief Return true if the residual function depends on v
   */
  bool get_v_dependent() const;

  /**
   * @brief Return true if the residual function depends on u
   */
  bool get_u_dependent() const;

  /**
   * @brief Print information on the residual model
   */
  template <class Scalar>
  friend std::ostream& operator<<(std::ostream& os, const ResidualModelAbstractTpl<Scalar>& model);

  /**
   * @brief Print relevant information of the residual model
   *
   * @param[out] os  Output stream object
   */
  virtual void print(std::ostream& os) const;

 protected:
  boost::shared_ptr<StateAbstract> state_;  //!< State description
  std::size_t nr_;                          //!< Residual vector dimension
  std::size_t nu_;                          //!< Control dimension
  VectorXs unone_;                          //!< No control vector
  bool q_dependent_;                        //!< Label that indicates if the residual function depends on q
  bool v_dependent_;                        //!< Label that indicates if the residual function depends on v
  bool u_dependent_;                        //!< Label that indicates if the residual function depends on u
};

template <typename _Scalar>
struct ResidualDataAbstractTpl {
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW

  typedef _Scalar Scalar;
  typedef MathBaseTpl<Scalar> MathBase;
  typedef DataCollectorAbstractTpl<Scalar> DataCollectorAbstract;
  typedef typename MathBase::VectorXs VectorXs;
  typedef typename MathBase::MatrixXs MatrixXs;

  template <template <typename Scalar> class Model>
  ResidualDataAbstractTpl(Model<Scalar>* const model, DataCollectorAbstract* const data)
      : shared(data),
        r(model->get_nr()),
        Rx(model->get_nr(), model->get_state()->get_ndx()),
        Ru(model->get_nr(), model->get_nu()) {
    r.setZero();
    Rx.setZero();
    Ru.setZero();
  }
  virtual ~ResidualDataAbstractTpl() {}

  DataCollectorAbstract* shared;  //!< Shared data allocated by the action model
  VectorXs r;                     //!< Residual vector
  MatrixXs Rx;                    //!< Jacobian of the residual vector with respect the state
  MatrixXs Ru;                    //!< Jacobian of the residual vector with respect the control
};

}  // namespace crocoddyl

/* --- Details -------------------------------------------------------------- */
/* --- Details -------------------------------------------------------------- */
/* --- Details -------------------------------------------------------------- */
#include "crocoddyl/core/residual-base.hxx"

#endif  // CROCODDYL_CORE_RESIDUAL_BASE_HPP_


#include <ros/ros.h>
#include <boost/assign.hpp>
#include <sensor_msgs/NavSatFix.h>
#include "ros/time.h"
sensor_msgs::NavSatFix msg;
sensor_msgs::NavSatFix origin,temp;
ros::Publisher gps_pub;
ros::Publisher origin_pub;
volatile int i=0;
  //ros::Time current_time;
  //ros::Time last_time;

void odomCallback(sensor_msgs::NavSatFix msg)
{
	if (ros::ok())
{
    if(i<20)
    {
      i++;
      temp.latitude+=msg.latitude;
      temp.longitude+=msg.longitude;
    }
    if(i==20)
    {
      i++;
      origin.latitude=temp.latitude/20;
      origin.longitude=temp.longitude/20;
     }
    //add covariance
    msg.position_covariance_type=2;
    msg.position_covariance=boost::assign::list_of(1.0)(0.0)(0.0)
                                                  (0.0)(1.0)(0.0)
                                                  (0.0)(0.0)(1.0);

        //publish the message
    gps_pub.publish(msg);
    origin_pub.publish(origin);
    //last_time = current_time;

  }
}

int main(int argc, char** argv){
  ros::init(argc, argv, "gps");

  ros::NodeHandle n;
  //gps covariance
  ros::Subscriber gps_sub=n.subscribe<sensor_msgs::NavSatFix>("vn_ins/fix",50,odomCallback);
  gps_pub = n.advertise<sensor_msgs::NavSatFix>("fix_c", 50);
  origin_pub=n.advertise<sensor_msgs::NavSatFix>("gps/origin",50);
  ros::spin();
  return 0;
}


#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>

#include "matutils.hpp"
#include "readreftable.hpp"

// #include <numeric>
// #include <boost/accumulators/accumulators.hpp>
// #include <boost/accumulators/statistics/stats.hpp>
// #include <boost/accumulators/statistics/mean.hpp>
// #include <boost/accumulators/statistics/variance.hpp>
// using namespace boost::accumulators;

// BOOST_AUTO_TEST_CASE(UnifDistrib, * boost::unit_test::tolerance(0.01)) {
//     accumulator_set<double,stats<tag::mean, tag::variance>> acc;
//     for(auto i = 0; i < 10000; i++) acc(dis(rng));
//     BOOST_TEST( mean(acc) == 0.5);
//     BOOST_TEST( variance(acc) == 1.0/12.0);
// }

TEST_CASE("Matutils : add linear Combination") {
    MatrixXi m1(2,3);
    MatrixXi m2(3,2);
    MatrixXi m3(2,5);

    m1 << 1, 2, 3,
          4, 5, 6;

    m2 << 1, 2,
          3, 4,
          5, 6;

    m3 << 1, 2, 3, 22, 28,
          4, 5, 6, 49, 64;
    addLinearComb(m1,m1,m2);
    CHECK( (m3 - m1).lpNorm<Infinity>() == 0 );
}

// TEST_CASE(NoiseNames) {
//     auto myread = readreftable("headerRF.txt","reftableRF.bin");
//     size_t noisecols = 5;
//     size_t nrow = myread.nrec;
//     // size_t oldsize = myread.stats.size();
//     auto stats_names(myread.stats_names);
//     // for(auto i = 0; i < noisecols; i++) {
//     //     stats_names.push_back("NOISE" + std::to_string(i));
//     // }
//     // addNoiseCols(myread,noisecols);
//     BOOST_TEST ( 1 == 1 );
//     // BOOST_TEST( myread.stats.size() == (oldsize + noisecols * nrow) );
//     // BOOST_TEST( myread.stats_names == stats_names );
// }


#include <boost/serialization/export.hpp>
#include "FilterLayer.hpp"
#include "LayerModel.hpp"

using namespace std;
using namespace snn;
using namespace internal;

BOOST_CLASS_EXPORT(FilterLayer)

FilterLayer::FilterLayer(LayerModel& model, shared_ptr<NeuralNetworkOptimizer> optimizer)
    : Layer(model, optimizer)
{
    this->numberOfFilters = model.numberOfFilters;
    this->sizeOfFilterMatrix = model.sizeOfFilerMatrix;
    this->shapeOfInput = model.shapeOfInput;
}

inline
vector<float> FilterLayer::computeOutput(const vector<float>& inputs, [[maybe_unused]] bool temporalReset)
{
    vector<float> outputs(this->neurons.size());
    for (int i = 0, n = 0; n < (int)this->neurons.size(); ++i)
    {
        auto neuronInputs = this->createInputsForNeuron(i, inputs);
        for (int f = 0; f < this->numberOfFilters; ++f, ++n)
        {
            outputs[n] = this->neurons[n].output(neuronInputs);
        }
    }
    return outputs;
}

inline
vector<float> FilterLayer::computeBackOutput(vector<float>& inputErrors)
{
    vector<float> errors(this->numberOfInputs, 0);
    for (int n = 0; n < (int)this->neurons.size(); ++n)
    {
        auto& error = this->neurons[n].backOutput(inputErrors[n]);
        const int neuronIndex = n / this->numberOfFilters;
        this->insertBackOutputForNeuron(neuronIndex, error, errors);
    }
    return errors;
}

std::vector<int> FilterLayer::getShapeOfInput() const
{
    return this->shapeOfInput;
}

std::vector<int> FilterLayer::getShapeOfOutput() const
{
    return this->shapeOfOutput;
}

int FilterLayer::getSizeOfFilterMatrix() const
{
    return this->sizeOfFilterMatrix;
}

int FilterLayer::isValid() const
{
    return this->Layer::isValid();
}

bool FilterLayer::operator==(const BaseLayer& layer) const
{
    try
    {
        const auto& f = dynamic_cast<const FilterLayer&>(layer);
        return this->Layer::operator==(layer)
            && this->numberOfInputs == f.numberOfInputs
            && this->sizeOfFilterMatrix == f.sizeOfFilterMatrix
            && this->shapeOfInput == f.shapeOfInput
            && this->shapeOfOutput == f.shapeOfOutput;
    }
    catch (bad_cast&)
    {
        return false;
    }
}

bool FilterLayer::operator!=(const BaseLayer& layer) const
{
    return !(*this == layer);
}


// Author(s): Jeroen van der Wulp
// Copyright: see the accompanying file COPYING or copy at
// https://github.com/mCRL2org/mCRL2/blob/master/COPYING
//
// 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)
//
/// \file symbolic_reachability_test.cpp
/// \brief Add your file description here.

#include <boost/test/minimal.hpp>

#include "mcrl2/lps/find.h"
#include "mcrl2/lps/io.h"
#include "mcrl2/lps/linearise.h"
#include "mcrl2/lps/next_state_generator.h"

using namespace mcrl2;

const std::string case_no_influenced_parameters(
  "act a;\n\n"
  "proc X(i: Nat) = a.X(i);\n\n"
  "init X(0);\n");

const std::string case_influenced_condition(
  "act a;\n\n"
  "proc X(i: Nat) = (i < 4) -> a.X(i) <> delta;\n\n"
  "init X(0);\n");

const std::string case_influenced_next(
  "act a;\n\n"
  "proc X(i: Nat, j : Nat) = a.X(j, i);\n\n"
  "init X(0, 0);\n");

const std::string case_influenced_action(
  "act a : Nat;\n\n"
  "proc X(i: Nat) = a(i + 1).X(i);\n\n"
  "init X(0);\n");

const std::string case_influenced_time(
  "act a;\n\n"
  "proc X(i: Nat) = a@(i + 1).X(i);\n\n"
  "init X(0);\n");

const std::string case_two_parameters(
  "act a : Nat;\n\n"
  "proc X(i: Nat, j : Nat) = (j < 4) -> a(i).X(i, j + 1) <> delta;\n\n"
  "init X(0, 1);\n");

const std::string case_summands(
  "act a: Nat;\n\n"
  "proc X(i: Nat, j : Nat) = a(j + 1).X(i, j) + a(i + 1).X(i, j);\n\n"
  "init X(0, 1);\n");

const std::string case_last(
  "act a: Nat;\n\n"
  "sort D = struct d1 | d2;\n"
  "proc X(id : Nat, n : Nat, dd:D) = sum d:D. a(id).X(id,(n + 1) mod 10, d);\n\n"
  "init allow({a},X(1,0,d1) || X(2,0,d1));\n");

class group_information
{

  private:
    mcrl2::lps::stochastic_specification const& m_model;
    std::vector< std::vector< std::size_t > > m_group_indices;

  private:

    template <typename SummandVector>
    void gather_summands(const SummandVector& summands, const data::variable_list& parameter_list, const std::set<data::variable>& parameter_set, std::size_t& summand_index)
    {
      for (typename SummandVector::const_iterator i = summands.begin(); i != summands.end(); ++i)
      {
        std::set<data::variable> used_variables = lps::find_free_variables(*i);
        std::set<data::variable> used_parameters;
        std::set_intersection(used_variables.begin(), used_variables.end(), parameter_set.begin(), parameter_set.end(), std::inserter(used_parameters, used_parameters.begin()));

        std::size_t j_index = 0;
        for (const auto & j : parameter_list)
        {
          if (used_parameters.find(j) != used_parameters.end())
          {
            m_group_indices[summand_index].push_back(j_index);
          }
          j_index++;
        }
        summand_index++;
      }
    }

    void gather(lps::stochastic_specification const& l)
    {
      lps::stochastic_linear_process specification(l.process());

      std::size_t size = specification.action_summands().size() + specification.deadlock_summands().size();
      m_group_indices.resize(size);

      std::set<data::variable> parameter_set = data::find_all_variables(specification.process_parameters());
      std::size_t summand_index = 0;
      gather_summands(specification.action_summands(), specification.process_parameters(), parameter_set, summand_index);
      gather_summands(specification.deadlock_summands(), specification.process_parameters(), parameter_set, summand_index);
    }

  public:
    /**
     * \brief constructor from an mCRL2 lps
     **/
    group_information(mcrl2::lps::stochastic_specification const& l) : m_model(l)
    {
      gather(l);
    }

    /**
     * \brief The number of groups (summands in the LPS)
     * \return lps::specification(l).summands().size()
     **/
    inline std::size_t number_of_groups() const
    {
      return m_group_indices.size();
    }

    inline std::size_t number_of_parameters() const
    {
      return m_model.process().process_parameters().size();
    }

    /**
     * \brief Indices of process parameters that influence event or next state of a summand
     * \param[in] index the selected summand
     * \returns reference to a vector of indices of parameters
     **/
    inline std::vector< std::size_t > const& operator[](std::size_t index) const
    {
      return m_group_indices[index];
    }
};

void check_info(mcrl2::lps::stochastic_specification const& model)
{
  group_information info(model);

  BOOST_CHECK(info.number_of_groups() == model.process().summand_count());

#ifdef SHOW_INFO
  for (std::size_t i = 0; i < info.number_of_groups(); ++i)
  {
    std::vector< std::size_t > const& group_info(info[i]);

    std::cerr << "group " << i << " : {";

    if (!group_info.empty())
    {
      std::cerr << group_info[0];
    }

    for (std::vector< std::size_t >::const_iterator j = ++group_info.begin(); j < group_info.end(); ++j)
    {

      std::cerr << "," << *j;
    }

    std::cerr << "}" << std::endl;
  }
#endif
}

int test_main(int argc, char** argv)
{
  using namespace mcrl2;
  using namespace mcrl2::lps;

  check_info(linearise(case_no_influenced_parameters));
  check_info(linearise(case_influenced_condition));
  check_info(linearise(case_influenced_action));
  check_info(linearise(case_influenced_time));
  check_info(linearise(case_influenced_next));
  check_info(linearise(case_two_parameters));
  check_info(linearise(case_summands));
  check_info(linearise(case_last));

  lps::stochastic_specification model=linearise(case_no_influenced_parameters);

  if (1 < argc)
  {
    load_lps(model, argv[1]);

    model.process().deadlock_summands().clear();

    data::rewriter        rewriter(model.data());

    next_state_generator explorer(model, rewriter);

    std::stack< state >     stack;
    std::set< state >   known;

    stack.push(explorer.initial_states().front().state());
    known.insert(stack.top());

    while (!stack.empty())
    {
      state current(stack.top());
      stack.pop();

      for (std::size_t i = 0; i < model.process().summand_count(); ++i)
      {
        next_state_generator::enumerator_queue_t enumeration_queue;
        for(next_state_generator::iterator j = explorer.begin(current, i, &enumeration_queue); j != explorer.end(); ++j)
        {
          if (known.find(j->target_state()) == known.end())
          {
            std::cerr << atermpp::pp(j->target_state()) << std::endl;
            known.insert(j->target_state());
            stack.push(j->target_state());
          }
        }
      }
    }
  }

  return 0;
}


// ==========================================================================
// Paradice Dice Roll Parser
//
// Copyright (C) 2009 Matthew Chaplain, All Rights Reserved.
// This file is covered by the MIT Licence:
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
// ==========================================================================
#include "paradice/dice_roll_parser.hpp"
#include <boost/optional.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/home/support/char_encoding/ascii.hpp>
#include <numeric>
#include <vector>

BOOST_FUSION_ADAPT_STRUCT(
    paradice::dice_roll,
    (odin::u32, repetitions_)
    (odin::u32, amount_)
    (odin::u32, sides_)
    (odin::s32, bonus_)
)

namespace paradice {

//* =========================================================================
/// \brief A grammar that matches dice-rolling expressions.  For example,
/// 2d6+12.
//* =========================================================================
template <class Iterator>
struct dice_roll_grammar
    : boost::spirit::qi::grammar
      <
          Iterator
        , dice_roll()
      >
{
    dice_roll_grammar()
        : dice_roll_grammar::base_type(dice_roll_)
    {
        using namespace boost::spirit;
        using namespace boost::spirit::qi;

        repetitions_
            = (uint_[_val = _1] >> lit("*"))
            | eps[_val = 1]
            ;

        bonuses_
            = eps[_val = 0]
           >> *( ( lit("+") >> int_[_val += _1] )
               | ( lit("-") >> int_[_val -= _1] )
               )
            ;

        dice_roll_
            = repetitions_
           >> uint_
           >> boost::spirit::ascii::no_case['d']
           >> uint_
           >> bonuses_
            ;
    }

    boost::spirit::qi::rule<Iterator, odin::u32()> repetitions_;
    boost::spirit::qi::rule<Iterator, odin::s32()> bonuses_;
    boost::spirit::qi::rule<Iterator, dice_roll()> dice_roll_;
};

//* =========================================================================
/// \brief Parses a string into a dice_roll.
///
/// Takes a string and, if it conforms to the format
/// "[<repetitions>*]<amount>d<sides>[<bonuses...>]", and converts it to a
/// dice_roll structure.
/// For example, "2d6+3-4" will convert to a dice_roll of { 1, 2, 6, -1 };
/// "3*2d20" will convert to a dice_roll of { 3, 2, 20, 0 };
//* =========================================================================
boost::optional<dice_roll> parse_dice_roll(
    std::string::const_iterator &begin
  , std::string::const_iterator  end)
{
    dice_roll_grammar<std::string::const_iterator>  roll_grammar;
    dice_roll                                       result;
    dice_roll                                      &ref_result = result;

    if (phrase_parse(
        begin
      , end
      , roll_grammar
      , boost::spirit::ascii::space
      , ref_result))
    {
        return boost::optional<dice_roll>(result);
    }
    else
    {
        return boost::optional<dice_roll>();
    }
}

}