//
//  bpp.hpp
//  PhyloAcc
//
//  Created by hzr on 3/8/16.
//  Copyright © 2016 hzr. All rights reserved.
//

#ifndef bpp_hpp
#define bpp_hpp

#include <stdio.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <string>
#include <armadillo>
#include <queue>

#include <cmath>
#include <sys/time.h>

#include <map>
#include <fstream>
#include <vector>
#include <set>
#include <sstream>
#include <algorithm>

#include "newick.h"
#include "profile.h"
#include "utils.h"

using namespace std;
using namespace arma;

//const double T1 = 0.5;

const double LOG_ZERO = -INFINITY;

class BPP
{
    friend class BPP_C;

private:

    int G;  //total base pairs
    int S;
    vector <unsigned int> element_size;
    vector <unsigned int> element_start;

    double ratio0;
    double ratio1;
    double cub;
    double nlb;
    int ropt;

    // names of species
    vector<int> target_species;
    vector<int> conservedgroup;
    //int refspecies;

    double conserve_prop;

    vector<int> outgroup;
    set<int> upper;
    set<int> upper_conserve;

    vector<int> subtree;
    vector<int> tosample;

    // MCMC running parameters
    int num_burn;   // num of burn-in updates
    int num_mcmc;   // num of MCMC updates
    int num_thin;   // num of updates between two samples

    vector < vector< string > > X;  //element * current profile

    // phylogenetic tree (using array to accelerate)
    int    (*children)[2];
    int     *parent;
    double  *distances;
    vec pi;
    vec log_pi;
    mat eigenvec; vec eigenval;
    mat eigeninv;
    mat submat;
    double indel;
    double indel2;

    int num_base;

    mat eigenvecprop; vec eigenvalprop;
    mat eigeninvprop; vec piprop;

    int moveroot = -10; // move root to one of its children

    vector<vector < vector <int > >> Max_Z;
    vector<vector < vector <int > >> cur_Z;

    double ind_lrate, ind_lrate2;  //for Z
    double ind_grate;
    double vlr = 100; // proposal variance of loss rate
    double vgr = 100; // proposal variance of gain rate
    //double prior_glr[3];
    double prior_l_a, prior_l_b;
    double prior_l2_a, prior_l2_b;
    double prior_g_a, prior_g_b;

    vector<mat >  TM_Int;
    //vector<mat>  TM_null;
    vector<mat> log_TM_Int;

    //vector<mat > log_cache_TM_neut;
    //vector<mat > log_cache_TM_cons;
    vector<mat > log_cache_TM_null;

    vector< double >  log_liks_sgl;
    vector<vector< double >>   log_liks_Z; //Store max logliklihood
    vector< double > log_liks_curZ; //Store current loglik
    vector< double > log_liks_propZ; //Store loglik with prop indel parameter

    vector< double > MH_ratio_gain; //
    vector< double > MH_ratio_loss; //

    vector< double > log_liks_null;
    vector< double > log_liks_resZ;

    vector< double > cur_nrate;
    vector< double > cur_crate;

    vector< double > cur_lrate;
    vector< double > cur_grate;
    vector< double > cur_lrate2;

    vector < vector <mat>> log_cache_TM_neut_ensemble;
    vector < vector <mat>> log_cache_TM_cons_ensemble;
    vector<double> neut_prior_density;
    vector<double> cons_prior_density;

    //hyperparameter of prior_c, prior_n;
    double nprior_a, nprior_b;  //around 1
    double cprior_a, cprior_b;  //around ratio

    time_t last_time;
    unsigned long int seed;

public:
    int N;
    int C;  //total number of elements
    vector< string > species_names; // size S
    vector<string> nodes_names;  //size N
    // GSL random number generator
    gsl_rng * RNG;

    BPP(int pC, PhyloProf & _prof, PhyloTree & _tree, string output_path, string _target, string _outgroup, double _conserve_prop, string _conservegroup, double _ratio0, double _ratio1, int _ropt, double _cub, double _nlb, double _npriora, double _npriorb, double _cpriora, double _cpriorb, int _seed, double _prep_grate, double _prep_lrate, double _prep_lrate2, double _prior_g_a, double _prior_g_b, double _prior_l_a, double _prior_l_b,double _prior_l2_a, double _prior_l2_b, double _indel, double _indel2, double missing_thres, bool _sample_indel)
{

    seed = _seed;
    RNG = gsl_rng_alloc(gsl_rng_default);
    gsl_rng_set(RNG, seed);

    C = _prof.element_names.size();
    G = _prof.G;
    S = _tree.S;
    N = S*2 - 1;
    pi = _tree.pi;

    ratio0 = _ratio0;
    ratio1 = _ratio1;
    cub = _cub;  // upper bound for conserved rate
    nlb = _nlb;  // lower bound for neutral rate
    ropt = _ropt; // restrict rate option: 0,1,2

    indel = _indel; //0.025; 0.25 for mammal
    indel2 = _indel2; //0.013 for ratite; 0.08 for mammal

    ind_lrate = _prep_lrate;
    ind_lrate2 = _prep_lrate2;
    ind_grate = _prep_grate;
//    prior_glr[0] = _prior_l * _prior_gla;
//    prior_glr[1] = _prior_g * _prior_gla;
//    prior_glr[2] = (1 - _prior_l -_prior_g)  * _prior_gla;

    prior_l_a = _prior_l_a, prior_l_b = _prior_l_b;
    prior_l2_a = _prior_l2_a, prior_l2_b = _prior_l2_b;
    prior_g_a = _prior_g_a, prior_g_b = _prior_g_b;

    nprior_a = _npriora, nprior_b = _npriorb;  //around 1
    cprior_a = _cpriora, cprior_b = _cpriorb;  //around ratio

    //nprior_a = 15, nprior_b = 0.1;  //around 1
    //cprior_a = 5, cprior_b = 0.02;  //around ratio

    // matching the species in profile and tree data
    MatchProfAndTree(_prof, _tree);

    element_size = vector<unsigned int>(C);
    element_start = vector<unsigned int>(C);

    // initialization of profile and tree
    species_names = _prof.species_names;
    nodes_names = _tree.nodes_names;

    //_refspecies = strutils::trim(_refspecies);
    //refspecies = find(species_names.begin(), species_names.end(),  _refspecies) - species_names.begin();

    vector<string> tmp = strutils::split(strutils::trim(_target),';');
    for(vector<string>::iterator it = tmp.begin(); it<tmp.end();it++)
    {
        ptrdiff_t pos = find(species_names.begin(), species_names.end(), *it) - species_names.begin();
        target_species.push_back(pos);
    }

    tmp = strutils::split(strutils::trim(_conservegroup),';');
    for(vector<string>::iterator it = tmp.begin(); it<tmp.end();it++)
    {
        ptrdiff_t pos = find(species_names.begin(), species_names.end(), *it) - species_names.begin();
        conservedgroup.push_back(pos);
    }
    conserve_prop = _conserve_prop;

    if(indel<1e-10)
    {
        num_base = 4;
    }else{
        num_base = 5;

    }

    //X = vector < vector <string> >(C);

    int start = 0;

    for(int c =0; c<C;c++)  // in mammal bed, the element pos is not continous
    {
        //int start = _prof.element_pos[c][0];
        //int end = _prof.element_pos[c][1];
        int end = start + _prof.element_pos[c][1] - _prof.element_pos[c][0];
        int len = end -start;
        element_size[c] = len;
        element_start[c] = start;
//        for(int s=0; s<S;s++){
//            //string y =strutils::ToLowerCase(_prof.X[s].substr(start,end));  // already to lower in reading alignment
//            X[c].push_back(_prof.X[s].substr(start,end));
//        }

        start = end;

    }

    InitPhyloTree(_tree);  //, _indel here read in Q, depends on _indel!

    //cout <<"eigenval: " << eigenval;
    //cout <<"eigenvec: " << eigenvec;

    cout << "InitPhyloTree finished" <<endl;

    // transition probability of hidden state Z
    TM_Int = vector<mat >(N, zeros<mat>(3,2));
    log_TM_Int = vector<mat >(N, zeros<mat>(3,2));

//    for(int s=0; s<N; s++)
//    {
//
//        //double x = exp(-ind_grate *distances[s]);
//        TM_Int[s](0,0) = 1 - ind_grate;
//        TM_Int[s](1,0) = ind_grate * (1 - ind_lrate2);
//        TM_Int[s](2,0) = ind_grate * ind_lrate2;
//        //double y = exp(-ind_lrate *distances[s]);
//        TM_Int[s](1,1) = 1 - ind_lrate;
//        TM_Int[s](2,1) = ind_lrate;
//
//    }

    tmp = strutils::split(strutils::trim(_outgroup),';');
    for(vector<string>::iterator it = tmp.begin(); it<tmp.end();it++)
    {
        ptrdiff_t pos = find(species_names.begin(), species_names.end(), *it) - species_names.begin();
        outgroup.push_back(pos);
    }
    getUppertree(N-1, outgroup, upper);
    getUppertree(N-1, conservedgroup, upper_conserve);

    for(int s=0; s<N-1; s++) // nodes: from bottom to top, doesn't include root
    {
        if(upper.find(s)==upper.end())
        {
            subtree.push_back(s);
        }
    }

   // ctnutils::DispVector(upper);
   // cout << endl;

//    for(set<int>:: iterator it = upper.begin(); it!=upper.end();it++)
//    {
//        TM_Int[*it](1,1) = 1;
//        TM_Int[*it](2,1) = 0;
//
//        TM_Int[*it](0,0) = 1 - ind_grate;
//        TM_Int[*it](1,0) = ind_grate;
//        TM_Int[*it](2,0) = 0;
//    }
//
//
//    for(int s=0; s<N; s++){
//        log_TM_Int[s] = log(TM_Int[s]);
//    }

    //if(_sample_indel) sample_indel(_prof.X, missing_thres, output_path, 200, 25); //_prof.X

    if(num_base > 4)  // no indel
    {
        submat *= (1-indel);
        mat B = ones<mat>(4,1) * indel;
        mat C = ones<mat>(1,5) * indel2;

        submat.insert_cols(4, B);
        submat.insert_rows(4, C);

        colvec c = sum(submat,1);
        submat.diag() -=c;
        mat a = null(submat.t());
        pi = a/accu(a);
    }

    cx_mat bvec;
    cx_vec aval;
    eig_gen(aval, bvec, submat);
    eigenval = conv_to<mat>::from(aval);
    eigenvec = conv_to<mat>::from(bvec).t();
    eigeninv = inv(eigenvec);

    log_pi = log(pi);

    log_cache_TM_null = vector<mat >(N, zeros<mat> (num_base,num_base));

    for(int i=0;i<N-1;i++)
    {

        if(distances[i]> 1e-10 )
        {
            mat tmp_diag  = exp(eigenval*distances[i]);
            mat x = eigenvec;
            x.each_col()%=tmp_diag;

            //log_cache_TM_neut[i] = log(eigeninv * x); //transpose Q
            log_cache_TM_null[i] = log(eigeninv * x); //log_cache_TM_neut[i] ;

//            tmp_diag  = exp(eigenval*distances[i]*ratio); //*scale_cons[c]);
//            x = eigenvec;
//            x.each_col()%=tmp_diag;
//
//            log_cache_TM_cons[i] = log(eigeninv * x);

        }else{

            log_cache_TM_null[i].fill(-INFINITY); //83, root
            log_cache_TM_null[i].diag().fill(0);
            //log_cache_TM_cons[i] = log_cache_TM_neut[i] ;
            //log_cache_TM_null[i] = log_cache_TM_null[i] ;
            //log_cache_TM_neut[i] = log_cache_TM_neut[i] ;
        }

    }

    //cout << "cache finished" <<endl;

}

~BPP()
{

    delete [] children;
    delete [] parent;
    delete [] distances;

    gsl_rng_free(RNG);
}

static vec log_exp_multi(mat x, vec log_y)
{
    double log_ymax = arma::max(log_y);
    log_y = log_y - log_ymax;
        vec result = x*exp(log_y);

    return(log(result) + log_ymax);
}

static vec log_multi(mat log_x, vec log_y)  //X^T * y
{
    log_x.each_col()+=log_y;

    double log_xmax = log_x.max();
    if(log_xmax == -INFINITY)
    {
        vec xx(log_x.n_cols);
        xx.fill(-INFINITY);
        return(xx);
    }

    log_x -= log_xmax;

    rowvec result = log(sum(exp(log_x))) + log_xmax;

    return(result.t());
}

static vec log_multi2(mat log_x, vec log_y)
{
    log_x.each_col()+=log_y;

   for(size_t i =0; i < log_x.n_cols; i++){
        double log_xmax = log_x.col(i).max();

        log_x.col(i) -= log_xmax;

        log_y.row(i) = log(sum(exp(log_x.col(i)))) + log_xmax;

    }
    return(log_y);
}

static rowvec log_exp_colsum(mat log_y)
{
    double log_ymax = log_y.max();
    log_y = log_y - log_ymax;

    rowvec result = log(sum(exp(log_y)));
    return(result + log_ymax);
}

static double log_exp_sum(mat log_y)
{
    double log_ymax = log_y.max();
    if(log_ymax == -INFINITY) return(-INFINITY);
    log_y = log_y - log_ymax;

    double result = accu(exp(log_y));
    return(log(result) + log_ymax);
}

static vec log_sample(vec log_y){  //return unnormalized prob
    double log_ymax = arma::max(log_y);
    log_y -= log_ymax;
    return(exp(log_y));
}

static vec log_sample_norm(mat & log_y){  //return normalized prob
        double x = log_exp_sum(log_y);
    if(x == INFINITY){ log_y.fill(0); return(log_y);}
        log_y -= x;
        log_y = exp(log_y);
    return(log_y);
}

static void printZ(int N, vector<int> & Z, int (*children)[2])
{
    //int j = root;

    //cout << nodes_names[j]<<"\t";
    bool precol =false;
    queue<int> stack;
    vector<bool> colors = vector<bool> (N,false);

    stack.push(N-1);

    while(stack.size()>0)
    {
        int j = stack.front();

        if(colors[j]!=precol) cout<<endl;
        cout << j << ": "<< Z[j] <<", ";

        precol = colors[j];

        stack.pop();

        if(children[j][0]!=-1)
        {
            for(int chi=0;chi<2;chi++)
            {
                int c = children[j][chi];
                colors[c]=!colors[j];
                stack.push(c);

            }

        }
    }
    cout<<endl;
    cout<<endl;

}

void MatchProfAndTree(PhyloProf & _prof, PhyloTree & _tree);
    void InitPhyloTree(PhyloTree & tree);//double indel, double indel2 = 0.01, double indel_pi = 0.5);
void InitMCMC(int _num_burn, int _num_mcmc, int _num_thin);
void Output_init(PhyloProf & prof, string output_path, vector<int> & ids);
    void Output_init0(PhyloProf & prof, ofstream& out_lik, vector<int> & ids);
void getSubtree(int root, vector<int> & visited_init);
void getSubtree(int root, set<int>& child, vector<int> & visited_init);
void getUppertree(int root, vector<int>& child, set<int> & visited_init);

    void sample_indel(vector< string> & X, double missing_thres, string output_path, int _iter = 100, int _MH = 10, bool _verbose = true);
void sample_proposal(int iter, double & lrate_prop, double & grate_prop, ofstream & output);
void sample_hyperparam(int iter, vector<int> & ids, ofstream & output); //double indel_prop, double indel2_prop,
double log_lik(vector< vector<vec> > & lambda, double indel, double indel2, int iter, int block, vector<unsigned int> & v, double p);
    };

#endif /* bpp_hpp */


#include "AlexandriaJNI.h"

#include <jni.h>

#include <iostream>

#include <boost/algorithm/string.hpp>

#include <sophiatx/utilities/key_conversion.hpp>
#include <sophiatx/protocol/transaction.hpp>

#include <fc/io/json.hpp>
#include <fc/real128.hpp>
#include <fc/crypto/base58.hpp>
#include <fc/crypto/aes.hpp>
#include <fc/crypto/rand.hpp>
#include <fc/api.hpp>

using namespace sophiatx::utilities;
using namespace sophiatx::protocol;
using namespace fc::ecc;
using namespace std;

struct Java_AlexandriaJNI_memo_data {

   static std::optional<Java_AlexandriaJNI_memo_data> from_string( string str ) {
      try {
         if( str.size() > sizeof(Java_AlexandriaJNI_memo_data)) {
            auto data = fc::from_base58( str );
            auto m  = fc::raw::unpack_from_vector<Java_AlexandriaJNI_memo_data>( data, 0 );
            FC_ASSERT( string(m) == str );
            return m;
         }
      } catch ( ... ) {}
      return std::optional<Java_AlexandriaJNI_memo_data>();
   }

   int64_t         nonce = 0;
   uint64_t        check = 0;
   vector<char>    encrypted;

   operator string()const {
      auto data = fc::raw::pack_to_vector(*this);
      auto base58 = fc::to_base58( data );
      return base58;
   }
};

JNIEXPORT jbyteArray JNICALL Java_AlexandriaJNI_generatePrivateKey(JNIEnv *env, jclass) {
   try {
      private_key_type priv_key = fc::ecc::private_key::generate();

      std::vector<char> key_bytes = fc::variant(priv_key).as<std::vector<char>>();

      jbyteArray ret = env->NewByteArray(static_cast<jsize>(key_bytes.size()));

      if (env->ExceptionCheck() || ret == nullptr) {
         return nullptr;
      }
      env->SetByteArrayRegion(ret, 0, static_cast<jsize>(key_bytes.size()), reinterpret_cast<jbyte*>(key_bytes.data()));

      if (env->ExceptionCheck()) {
         env->ReleaseByteArrayElements(ret, reinterpret_cast<jbyte*>(key_bytes.data()), 0);
         return nullptr;
      }
      return ret;
    } catch (const fc::exception& e) {
       return nullptr;
    }
}

JNIEXPORT jbyteArray JNICALL Java_AlexandriaJNI_generatePrivateKeyFromBrainKey(JNIEnv *env, jclass,
                                                                               jstring inJNIStrBrainKey) {

   const char *brain_key = env->GetStringUTFChars(inJNIStrBrainKey, NULL);
   if (brain_key == nullptr || env->ExceptionCheck()) {
      return nullptr;
   }

   try {
      fc::sha512 h = fc::sha512::hash(string(brain_key) + " 0");
      env->ReleaseStringUTFChars(inJNIStrBrainKey, brain_key);

      auto priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(h));

      std::vector<char> key_bytes = fc::variant(priv_key).as<std::vector<char>>();

      jbyteArray ret = env->NewByteArray(static_cast<jsize>(key_bytes.size()));

      if (env->ExceptionCheck() || ret == nullptr) {
         return nullptr;
      }
      env->SetByteArrayRegion(ret, 0, static_cast<jsize>(key_bytes.size()), reinterpret_cast<jbyte*>(key_bytes.data()));

      if (env->ExceptionCheck()) {
         env->ReleaseByteArrayElements(ret, reinterpret_cast<jbyte*>(key_bytes.data()), 0);
         return nullptr;
      }
      return ret;

   } catch (const fc::exception& e) {
      env->ReleaseStringUTFChars(inJNIStrBrainKey, brain_key);
      return nullptr;
   }

}

JNIEXPORT jbyteArray JNICALL Java_AlexandriaJNI_getPublicKey(JNIEnv *env, jclass, jbyteArray private_key) {
   try {
      int length = env->GetArrayLength(private_key);
      if(length == -1) {
         return nullptr;
      }

      std::vector<char> private_k((size_t)length);
      env->GetByteArrayRegion(private_key, 0, length, reinterpret_cast<jbyte*>(&private_k.front()));

      if (env->ExceptionCheck()) {
         return nullptr;
      }

      fc::ecc::private_key key = fc::variant(private_k).as<fc::ecc::private_key>();

      public_key_type pub_key = key.get_public_key();

      public_key_type::binary_key k;
      k.data = pub_key.key_data;
      k.check = fc::ripemd160::hash( k.data.data, static_cast<uint32_t>(k.data.size()) )._hash[0];
      auto data = fc::raw::pack_to_vector( k );

      jbyteArray ret = env->NewByteArray(static_cast<jsize>(data.size()));

      if (env->ExceptionCheck() || ret == nullptr) {
         return nullptr;
      }
      env->SetByteArrayRegion(ret, 0, static_cast<jsize>(data.size()), reinterpret_cast<jbyte*>(data.data()));

      if (env->ExceptionCheck()) {
         env->ReleaseByteArrayElements(ret, reinterpret_cast<jbyte*>(data.data()), 0);
         return nullptr;
      }

      return ret;
   } catch (const fc::exception& e) {
      return nullptr;
   }
}

JNIEXPORT jbyteArray JNICALL Java_AlexandriaJNI_getTransactionDigest(JNIEnv *env, jclass, jstring inJNIStrTransaction,
                                                                     jbyteArray inJNIChainID) {

   const char *transaction = env->GetStringUTFChars(inJNIStrTransaction, 0);
   if (transaction == nullptr) {
      return nullptr;
   }

   int length = env->GetArrayLength(inJNIChainID);
   if(length == -1) {
      env->ReleaseStringUTFChars(inJNIStrTransaction, transaction);
      return nullptr;
   }

   std::vector<char> chain_id((size_t)length);
   env->GetByteArrayRegion(inJNIChainID, 0, length, reinterpret_cast<jbyte*>(&chain_id.front()));

   if (env->ExceptionCheck()) {
      env->ReleaseStringUTFChars(inJNIStrTransaction, transaction);
      return nullptr;
   }

   try {
      string tx_str(transaction);
      fc::variant v = fc::json::from_string( tx_str, fc::json::strict_parser );
      signed_transaction stx;
      fc::from_variant( v, stx);
      digest_type dig = stx.sig_digest(fc::sha256(chain_id.data(), chain_id.size()));
      env->ReleaseStringUTFChars(inJNIStrTransaction, transaction);

      jbyteArray ret = env->NewByteArray(static_cast<jsize>(dig.data_size()));

      if (env->ExceptionCheck() || ret == nullptr) {
         return nullptr;
      }
      env->SetByteArrayRegion(ret, 0, static_cast<jsize>(dig.data_size()), reinterpret_cast<jbyte*>(dig.data()));

      if (env->ExceptionCheck()) {
         env->ReleaseByteArrayElements(ret, reinterpret_cast<jbyte*>(dig.data()), 0);
         return nullptr;
      }

      return ret;
   } catch (const fc::exception& e) {
      env->ReleaseStringUTFChars(inJNIStrTransaction, transaction);
      return nullptr;
   }
}

JNIEXPORT jbyteArray JNICALL Java_AlexandriaJNI_signDigest(JNIEnv *env, jclass, jbyteArray inJNIDigest,
                                                           jbyteArray inJNIPrivateKey) {

   int length = env->GetArrayLength(inJNIDigest);
   if(length == -1) {
      return nullptr;
   }

   std::vector<char> digest((size_t)length);
   env->GetByteArrayRegion(inJNIDigest, 0, length, reinterpret_cast<jbyte*>(&digest.front()));

   if (env->ExceptionCheck()) {
      return nullptr;
   }

   length = env->GetArrayLength(inJNIPrivateKey);
   if(length == -1) {
      return nullptr;
   }

   std::vector<char> private_key((size_t)length);
   env->GetByteArrayRegion(inJNIPrivateKey, 0, length, reinterpret_cast<jbyte*>(&private_key.front()));

   if (env->ExceptionCheck()) {
      return nullptr;
   }

    try {
       fc::sha256 dig(digest.data(), digest.size());
       fc::ecc::private_key key = fc::variant(private_key).as<fc::ecc::private_key>();
       auto sig = key.sign_compact(dig, fc::ecc::bip_0062);

       jbyteArray ret = env->NewByteArray(static_cast<jsize>(sig.size()));

       if (env->ExceptionCheck() || ret == nullptr) {
          return nullptr;
       }
       env->SetByteArrayRegion(ret, 0, static_cast<jsize>(sig.size()), reinterpret_cast<jbyte*>(sig.data));

       if (env->ExceptionCheck()) {
          env->ReleaseByteArrayElements(ret, reinterpret_cast<jbyte*>(sig.data), 0);
          return nullptr;
       }

       return ret;
    } catch (const fc::exception& e) {

       return nullptr;
    }
 }

JNIEXPORT jstring JNICALL Java_AlexandriaJNI_addSignature(JNIEnv *env, jclass, jstring inJNIStrTransaction,
                                                          jbyteArray inJNISignature) {

   const char *transaction = env->GetStringUTFChars(inJNIStrTransaction, 0);
   if (transaction == nullptr) {
      return nullptr;
   }

   int length = env->GetArrayLength(inJNISignature);
   if(length != 65) {
      env->ReleaseStringUTFChars(inJNIStrTransaction, transaction);
      return nullptr;
   }

   compact_signature signature;
   env->GetByteArrayRegion(inJNISignature, 0, 65, reinterpret_cast<jbyte*>(&signature));

   if (env->ExceptionCheck()) {
      env->ReleaseStringUTFChars(inJNIStrTransaction, transaction);
      return nullptr;
   }

    try {
       string tx_str(transaction);
       fc::variant v = fc::json::from_string( tx_str, fc::json::strict_parser );
       signed_transaction stx;
       fc::from_variant( v, stx );

       stx.signatures.push_back(signature);

       env->ReleaseStringUTFChars(inJNIStrTransaction, transaction);
       return env->NewStringUTF(fc::json::to_string(stx).c_str());

    } catch (const fc::exception& e) {
       env->ReleaseStringUTFChars(inJNIStrTransaction, transaction);
       return nullptr;
    }
 }

JNIEXPORT jboolean JNICALL Java_AlexandriaJNI_verifySignature(JNIEnv *env, jclass, jbyteArray inJNIDigest,
                                                              jbyteArray inJNIPublicKey, jbyteArray inJNISignature) {
   int length = env->GetArrayLength(inJNISignature);
   if(length != 65) {
      return static_cast<jboolean>(false);
   }

   compact_signature signature;
   env->GetByteArrayRegion(inJNISignature, 0, 65, reinterpret_cast<jbyte*>(&signature));

   if (env->ExceptionCheck()) {
      return static_cast<jboolean>(false);
   }

   length = env->GetArrayLength(inJNIDigest);
   if(length == -1) {
      return static_cast<jboolean>(false);
   }

   std::vector<char> digest((size_t)length);
   env->GetByteArrayRegion(inJNIDigest, 0, length, reinterpret_cast<jbyte*>(&digest.front()));

   if (env->ExceptionCheck()) {
      return static_cast<jboolean>(false);
   }

   length = env->GetArrayLength(inJNIPublicKey);
   if(length == -1) {
      return static_cast<jboolean>(false);
   }

   std::vector<char> pub_key((size_t)length);
   env->GetByteArrayRegion(inJNIPublicKey, 0, length, reinterpret_cast<jbyte*>(&pub_key.front()));

   if (env->ExceptionCheck()) {
      return static_cast<jboolean>(false);
   }

   try {
       fc::sha256 dig(digest.data(), digest.size());

       auto bin_key = fc::raw::unpack_from_vector<public_key_type::binary_key>(pub_key, 0);
       public_key_type public_key(bin_key.data);

       if(public_key == fc::ecc::public_key::recover_key(signature, dig, fc::ecc::bip_0062)) {
          return static_cast<jboolean>(true);
       }
   } catch (const fc::exception& e) {
      return static_cast<jboolean>(false);
   }
   return static_cast<jboolean>(false);
 }

JNIEXPORT jstring JNICALL Java_AlexandriaJNI_encryptMemo(JNIEnv *env, jclass, jstring inJNIStrMemo,
                                                         jbyteArray inJNIPrivateKey, jbyteArray inJNIPublicKey) {

   const char *memo = env->GetStringUTFChars(inJNIStrMemo, 0);
   if (memo == nullptr) {
      return nullptr;
   }

   int length = env->GetArrayLength(inJNIPrivateKey);
   if(length == -1) {
      return nullptr;
   }

   std::vector<char> private_key((size_t)length);
   env->GetByteArrayRegion(inJNIPrivateKey, 0, length, reinterpret_cast<jbyte*>(&private_key.front()));

   if (env->ExceptionCheck()) {
      env->ReleaseStringUTFChars(inJNIStrMemo, memo);
      return nullptr;
   }

   length = env->GetArrayLength(inJNIPublicKey);
   if(length == -1) {
      env->ReleaseStringUTFChars(inJNIStrMemo, memo);
      return nullptr;
   }

   std::vector<char> pub_key((size_t)length);
   env->GetByteArrayRegion(inJNIPublicKey, 0, length, reinterpret_cast<jbyte*>(&pub_key.front()));

   if (env->ExceptionCheck()) {
      env->ReleaseStringUTFChars(inJNIStrMemo, memo);
      return nullptr;
   }

    try {
       Java_AlexandriaJNI_memo_data m;

       auto bin_key = fc::raw::unpack_from_vector<public_key_type::binary_key>(pub_key, 0);
       public_key_type public_key(bin_key.data);

       fc::ecc::private_key key = fc::variant(private_key).as<fc::ecc::private_key>();

       m.nonce = fc::time_point::now().time_since_epoch().count();

       auto shared_secret = key.get_shared_secret( public_key );

       fc::sha512::encoder enc;
       fc::raw::pack( enc, m.nonce );
       fc::raw::pack( enc, shared_secret );
       auto encrypt_key = enc.result();

       m.encrypted = fc::aes_encrypt( encrypt_key, fc::raw::pack_to_vector(string(memo)) );
       m.check = fc::sha256::hash( encrypt_key )._hash[0];

       env->ReleaseStringUTFChars(inJNIStrMemo, memo);

       return env->NewStringUTF(string(m).c_str());

    } catch (const fc::exception& e) {
       env->ReleaseStringUTFChars(inJNIStrMemo, memo);
       return nullptr;
    }
 }

JNIEXPORT jstring JNICALL Java_AlexandriaJNI_decryptedMemo(JNIEnv *env, jclass, jstring inJNIStrMemo,
                                                           jbyteArray inJNIPrivateKey, jbyteArray inJNIPublicKey) {

   const char *memo = env->GetStringUTFChars(inJNIStrMemo, 0);
   if (memo == nullptr) {
      return nullptr;
   }

   int length = env->GetArrayLength(inJNIPrivateKey);
   if(length == -1) {
      return nullptr;
   }

   std::vector<char> private_key((size_t)length);
   env->GetByteArrayRegion(inJNIPrivateKey, 0, length, reinterpret_cast<jbyte*>(&private_key.front()));

   if (env->ExceptionCheck()) {
      env->ReleaseStringUTFChars(inJNIStrMemo, memo);
      return nullptr;
   }

   length = env->GetArrayLength(inJNIPublicKey);
   if(length == -1) {
      env->ReleaseStringUTFChars(inJNIStrMemo, memo);
      return nullptr;
   }

   std::vector<char> pub_key((size_t)length);
   env->GetByteArrayRegion(inJNIPublicKey, 0, length, reinterpret_cast<jbyte*>(&pub_key.front()));

   if (env->ExceptionCheck()) {
      env->ReleaseStringUTFChars(inJNIStrMemo, memo);
      return nullptr;
   }

    try {
       string str_memo(memo);
       auto m = Java_AlexandriaJNI_memo_data::from_string( str_memo );

       if( m ) {
          fc::sha512 shared_secret;
          auto bin_key = fc::raw::unpack_from_vector<public_key_type::binary_key>(pub_key, 0);
          public_key_type public_key(bin_key.data);

          fc::ecc::private_key key = fc::variant(private_key).as<fc::ecc::private_key>();

          shared_secret = key.get_shared_secret(public_key);

          fc::sha512::encoder enc;
          fc::raw::pack( enc, m->nonce );
          fc::raw::pack( enc, shared_secret );
          auto encryption_key = enc.result();

          uint64_t check = fc::sha256::hash( encryption_key )._hash[0];
          if( check != m->check ) {
             env->ReleaseStringUTFChars(inJNIStrMemo, memo);
             return nullptr;
          }

          vector<char> decrypted = fc::aes_decrypt( encryption_key, m->encrypted );

          env->ReleaseStringUTFChars(inJNIStrMemo, memo);
          return env->NewStringUTF(fc::raw::unpack_from_vector<std::string>( decrypted, 0 ).c_str());
       } else {
          env->ReleaseStringUTFChars(inJNIStrMemo, memo);
          return nullptr;
       }
    } catch (const fc::exception& e) {
       env->ReleaseStringUTFChars(inJNIStrMemo, memo);
       return nullptr;
    }
}

JNIEXPORT jstring JNICALL Java_AlexandriaJNI_publicKeyToString(JNIEnv *env, jclass, jbyteArray inJNIPublicKey) {

   int length = env->GetArrayLength(inJNIPublicKey);
      if(length == -1) {
      return nullptr;
   }

   std::vector<char> pub_key((size_t)length);
   env->GetByteArrayRegion(inJNIPublicKey, 0, length, reinterpret_cast<jbyte*>(&pub_key.front()));

   if (env->ExceptionCheck()) {
      return nullptr;
   }
   try {
      auto bin_key = fc::raw::unpack_from_vector<public_key_type::binary_key>(pub_key, 0);
      public_key_type public_key(bin_key.data);
      auto public_key_str = fc::json::to_string(public_key);
      return env->NewStringUTF(public_key_str.substr(1, public_key_str.size() - 2).c_str());

   } catch (const fc::exception& e) {
      return nullptr;
   }

}

JNIEXPORT jbyteArray JNICALL Java_AlexandriaJNI_stringToPublicKey(JNIEnv *env, jclass, jstring inJNIStrPublicKey) {

   const char *public_key = env->GetStringUTFChars(inJNIStrPublicKey, 0);
   if (public_key == nullptr) {
      return nullptr;
   }

   try {
      fc::variant v = fc::json::from_string( string(public_key), fc::json::relaxed_parser );
      public_key_type pub_key;
      fc::from_variant( v, pub_key );

      env->ReleaseStringUTFChars(inJNIStrPublicKey, public_key);

      public_key_type::binary_key k;
      k.data = pub_key.key_data;
      k.check = fc::ripemd160::hash( k.data.data, static_cast<uint32_t>(k.data.size()) )._hash[0];
      auto data = fc::raw::pack_to_vector( k );

      jbyteArray ret = env->NewByteArray(static_cast<jsize>(data.size()));

      if (env->ExceptionCheck() || ret == nullptr) {
         return nullptr;
      }
      env->SetByteArrayRegion(ret, 0, static_cast<jsize>(data.size()), reinterpret_cast<jbyte*>(data.data()));

      if (env->ExceptionCheck()) {
         env->ReleaseByteArrayElements(ret, reinterpret_cast<jbyte*>(data.data()), 0);
         return nullptr;
      }

      return ret;
   } catch (const fc::exception& e) {
      return nullptr;
   }

}

JNIEXPORT jstring JNICALL Java_AlexandriaJNI_privateKeyToWif(JNIEnv *env, jclass, jbyteArray array) {
   try {
      int length = env->GetArrayLength(array);
      if(length == -1) {
         return nullptr;
      }

      std::vector<char> private_k((size_t)length);
      env->GetByteArrayRegion(array, 0, length, reinterpret_cast<jbyte*>(&private_k.front()));

      if (env->ExceptionCheck()) {
         return nullptr;
      }

      fc::ecc::private_key key = fc::variant(private_k).as<fc::ecc::private_key>();

      return env->NewStringUTF(key_to_wif(key).c_str());

   } catch (const fc::exception& e) {
      return nullptr;
   }
}

JNIEXPORT jbyteArray JNICALL Java_AlexandriaJNI_wifToPrivateKey(JNIEnv *env, jclass, jstring inJNIStrPrivateKey) {
   const char *private_key = env->GetStringUTFChars(inJNIStrPrivateKey, 0);

   if (private_key == nullptr || env->ExceptionCheck()) {
      return nullptr;
   }

   try {
      auto priv_key = sophiatx::utilities::wif_to_key(string(private_key));
      env->ReleaseStringUTFChars(inJNIStrPrivateKey, private_key);

      if(!priv_key) {
         return nullptr;
      }

      std::vector<char> key_bytes = fc::variant(*priv_key).as<std::vector<char>>();

      jbyteArray ret = env->NewByteArray(static_cast<jsize>(key_bytes.size()));

      if (env->ExceptionCheck() || ret == nullptr) {
         return nullptr;
      }
      env->SetByteArrayRegion(ret, 0, static_cast<jsize>(key_bytes.size()), reinterpret_cast<jbyte*>(key_bytes.data()));

      if (env->ExceptionCheck()) {
         env->ReleaseByteArrayElements(ret, reinterpret_cast<jbyte*>(key_bytes.data()), 0);
         return nullptr;
      }
      return ret;
   } catch (const fc::exception& e) {
      env->ReleaseStringUTFChars(inJNIStrPrivateKey, private_key);
      return nullptr;
   }
}

JNIEXPORT jstring JNICALL Java_AlexandriaJNI_toBase58(JNIEnv *env, jclass, jbyteArray array) {
   try {
      int length = env->GetArrayLength(array);
      if(length == -1) {
         return nullptr;
      }

      std::vector<char> data((size_t)length);
      env->GetByteArrayRegion(array, 0, length, reinterpret_cast<jbyte*>(&data.front()));

      if (env->ExceptionCheck()) {
         return nullptr;
      }

      return env->NewStringUTF(fc::to_base58(data).c_str());
   } catch (const fc::exception& e) {
      return nullptr;
   }
}

JNIEXPORT jbyteArray JNICALL Java_AlexandriaJNI_fromBase58(JNIEnv *env, jclass, jstring inJNIStrdata) {
   const char *str_data = env->GetStringUTFChars(inJNIStrdata, 0);

   if (str_data == nullptr || env->ExceptionCheck()) {
      return nullptr;
   }

   try {
      std::vector<char> bytes = fc::from_base58(string(str_data));
      env->ReleaseStringUTFChars(inJNIStrdata, str_data);

      jbyteArray ret = env->NewByteArray(static_cast<jsize>(bytes.size()));

      if (env->ExceptionCheck() || ret == nullptr) {
         return nullptr;
      }
      env->SetByteArrayRegion(ret, 0, static_cast<jsize>(bytes.size()), reinterpret_cast<jbyte*>(bytes.data()));

      if (env->ExceptionCheck()) {
         env->ReleaseByteArrayElements(ret, reinterpret_cast<jbyte*>(bytes.data()), 0);
         return nullptr;
      }
      return ret;

   } catch (const fc::exception& e) {
      env->ReleaseStringUTFChars(inJNIStrdata, str_data);
      return nullptr;
   }
}

FC_REFLECT( Java_AlexandriaJNI_memo_data, (nonce)(check)(encrypted) )


// __BEGIN_LICENSE__
//  Copyright (c) 2006-2012, United States Government as represented by the
//  Administrator of the National Aeronautics and Space Administration. All
//  rights reserved.
//
//  The NASA Vision Workbench is licensed under the Apache License,
//  Version 2.0 (the "License"); you may not use this file except in
//  compliance with the License. You may obtain a copy of the License at
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// __END_LICENSE__

#ifdef _MSC_VER
#pragma warning(disable:4244)
#pragma warning(disable:4267)
#pragma warning(disable:4996)
#endif

#include <cstdlib>
#include <iostream>
#include <cmath>
#include <boost/tokenizer.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/numeric/conversion/cast.hpp>
#include <boost/program_options.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/foreach.hpp>
namespace fs = boost::filesystem;
namespace po = boost::program_options;

#include <vw/Core/Functors.h>
#include <vw/Image/Algorithms.h>
#include <vw/Image/ImageMath.h>
#include <vw/Image/ImageViewRef.h>
#include <vw/Image/PerPixelViews.h>
#include <vw/Image/PixelMask.h>
#include <vw/Image/MaskViews.h>
#include <vw/Image/PixelTypes.h>
#include <vw/Image/Statistics.h>
#include <vw/FileIO/DiskImageView.h>
#include <vw/Cartography/GeoReference.h>
#include <vw/tools/Common.h>
#include <vw/FileIO/DiskImageResourceGDAL.h>
#include <vw/Image/Interpolation.h>
#include <asp/Core/Macros.h>
#include <asp/Core/Common.h>

using namespace vw;
using namespace vw::cartography;

struct Options : asp::BaseOptions {};

// Convolve a DEM with exp(-sigma*x^2). The input DEM must
// have its invalid pixels masked.

template <class ImageT>
class BlurDEM:
  public ImageViewBase< BlurDEM<ImageT> > {
  ImageT m_img;
  int m_search_dist; // half of size of window to convolve with
  ImageView<double> m_gauss_kernel;
  typedef typename ImageT::pixel_type PixelT;

public:

  typedef PixelT pixel_type;
  typedef PixelT result_type;
  typedef ProceduralPixelAccessor<BlurDEM> pixel_accessor;

  BlurDEM( ImageViewBase<ImageT> const& img,
                     double blur_sigma) :
    m_img(img.impl()) {
    VW_ASSERT(blur_sigma > 0,
              ArgumentErr() << "Expecting positive sigma.");

    // Cut the gaussian exp(-sigma*x^2) where its value is 'scale'.
    double scale = 0.001;
    m_search_dist = (int)ceil(sqrt(-log(scale)/blur_sigma));
    std::cout << "Search distance is " << m_search_dist << std::endl;

    // The gaussian kernel
    int h = m_search_dist;
    m_gauss_kernel.set_size(2*h+1, 2*h+1);
    for (int c = 0; c < m_gauss_kernel.cols(); c++){
      for (int r = 0; r < m_gauss_kernel.rows(); r++){
        double r2 = double(c-h)*(c-h) + double(r-h)*(r-h);
        m_gauss_kernel(c, r) = exp(-blur_sigma*r2);
      }
    }

  }

  inline int32 cols() const { return m_img.cols(); }
  inline int32 rows() const { return m_img.rows(); }
  inline int32 planes() const { return 1; }

  inline pixel_accessor origin() const { return pixel_accessor(*this); }

  inline result_type operator()( size_t i, size_t j, size_t p=0 ) const {
    vw_throw( NoImplErr() << "BlurDEM: operator() not implemented.\n" );
  }

  typedef CropView< ImageView<PixelT> > prerasterize_type;
  inline prerasterize_type prerasterize( BBox2i const& bbox ) const {

    // Crop into an expanded box as to have enough pixels to do
    // the blurring at every pixel in the current box.
    int h = m_search_dist; // shorten
    BBox2i biased_box = bbox;
    biased_box.expand(h+1);
    biased_box.crop(bounding_box(m_img));
    ImageView<PixelT> img( crop( m_img, biased_box ) );
    ImageView<PixelT> filled_img = copy(img);

    int nc = img.cols(), nr = img.rows(); // shorten
    for (int row = 0; row < nr; row++){
      for (int col = 0; col < nc; col++){
        PixelT V; V.validate();
        double sum = 0.0;
        for (int c = std::max(col-h, 0); c <= std::min(col+h, nc-1); c++){
          for (int r = std::max(row-h, 0); r <= std::min(row+h, nr-1); r++){
            if (!is_valid(img(c, r))) continue;
            double wt = m_gauss_kernel(c-col+h, r-row+h);
              V   += wt*img(c, r);
              sum += wt;
          }
        }
        if (sum > 0) filled_img(col, row) = V/sum;

      }
    }

    return prerasterize_type(filled_img,
                             -biased_box.min().x(), -biased_box.min().y(),
                             cols(), rows());

  }
  template <class ImgT>
  inline void rasterize( ImgT const& img, BBox2i const& bbox ) const {
    vw::rasterize( prerasterize(bbox), img, bbox );
  }

};

template <class ImgT>
BlurDEM<ImgT>
blur_dem( ImageViewBase<ImgT> const& img, double blur_sigma) {
  typedef BlurDEM<ImgT> result_type;
  return result_type( img.impl(), blur_sigma);
}

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

  Options opt;

  if (argc < 4){
    std::cerr << "Usage: " << argv[0] << " input-DEM.tif blur_sigma output-DEM.tif"
              << std::endl;
    exit(1);
  }

  std::string infile = argv[1];
  double blur_sigma = atof(argv[2]);
  std::string outfile = argv[3];

  std::cout << "Reading: " << infile << std::endl;
  std::cout << "blur sigma is " << blur_sigma << std::endl;

  DiskImageResourceGDAL in_rsrc(infile);
  float nodata_val = -32768;
  if ( in_rsrc.has_nodata_read() ) {
    nodata_val = in_rsrc.nodata_read();
    vw_out() << "\tFound input nodata value: " << nodata_val << std::endl;
  }else{
    std::cerr << "Nodata value not found in: " << infile << std::endl;
    exit(1);
  }

  DiskImageView<float> dem(in_rsrc);

  GeoReference georef;
  read_georeference(georef, in_rsrc);

  std::cout << "Writing: " << outfile << std::endl;
  block_write_gdal_image(outfile,
                         apply_mask
                         (blur_dem
                          (create_mask(dem, nodata_val), blur_sigma),
                          nodata_val),
                         georef, nodata_val, opt,
                         TerminalProgressCallback("asp","")
                         );
  return 0;

}


#include <Eigen/Dense>

#include <gpd/net/lenet.h>

namespace gpd {
namespace test {
namespace {

int DoMain(int argc, char *argv[]) {
  // Test with input from file.
  net::Lenet network(
      1, "/home/atenpas/catkin_ws/src/gpd_no_ros/lenet/3channels/params/", 60,
      15);
  std::vector<float> x = network.readFileLineByLineIntoVector(
      "src/gpd/caffe/15channels/txt/rand_input.txt");
  network.forward(x);

  return 0;
}

}  // namespace
}  // namespace test
}  // namespace gpd

int main(int argc, char *argv[]) { return gpd::test::DoMain(argc, argv); }


#include "Expression.h"

#include "Parser.h"
#include "RuntimeInfo.h"
#include "Utils.h"
#include <boost/format.hpp>
#include <boost/locale/collator.hpp>
#include <boost/algorithm/string.hpp>

namespace FileSearch {
namespace AppCore{

class EqualsVisitor : public boost::static_visitor<bool> {
public:
    template <typename T, typename U>
    bool operator()(const T &, const U &) const
    {
        throw EvaluateException("Cannot compare different types");
    }

    template <typename T>
    bool operator()(const T & lhs, const T & rhs) const
    {
        return lhs == rhs;
    }
};

class NotEqualsVisitor : public boost::static_visitor<bool> {
public:
    template <typename T, typename U>
    bool operator()(const T &, const U &) const
    {
        throw EvaluateException("Cannot compare different types");
    }

    template <typename T>
    bool operator()(const T & lhs, const T & rhs) const
    {
        return lhs != rhs;
    }
};

Expression::Expression(ExprNodePtr root, std::shared_ptr<RuntimeInfo> runtimeInfo) : root_(std::move(root)), runtimeInfo_(runtimeInfo) {

}

Expression::~Expression() {
    delTree(root_);
}

std::shared_ptr<RuntimeInfo> Expression::runtimeInfo() const {
    return runtimeInfo_;
}

ExprValue Expression::evaluate(PropertyMap&& properties) {
    properties_ = std::move(properties);
    result_ = calcTree(root_);
    return result_;
}

ExprValue Expression::calcTree(const ExprNodePtr& tree) {
    switch (tree->operationType) {
    case  OP_VARIABLE:
        return getPropertyValue(boost::get<std::string>(tree->value));
    case OP_VALUE:
        return tree->value;
    case OP_PLUS:
        try {
            return boost::get<ExprInteger>(calcTree(tree->left)) + boost::get<ExprInteger>(calcTree(tree->right));
        } catch (boost::bad_get&) {
            throw EvaluateException("Invalid argument for operator '+'");
        }
    case OP_MINUS:
        try {
            return boost::get<ExprInteger>(calcTree(tree->left)) - boost::get<ExprInteger>(calcTree(tree->right));
        } catch (boost::bad_get&) {
            throw EvaluateException("Invalid argument for operator '-'");
        }
    case OP_MULTIPLY:
        try {
            return boost::get<ExprInteger>(calcTree(tree->left)) * boost::get<ExprInteger>(calcTree(tree->right));
        } catch (boost::bad_get&) {
            throw EvaluateException("Invalid argument for operator '*'");
        }
    case OP_DIVIDE:
        try {
            return boost::get<ExprInteger>(calcTree(tree->left)) * boost::get<ExprInteger>(calcTree(tree->right));
        } catch (boost::bad_get&) {
            throw EvaluateException("Invalid argument for operator '*'");
        }
    case OP_CONTAINS:
        return operationContains(tree);
    case OP_LESS:
    case OP_LESSOREQUAL:
    case OP_GREATER:
    case OP_GREATEROREQUAL:
        return operationCompare(tree);
    case OP_EQUAL:
        return operationEquals(tree);
    case OP_NOTEQUAL:
        return operationNotEquals(tree);
    case OP_UMINUS:
        try {
            return -boost::get<ExprInteger>(calcTree(tree->left));
        } catch (boost::bad_get&) {
            throw EvaluateException("Invalid argument for unary minus operator");
        }
    case OP_NOT:
        return operationIsNot(tree);
        /*try {
            return !boost::get<bool>(calcTree(tree->left));
        } catch (boost::bad_get&) {
            throw EvaluateException("Invalid argument for 'not' operator");
        }*/
    case OP_OR:
    case OP_AND:
        return operationLogical(tree);
    case OP_IS:
        return operationIs(tree);
    case OP_FUNCTIONCALL:
        {
        ExprValue left = calcTree(tree->left);
        return runtimeInfo_->callFunction(tree, left);
        }
    default:
        throw ParserException(str(boost::format("Unknown operation %d") % static_cast<int>(tree->operationType)), 0);
    }
}

bool Expression::operationContains(const ExprNodePtr& node) {
    std::string left, right;
    try {
        left = boost::get<std::string>(calcTree(node->left));
    } catch (boost::bad_get&) {
        throw InvalidFunctionArgument("Invalid argument for operation 'contains'", left);
    }
    try {
        right = boost::get<std::string>(calcTree(node->right));
    } catch (boost::bad_get&) {
        throw InvalidFunctionArgument("Invalid argument for operation 'contains'", right);
    }
    namespace bl = boost::locale;
    bl::comparator<char, bl::collator_base::secondary> cmpr;
    std::wstring leftW = Utils::Utf8ToWideString(left);
    std::wstring rightW = Utils::Utf8ToWideString(right);

    auto range = boost::algorithm::ifind_first(left, right); // case insensitive search
    return Utils::FindSubstrCaseInsensitive(leftW, rightW) != -1;
}

bool Expression::operationCompare(const ExprNodePtr& node) {
    ExprInteger left, right;
    try {
        left = boost::get<ExprInteger>(calcTree(node->left));
    } catch (boost::bad_get&) {
        throw InvalidFunctionArgument("Invalid argument for comparison operator", left);
    }
    try {
        right = boost::get<ExprInteger>(calcTree(node->right));
    } catch (boost::bad_get&) {
        throw InvalidFunctionArgument("Invalid argument for comparison operator", right);
    }

    switch (node->operationType) {
        case OP_LESS:
            return left < right;
        case OP_LESSOREQUAL:
            return left <= right;
        case OP_GREATER:
            return left > right;
        case OP_GREATEROREQUAL:
            return left >= right;
        default:; // just for Resharper to be happy
    }
    assert(false);
    return false;
}

bool Expression::operationEquals(const ExprNodePtr& node) {
    auto left = calcTree(node->left);
    auto right = calcTree(node->right);
    return boost::apply_visitor(EqualsVisitor(), left, right);
}

bool Expression::operationNotEquals(const ExprNodePtr& node) {
    auto left = calcTree(node->left);
    auto right = calcTree(node->right);
    return boost::apply_visitor(NotEqualsVisitor(), left, right);
}

bool Expression::operationLogical(const ExprNodePtr& node) {
    bool left, right;
    try {
        left = boost::get<bool>(calcTree(node->left));
    } catch (boost::bad_get&) {
        throw InvalidFunctionArgument("Invalid argument for logical operator", left);
    }

    if (node->operationType == OP_AND && !left) {
        return false;// Do not calculate right side
    } else if (node->operationType == OP_OR && left) {
        return true;// Do not calculate right side
    }
    try {
        right = boost::get<bool>(calcTree(node->right));
    } catch (boost::bad_get&) {
        throw InvalidFunctionArgument("Invalid argument for logical operator", right);
    }

    switch (node->operationType) {
        case OP_AND:
            return left && right;
        case OP_OR:
            return left || right;
        default:; // just for Resharper to be happy
    }
    assert(false);
    return false;
}

bool Expression::operationIs(const ExprNodePtr& node) {
    FileAttributeSet left;
    FileAttribute right;
    try {
        left = boost::get<FileAttributeSet>(calcTree(node->left));
    } catch (boost::bad_get&) {
        throw InvalidFunctionArgument("Invalid left argument for operator 'is'", left);
    }
    try {
        right = boost::get<FileAttribute>(calcTree(node->right));
    } catch (boost::bad_get&) {
        throw InvalidFunctionArgument("Invalid right argument for operator 'is'", right);
    }
    return left.find(right) != left.end();
}

bool Expression::operationIsNot(const ExprNodePtr& node) {
    FileAttributeSet left;
    FileAttribute right;
    try {
        left = boost::get<FileAttributeSet>(calcTree(node->left));
    } catch (boost::bad_get&) {
        throw InvalidFunctionArgument("Invalid left argument for operator 'not'", left);
    }
    try {
        right = boost::get<FileAttribute>(calcTree(node->right));
    } catch (boost::bad_get&) {
        throw InvalidFunctionArgument("Invalid right argument for operator 'not'", right);
    }
    return left.find(right) == left.end();
}

ExprValue Expression::getPropertyValue(const std::string& propertyName) {
    auto callback = properties_.find(propertyName);
    if (callback == properties_.end()) {
        throw EvaluateException(str(boost::format("Property callback not set for property '%s'") % propertyName));
    }
    return callback->second();
}

void Expression::delTree(ExprNodePtr tree) {
    if (tree == nullptr) {
        return;
    }

    delTree(tree->left);
    delTree(tree->right);

    delete tree;
}

}
}


/*
 Copyright (C) 2016 Quaternion Risk Management Ltd
 All rights reserved.

 This file is part of ORE, a free-software/open-source library
 for transparent pricing and risk analysis - http://opensourcerisk.org

 ORE is free software: you can redistribute it and/or modify it
 under the terms of the Modified BSD License.  You should have received a
 copy of the license along with this program.
 The license is also available online at <http://opensourcerisk.org>

 This program is distributed on the basis that it will form a useful
 contribution to risk analytics and model standardisation, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
*/

/*! \file orea/aggregation/postprocess.hpp
    \brief Exposure aggregation and XVA calculation
    \ingroup analytics
*/

#pragma once

#include <orea/aggregation/collatexposurehelper.hpp>
#include <orea/cube/inmemorycube.hpp>
#include <orea/scenario/aggregationscenariodata.hpp>

#include <ored/portfolio/nettingsetmanager.hpp>
#include <ored/portfolio/portfolio.hpp>
#include <ored/report/report.hpp>

#include <ql/time/date.hpp>

#include <boost/shared_ptr.hpp>

namespace ore {
namespace analytics {
using namespace QuantLib;
using namespace data;

enum class AllocationMethod {
    None,
    Marginal, // Pykhtin & Rosen, 2010
    RelativeFairValueGross,
    RelativeFairValueNet,
    RelativeXVA
};

std::ostream& operator<<(std::ostream& out, AllocationMethod m);

AllocationMethod parseAllocationMethod(const string& s);

//! Exposure Aggregation and XVA Calculation
/*!
  This class aggregates NPV cube data, computes exposure statistics
  and various XVAs, all at trade and netting set level:

  1) Exposures
  - Expected Positive Exposure, EPE: E[max(NPV(t),0) / N(t)]
  - Expected Negative Exposure, ENE: E[max(-NPV(t),0) / N(t)]
  - Basel Expected Exposure, EE_B: EPE(t)/P(t)
  - Basel Expected Positive Exposure, EPE_B
  - Basel Effective Expected Exposure, EEE_B: max( EEE_B(t-1), EE_B(t))
  - Basel Effective Expected Positive Exposure, EEPE_B
  - Potential Future Exposure, PFE: q-Quantile of the distribution of

  2) Dynamic Initial Margin via regression

  3) XVAs:
  - Credit Value Adjustment, CVA
  - Debit Value Adjustment, DVA
  - Funding Value Adjustment, FVA
  - Collateral Value Adjustment, COLVA
  - Margin Value Adjustment, MVA

  4) Allocation from netting set to trade level such that allocated contributions
  add up to the netting set
  - CVA and DVA
  - EPE and ENE

  All analytics are precomputed when the class constructor is called.
  A number of inspectors described below then return the individual analytics results.

  Note:
  - exposures are discounted at the numeraire N(t) used in the
  Monte Carlo simulation which produces the NPV cube.
  - NPVs take collateral into account, depending on CSA settings

  \ingroup analytics

  \todo Introduce enumeration for TradeAction type and owner
  \todo Interpolation for DIM(t-MPOR) when the simulation grid spacing is different from MPOR
  \todo Revise alternatives to the RelativeXVA exposure and XVA allocation method
  \todo Add trade-level MVA
  \todo Take the spread received on posted initial margin into account in MVA calculation
*/
class PostProcess {
public:
    //! Constructor
    PostProcess( //! Trade portfolio to identidy e.g. netting set, maturity, break dates for each trade
        const boost::shared_ptr<Portfolio>& portfolio,
        //! Netting set manager to access CSA details for each netting set
        const boost::shared_ptr<NettingSetManager>& nettingSetManager,
        //! Market data object to access e.g. discounting and funcing curves
        const boost::shared_ptr<Market>& market,
        //! Market configuration to use
        const std::string& configuration,
        //! Input NPV Cube
        const boost::shared_ptr<NPVCube>& cube,
        //! Subset of simulated market data, index fixings and FX spot rates, associated with the NPV cube
        const boost::shared_ptr<AggregationScenarioData>& scenarioData,
        //! Selection of analytics to be produced
        const map<string, bool>& analytics,
        //! Expression currency for all results
        const string& baseCurrency,
        //! Method to be used for Exposure/XVA allocation down to trade level
        const string& allocationMethod,
        //! Cutoff parameter for the marginal allocation method below which we switch to equal disctribution
        Real cvaMarginalAllocationLimit,
        //! Quantile for Potential Future Exposure output
        Real quantile = 0.95,
        //! Collateral calculation type to be used, see class %CollateralExposureHelper
        const string& calculationType = "Symmetric",
        //! Credit curve name to be used for "our" credit risk in DVA calculations
        const string& dvaName = "",
        //! Borrowing curve name to be used in FVA calculations
        const string& fvaBorrowingCurve = "",
        //! Lending curve name to be used in FVA calculations
        const string& fvaLendingCurve = "",
        //! Quantile used in dynamic initial margin calculation
        Real dimQuantile = 0.99,
        //! Initial margin horizon in calendar days, 2 weeks = 14 days
        Size dimHorizonCalendarDays = 14,
        //! Order of the regression polynomial used in DIM estmation
        Size dimRegressionOrder = 0,
        //! Regressors to be used in the DIM estimation by regression, each must match an additional scenario data key
        vector<string> dimRegressors = vector<string>(),
        //! Number of local regression evaluations, e.g. to validata DIM by regression
        Size dimLocalRegressionEvaluations = 0,
        //! Local regression band width in standard deviations of the regression variable
        Real dimLocalRegressionBandwidth = 0,
        //! Scaling factor applied to all DIM values
        Real dimScaling = 1.0,
        //! Assume t=0 collateral balance equals NPV (set to 0 if false)
        bool fullInitialCollateralisation = false,
        //! own capital discounting rate for discounting expected capital for KVA
        Real kvaCapitalDiscountRate = 0.10,
        //! alpha to adjust EEPE to give EAD for risk capital
        Real kvaAlpha = 1.4,
        //! regulatory adjustment, 1/min cap requirement
        Real kvaRegAdjustment = 12.5,
        //! Cost of Capital for KVA = regulatory adjustment x capital hurdle
        Real kvaCapitalHurdle = 0.012,
        //! Our KVA PD floor
        Real kvaOurPdFloor = 0.03,
        //! Their KVA PD floor
        Real kvaTheirPdFloor = 0.03,
        //! Our KVA CVA Risk Weight
        Real kvaOurCvaRiskWeight = 0.05,
        //! Their KVA CVA Risk Weight,
        Real kvaTheirCvaRiskWeight = 0.05);

    //! Return list of Trade IDs in the portfolio
    const vector<string>& tradeIds() { return tradeIds_; }
    //! Return list of netting set IDs in the portfolio
    const vector<string>& nettingSetIds() { return nettingSetIds_; }

    //! Return trade level Expected Positive Exposure evolution
    const vector<Real>& tradeEPE(const string& tradeId);
    //! Return trade level Expected Negative Exposure evolution
    const vector<Real>& tradeENE(const string& tradeId);
    //! Return trade level Basel Expected Exposure evolution
    const vector<Real>& tradeEE_B(const string& tradeId);
    //! Return trade level Basel Expected Positive Exposure evolution
    const Real& tradeEPE_B(const string& tradeId);
    //! Return trade level Effective Expected Exposure evolution
    const vector<Real>& tradeEEE_B(const string& tradeId);
    //! Return trade level Effective Expected Positive Exposure evolution
    const Real& tradeEEPE_B(const string& tradeId);
    //! Return trade level Potential Future Exposure evolution
    const vector<Real>& tradePFE(const string& tradeId);
    // const vector<Real>& tradeVAR(const string& tradeId);

    //! Return Netting Set Expected Positive Exposure evolution
    const vector<Real>& netEPE(const string& nettingSetId);
    //! Return Netting Set Expected Negative Exposure evolution
    const vector<Real>& netENE(const string& nettingSetId);
    //! Return Netting Set Basel Expected Exposure evolution
    const vector<Real>& netEE_B(const string& nettingSetId);
    //! Return Netting Set Basel Expected Positive Exposure evolution
    const Real& netEPE_B(const string& nettingSetId);
    //! Return Netting Set Effective Expected Exposure evolution
    const vector<Real>& netEEE_B(const string& nettingSetId);
    //! Return Netting Set Effective Expected Positive Exposure evolution
    const Real& netEEPE_B(const string& nettingSetId);
    //! Return Netting Set Potential Future Exposure evolution
    const vector<Real>& netPFE(const string& nettingSetId);
    // const vector<Real>& netVAR(const string& nettingSetId);

    //! Return the netting set's expected collateral evolution
    const vector<Real>& expectedCollateral(const string& nettingSetId);
    //! Return the netting set's expected COLVA increments through time
    const vector<Real>& colvaIncrements(const string& nettingSetId);
    //! Return the netting set's expected Collateral Floor increments through time
    const vector<Real>& collateralFloorIncrements(const string& nettingSetId);

    //! Return the trade EPE, allocated down from the netting set level
    const vector<Real>& allocatedTradeEPE(const string& tradeId);
    //! Return trade ENE, allocated down from the netting set level
    const vector<Real>& allocatedTradeENE(const string& tradeId);

    //! Return trade (stand-alone) CVA
    Real tradeCVA(const string& tradeId);
    //! Return trade (stand-alone) DVA
    Real tradeDVA(const string& tradeId);
    //! Return trade (stand-alone) MVA
    Real tradeMVA(const string& tradeId);
    //! Return trade (stand-alone) FBA (Funding Benefit Adjustment)
    Real tradeFBA(const string& tradeId);
    //! Return trade (stand-alone) FCA (Funding Cost Adjustment)
    Real tradeFCA(const string& tradeId);
    //! Return trade (stand-alone) FBA (Funding Benefit Adjustment) excluding own survival probability
    Real tradeFBA_exOwnSP(const string& tradeId);
    //! Return trade (stand-alone) FCA (Funding Cost Adjustment) excluding own survival probability
    Real tradeFCA_exOwnSP(const string& tradeId);
    //! Return trade (stand-alone) FBA (Funding Benefit Adjustment) excluding both survival probabilities
    Real tradeFBA_exAllSP(const string& tradeId);
    //! Return trade (stand-alone) FCA (Funding Cost Adjustment) excluding both survival probabilities
    Real tradeFCA_exAllSP(const string& tradeId);
    //! Return allocated trade CVA (trade CVAs add up to netting set CVA)
    Real allocatedTradeCVA(const string& tradeId);
    //! Return allocated trade DVA (trade DVAs add up to netting set DVA)
    Real allocatedTradeDVA(const string& tradeId);
    //! Return netting set CVA
    Real nettingSetCVA(const string& nettingSetId);
    //! Return netting set DVA
    Real nettingSetDVA(const string& nettingSetId);
    //! Return netting set MVA
    Real nettingSetMVA(const string& nettingSetId);
    //! Return netting set FBA
    Real nettingSetFBA(const string& nettingSetId);
    //! Return netting set FCA
    Real nettingSetFCA(const string& nettingSetId);
    //! Return netting set KVA-CCR
    Real nettingSetOurKVACCR(const string& nettingSetId);
    //! Return netting set KVA-CCR from counterparty persepctive
    Real nettingSetTheirKVACCR(const string& nettingSetId);
    //! Return netting set KVA-CVA
    Real nettingSetOurKVACVA(const string& nettingSetId);
    //! Return netting set KVA-CVA from counterparty persepctive
    Real nettingSetTheirKVACVA(const string& nettingSetId);
    //! Return netting set FBA excluding own survival probability
    Real nettingSetFBA_exOwnSP(const string& nettingSetId);
    //! Return netting set FCA excluding own survival probability
    Real nettingSetFCA_exOwnSP(const string& nettingSetId);
    //! Return netting set FBA excluding both survival probabilities
    Real nettingSetFBA_exAllSP(const string& nettingSetId);
    //! Return netting set FCA excluding both survival probabilities
    Real nettingSetFCA_exAllSP(const string& nettingSetId);
    //! Return netting set COLVA
    Real nettingSetCOLVA(const string& nettingSetId);
    //! Return netting set Collateral Floor value
    Real nettingSetCollateralFloor(const string& nettingSetId);

    //! Inspector for the input NPV cube (by trade, time, scenario)
    const boost::shared_ptr<NPVCube>& cube() { return cube_; }
    //! Return the  for the input NPV cube after netting and collateral (by netting set, time, scenario)
    const boost::shared_ptr<NPVCube>& netCube() { return nettedCube_; }
    //! Return the dynamic initial margin cube (regression approach)
    const boost::shared_ptr<NPVCube>& dimCube() { return dimCube_; }
    //! Write average (over samples) DIM evolution through time for all netting sets
    void exportDimEvolution(ore::data::Report& dimEvolutionReport);
    //! Write DIM as a function of sample netting set NPV for a given time step
    void exportDimRegression(const std::string& nettingSet, const std::vector<Size>& timeSteps,
                             const std::vector<boost::shared_ptr<ore::data::Report>>& dimRegReports);

private:
    //! Helper function to return the collateral account evolution for a given netting set
    boost::shared_ptr<vector<boost::shared_ptr<CollateralAccount>>>
    collateralPaths(const string& nettingSetId, const boost::shared_ptr<NettingSetManager>& nettingSetManager,
                    const boost::shared_ptr<Market>& market, const std::string& configuration,
                    const boost::shared_ptr<AggregationScenarioData>& scenarioData, Size dates, Size samples,
                    const vector<vector<Real>>& nettingSetValue, Real nettingSetValueToday,
                    const Date& nettingSetMaturity);

    void updateNettingSetKVA();
    void updateStandAloneXVA();
    void updateAllocatedXVA();

    //! Fill dynamic initial margin cube (per netting set, date and sample)
    void dynamicInitialMargin();
    //! Compile the array of DIM regressors for the specified netting set, date and sample index
    Disposable<Array> regressorArray(string nettingSet, Size dateIndex, Size sampleIndex);
    //! Perform the calculation of IM as of t=t0
    void performT0DimCalc();

    boost::shared_ptr<Portfolio> portfolio_;
    boost::shared_ptr<NettingSetManager> nettingSetManager_;
    boost::shared_ptr<Market> market_;
    const std::string configuration_;
    boost::shared_ptr<NPVCube> cube_;
    boost::shared_ptr<AggregationScenarioData> scenarioData_;
    map<string, bool> analytics_;

    map<string, vector<vector<Real>>> nettingSetNPV_, nettingSetFLOW_, nettingSetDIM_, nettingSetLocalDIM_,
        nettingSetDeltaNPV_;
    map<string, vector<vector<Array>>> regressorArray_;
    map<string, vector<Real>> nettingSetExpectedDIM_, nettingSetZeroOrderDIM_, nettingSetSimpleDIMh_,
        nettingSetSimpleDIMp_;
    map<string, vector<Real>> tradeEPE_, tradeENE_, tradeEE_B_, tradeEEE_B_, tradePFE_, tradeVAR_;
    map<string, Real> tradeEPE_B_, tradeEEPE_B_;
    map<string, vector<Real>> allocatedTradeEPE_, allocatedTradeENE_;
    map<string, vector<Real>> netEPE_, netENE_, netEE_B_, netEEE_B_, netPFE_, netVAR_, expectedCollateral_;
    map<string, Real> netEPE_B_, netEEPE_B_;
    map<string, vector<Real>> colvaInc_, eoniaFloorInc_;
    map<string, Real> tradeCVA_, tradeDVA_, tradeMVA_, tradeFBA_, tradeFCA_, tradeFBA_exOwnSP_, tradeFCA_exOwnSP_,
        tradeFBA_exAllSP_, tradeFCA_exAllSP_;
    map<string, Real> sumTradeCVA_, sumTradeDVA_; // per netting set
    map<string, Real> allocatedTradeCVA_, allocatedTradeDVA_;
    map<string, Real> nettingSetCVA_, nettingSetDVA_, nettingSetMVA_;
    map<string, Real> nettingSetCOLVA_, nettingSetCollateralFloor_;
    map<string, Real> ourNettingSetKVACCR_, theirNettingSetKVACCR_, ourNettingSetKVACVA_, theirNettingSetKVACVA_;
    map<string, Real> nettingSetFCA_, nettingSetFBA_, nettingSetFCA_exOwnSP_, nettingSetFBA_exOwnSP_,
        nettingSetFCA_exAllSP_, nettingSetFBA_exAllSP_;
    boost::shared_ptr<NPVCube> nettedCube_;
    boost::shared_ptr<NPVCube> dimCube_;
    map<string, Real> net_t0_im_reg_h_, net_t0_im_simple_h_;

    vector<string> tradeIds_;
    vector<string> nettingSetIds_;
    map<string, string> counterpartyId_; // for each nettingSetId
    string baseCurrency_;
    Real quantile_;
    CollateralExposureHelper::CalculationType calcType_;
    string dvaName_;
    string fvaBorrowingCurve_;
    string fvaLendingCurve_;
    Real dimQuantile_;
    Size dimHorizonCalendarDays_;
    Size dimRegressionOrder_;
    vector<string> dimRegressors_;
    Size dimLocalRegressionEvaluations_;
    Real dimLocalRegressionBandwidth_;
    Real dimScaling_;
    bool fullInitialCollateralisation_;
    Real kvaCapitalDiscountRate_;
    Real kvaAlpha_;
    Real kvaRegAdjustment_;
    Real kvaCapitalHurdle_;
    Real kvaOurPdFloor_;
    Real kvaTheirPdFloor_;
    Real kvaOurCvaRiskWeight_;
    Real kvaTheirCvaRiskWeight_;
};
} // namespace analytics
} // namespace ore


/* -*- c++ -*- */
/*
 * Copyright 2015 Free Software Foundation, Inc.
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation; either version 3, or (at your
 * option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <ldpc_bit_flip_decoder_impl.h>
#include <math.h>
#include <boost/assign/list_of.hpp>
#include <volk/volk.h>
#include <sstream>
#include <stdio.h>
#include <vector>

namespace gr {
  namespace fec {
    namespace code {

      generic_decoder::sptr
      ldpc_bit_flip_decoder::make(const fec_mtrx_sptr mtrx_obj,
                                  unsigned int max_iter)
      {
        return generic_decoder::sptr
          (new ldpc_bit_flip_decoder_impl(mtrx_obj, max_iter));
      }

      ldpc_bit_flip_decoder_impl::ldpc_bit_flip_decoder_impl(const fec_mtrx_sptr mtrx_obj,
                                                             unsigned int max_iter)
        : generic_decoder("ldpc_bit_flip_decoder")
      {
        // FEC matrix object to use for decoding
        d_mtrx = mtrx_obj;

        d_rate = static_cast<double>(d_mtrx->k())/static_cast<double>(d_mtrx->n());

        // Set frame size to k, the # of bits in the information word
        // All buffers and settings will be based on this value.
        set_frame_size(d_mtrx->k());
        // Maximum number of iterations in the decoding algorithm
        d_max_iterations = max_iter;
      }

      ldpc_bit_flip_decoder_impl::~ldpc_bit_flip_decoder_impl()
      {
      }

      int
      ldpc_bit_flip_decoder_impl::get_output_size()
      {
        return d_output_size;
      }

      int
      ldpc_bit_flip_decoder_impl::get_input_size()
      {
        return d_input_size;
      }

      bool
      ldpc_bit_flip_decoder_impl::set_frame_size(unsigned int frame_size)
      {
        if(frame_size % d_mtrx->k() != 0) {
          GR_LOG_ERROR(d_logger, boost::format("Frame size (%1% bits) must be a "
                                               "multiple of the information word "
                                               "size of the LDPC matrix, %2%") \
                       % frame_size % (d_mtrx->k()));
          throw std::runtime_error("ldpc_bit_flip_decoder: cannot use frame size.");
        }

        d_output_size = frame_size;
        d_input_size = static_cast<int>(round(frame_size / d_rate));

        return true;
      }

      double
      ldpc_bit_flip_decoder_impl::rate()
      {
        return d_rate;
      }

      void
      ldpc_bit_flip_decoder_impl::generic_work(void *inbuffer,
                                               void *outbuffer)
      {
        // Populate the information word
        const float *in = (const float*)inbuffer;
        unsigned char *out = (unsigned char*) outbuffer;

        int j = 0;
        for(int i = 0; i < d_input_size; i+=d_mtrx->n()) {
          d_mtrx->decode(&out[j], &in[i], d_mtrx->k(), d_max_iterations);
          j += d_mtrx->k();
        }

      } /* ldpc_bit_flip_decoder_impl::generic_work() */

    } /* namespace code */
  } /* namespace fec */
} /* namespace gr */


//          Copyright Gavin Band 2008 - 2012.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#include <map>
#include <string>
#include <boost/bimap.hpp>
#include <boost/foreach.hpp>
#define foreach BOOST_FOREACH
#include <Eigen/Core>

#include "genfile/VariantIdentifyingData.hpp"
#include "genfile/VariantEntry.hpp"
#include "components/SNPSummaryComponent/PairwiseCallComparerManager.hpp"
#include "components/SNPSummaryComponent/FrequentistTestCallMerger.hpp"

//#define CONSENSUS_CALLER_DEBUG 1

FrequentistTestCallMerger::SharedPtr FrequentistTestCallMerger::create_shared( std::string const& comparison_method, double threshhold ) {
	return SharedPtr( new FrequentistTestCallMerger( comparison_method, threshhold )) ;
}

FrequentistTestCallMerger::FrequentistTestCallMerger( std::string const& comparison_method, double threshhold ):
	m_comparison_method( comparison_method ),
	m_pvalue_range( std::make_pair( threshhold, 100.0 ))
{}

std::string FrequentistTestCallMerger::get_spec() const {
	return m_spec ;
}

std::string FrequentistTestCallMerger::get_result_as_string() const {
	std::string result ;
	foreach( std::string const& call, m_concordant_calls ) {
		if( result.size() > 0 ) {
			result += "," ;
		}
		result += call ;
	}
	return result ;
}

void FrequentistTestCallMerger::begin_comparisons( genfile::VariantIdentifyingData const& snp ) {
	m_snp = snp ;
	m_comparison_values.clear() ;
	m_concordant_calls.clear() ;
}

void FrequentistTestCallMerger::set_result(
	std::string const& callset1,
	std::string const& callset2,
	std::string const& comparison_method,
	std::string const& comparison_variable,
	genfile::VariantEntry const& value
) {
	if( comparison_method == m_comparison_method && comparison_variable == "pvalue" ) {
		m_comparison_values[ std::make_pair( callset1, callset2 ) ] = value ;
	}
}

void FrequentistTestCallMerger::end_comparisons() {
	// Divide the calls into groups of calls all having p-value greater than the threshhold.
	// We assume comparisons are transitive: in reality this isn't the case but it's simpler to
	// assume transitivity here.

	typedef boost::bimap< std::string, int > CallNames ;
	CallNames call_names ;
	{
		int index = 0 ;
		foreach( ComparisonValues::value_type const& key, m_comparison_values ) {
			if( call_names.left.find( key.first.first ) == call_names.left.end() ) {
					call_names.left.insert( std::make_pair( key.first.first, index++ ) );
			}
			if( call_names.left.find( key.first.second ) == call_names.left.end() ) {
				call_names.left.insert( std::make_pair( key.first.second, index++ ) );
			}
		}
	}

	Eigen::MatrixXd mismatches = Eigen::MatrixXd::Zero( call_names.size(), call_names.size() ) ;
	foreach( ComparisonValues::value_type value, m_comparison_values ) {
		double pvalue = value.second.as< double >() ;
		if( pvalue < m_pvalue_range.first || pvalue > m_pvalue_range.second ) {
			std::size_t const i = call_names.left.at( value.first.first ) ;
			std::size_t const j = call_names.left.at( value.first.second ) ;
			mismatches( i, j )++ ;
			mismatches( j, i )++ ;
		}
	}

#if CONSENSUS_CALLER_DEBUG
	std::cerr << std::resetiosflags( std::ios::floatfield ) ;
	std::cerr << "############################\n" ;
	std::cerr << "SNP: " << m_snp << ":\n" ;

	foreach( ComparisonValues::value_type value, m_comparison_values ) {
		std::cerr << value.first.first << " / " << value.first.second << ": P-value = " << value.second << ".\n" ;
	}

	std::cerr << " mismatches are:\n" ;
	typedef boost::bimap< std::string, int >::right_map::value_type V ;
	foreach( V const& value, call_names.right ) {
		std::cerr << value.first << ":" << value.second << " " ;
	}
	std::cerr << "\n" ;
	std::cerr << mismatches << "\n";
	std::cerr << "\n" ;
#endif

	for( CallNames::left_map::const_iterator i = call_names.left.begin(); i != call_names.left.end(); ++i ) {
		m_concordant_calls.insert( i->first ) ;
	}

	// At a really good SNP there are no mismatches (=> all rows are zero.)
	// At a quite good SNP we will have a (N-1)x(N-1) sub-table of zeroes.
	if( mismatches.array().maxCoeff() > 0 ) {
		// try knocking out one call.
		for( int i = 0; i < mismatches.rows(); ++i ) {
			Eigen::RowVectorXd saved_row = mismatches.row(i) ;
			Eigen::VectorXd saved_col = mismatches.col(i) ;
			mismatches.row(i).setZero() ;
			mismatches.col(i).setZero() ;
			if( mismatches.array().maxCoeff() == 0 ) {
				m_concordant_calls.erase( call_names.right.at( i ) ) ;
				mismatches.col(i) = saved_col ;
				mismatches.row(i) = saved_row ;
				break ;
			}
			mismatches.col(i) = saved_col ;
			mismatches.row(i) = saved_row ;
		}
		if( m_concordant_calls.size() == call_names.size() ) {
			m_concordant_calls.clear() ;
		}
	}

#if CONSENSUS_CALLER_DEBUG
	std::cerr << "Consensus calls are: " ;
	std::copy( m_concordant_calls.begin(), m_concordant_calls.end(), std::ostream_iterator< std::string >( std::cerr, " " ) ) ;
	std::cerr << ".\n" ;
#endif

	if( m_concordant_calls.size() == 1 ) {
		m_concordant_calls.clear() ;
	}
}


#ifdef _MSC_VER
#include <boost/math/tr1.hpp>
using namespace boost::math::tr1;
#else
#include <math.h>
#endif

#include "FstAcoustics.hh"

FstAcoustics::FstAcoustics(const char *hmm_fname, const char *dur_fname):
  m_frame(0), m_acoustics(nullptr), m_hmm_reader(nullptr)
{
  if (hmm_fname != nullptr) {
    hmm_read(hmm_fname);
  }

  if (dur_fname != nullptr) {
    duration_read(dur_fname);
  }
}

FstAcoustics::~FstAcoustics() {
  if (m_hmm_reader) delete m_hmm_reader;
}

// This is a direct copy from Toolbox.cc. FIXME: Code duplication
void
FstAcoustics::hmm_read(const char *file)
{
  std::ifstream in(file);
  if (!in)
    throw OpenError();
  if (m_hmm_reader) {
    delete m_hmm_reader;
  }
  m_hmm_reader = new NowayHmmReader();
  m_hmm_reader->read(in);
}

// This is a direct copy from Toolbox.cc. FIXME: Code duplication
void FstAcoustics::lna_open(const char *file, int size)
{
  m_lna_reader.open_file(file, size);
  m_acoustics = &m_lna_reader;
}

// This is a direct copy from Toolbox.cc. FIXME: Code duplication
void FstAcoustics::lna_open_fd(const int fd, int size)
{
  m_lna_reader.open_fd(fd, size);
  m_acoustics = &m_lna_reader;
}

// This is a direct copy from Toolbox.cc. FIXME: Code duplication
void FstAcoustics::lna_close()
{
  m_lna_reader.close();
}

void FstAcoustics::duration_read(const char *fname, std::vector<float> *a_table_ptr, std::vector<float> *b_table_ptr) {
  std::ifstream dur_in(fname);
  if (!dur_in)
    throw OpenError();

  int version;
  float a,b;

  std::vector<float> &a_table = a_table_ptr ? *a_table_ptr : m_a_table;
  std::vector<float> &b_table = b_table_ptr ? *b_table_ptr : m_b_table;

  dur_in >> version;
  if (version!=4)
    throw InvalidFormat();

  int num_states, state_id;
  dur_in >> num_states;
  a_table.resize(num_states);
  b_table.resize(num_states);

  for (int i=0; i<num_states; i++) {
    dur_in >> state_id;
    if (state_id != i) {
      throw InvalidFormat();
    }
    dur_in >> a >> b;
    a_table.push_back(a);
    b_table.push_back(b);
  }
}

float FstAcoustics::duration_logprob(int emission_pdf_idx, int duration) {
  //fprintf(stderr, "Request dur for %d (%d)\n", emission_pdf_idx, duration);
  float a = m_a_table[emission_pdf_idx];
  if (a<=0) {
    return 0.0f;
  }

  float b = m_b_table[emission_pdf_idx];
  float const_term = -a*logf(b)-lgammaf(a);
  return (a-1)*logf(duration)-duration/b+const_term;
}


/*
 * GridMapCvProcessing.cpp
 *
 *  Created on: Apr 15, 2016
 *      Author: Péter Fankhauser, Magnus Gärtner
 *	 Institute: ETH Zurich, ANYbotics
 */

#include "grid_map_cv/GridMapCvProcessing.hpp"
#include "grid_map_cv/GridMapCvConverter.hpp"

#include <Eigen/Geometry>
#include <opencv2/core/eigen.hpp>

namespace grid_map {

GridMapCvProcessing::GridMapCvProcessing() {}

GridMapCvProcessing::~GridMapCvProcessing() {}

bool GridMapCvProcessing::changeResolution(const GridMap& gridMapSource, GridMap& gridMapResult, const double resolution,
                                           const int interpolationAlgorithm) {
  GridMap gridMapSourceCopy(gridMapSource);
  gridMapSourceCopy.convertToDefaultStartIndex();
  const double sizeFactor = gridMapSourceCopy.getResolution() / resolution;
  bool firstLayer = true;
  for (const auto& layer : gridMapSourceCopy.getLayers()) {
    cv::Mat imageSource, imageResult;
    const float minValue = gridMapSourceCopy.get(layer).minCoeffOfFinites();
    const float maxValue = gridMapSourceCopy.get(layer).maxCoeffOfFinites();
    const bool hasNaN = gridMapSourceCopy.get(layer).hasNaN();
    bool result;
    if (hasNaN) {
      result = GridMapCvConverter::toImage<unsigned short, 4>(gridMapSourceCopy, layer, CV_16UC4, minValue, maxValue, imageSource);
    } else {
      result = GridMapCvConverter::toImage<unsigned short, 1>(gridMapSourceCopy, layer, CV_16UC1, minValue, maxValue, imageSource);
    }
    if (!result) return false;
    cv::resize(imageSource, imageResult, cv::Size(0.0, 0.0), sizeFactor, sizeFactor, interpolationAlgorithm);
    if (firstLayer) {
      if (!GridMapCvConverter::initializeFromImage(imageResult, resolution, gridMapResult, gridMapSourceCopy.getPosition())) return false;
      firstLayer = false;
    }
    if (hasNaN) {
      result = GridMapCvConverter::addLayerFromImage<unsigned short, 4>(imageResult, layer, gridMapResult, minValue, maxValue);
    } else {
      result = GridMapCvConverter::addLayerFromImage<unsigned short, 1>(imageResult, layer, gridMapResult, minValue, maxValue);
    }
    if (!result) return false;
  }
  gridMapResult.setFrameId(gridMapSourceCopy.getFrameId());
  gridMapResult.setTimestamp(gridMapSourceCopy.getTimestamp());
  gridMapResult.setBasicLayers(gridMapSourceCopy.getBasicLayers());
  return true;
}

GridMap GridMapCvProcessing::getTransformedMap(GridMap&& gridMapSource, const Eigen::Isometry3d& transform,
                                               const std::string& heightLayerName, const std::string& newFrameId) {
  // Check if height layer is valid.
  if (!gridMapSource.exists(heightLayerName)) {
    throw std::out_of_range("GridMap::getTransformedMap(...) : No map layer '" + heightLayerName + "' available.");
  }

  // Check if transformation is z aligned.
  if (std::abs(transform(2, 2) - 1) >= 0.05) {
    throw std::invalid_argument("The given transform is not Z aligned!");
  }

  auto yawPitchRoll = transform.rotation().eulerAngles(2, 1, 0);  // Double check convention!
  double rotationAngle = yawPitchRoll.x() * 180 / CV_PI;
  if (std::abs(yawPitchRoll.y()) >= 3 && std::abs(yawPitchRoll.z()) >= 3) {  // Resolve yaw ambiguity in euler angles.
    rotationAngle += 180;
  }

  gridMapSource.convertToDefaultStartIndex();

  // Create the rotated gridMap.
  GridMap transformedMap(gridMapSource.getLayers());
  transformedMap.setBasicLayers(gridMapSource.getBasicLayers());
  transformedMap.setTimestamp(gridMapSource.getTimestamp());
  transformedMap.setFrameId(newFrameId);

  // openCV rotation parameters, initalized on first layer.
  cv::Mat imageRotationMatrix;
  cv::Rect2f boundingBox;

  bool firstLayer = true;
  for (const auto& layer : gridMapSource.getLayers()) {
    cv::Mat imageSource, imageResult;

    // From gridMap to openCV image. Assumes defaultStartIndex.
    cv::eigen2cv(gridMapSource[layer], imageSource);

    // Calculate transformation matrix and update geometry of the resulting grid map.
    if (firstLayer) {
      // Get rotation matrix for rotating the image around its center in pixel coordinates. See
      // https://answers.opencv.org/question/82708/rotation-center-confusion/
      cv::Point2f imageCenter =
          cv::Point2f((imageSource.cols - 1) / 2.0, (imageSource.rows - 1) / 2.0);

      imageRotationMatrix = cv::getRotationMatrix2D(imageCenter, rotationAngle, 1.0);
      boundingBox = cv::RotatedRect(cv::Point2f(0, 0), imageSource.size(), rotationAngle).boundingRect2f();

      // Adjust transformation matrix. See
      // https://docs.opencv.org/3.4/da/d54/group__imgproc__transform.html#gafbbc470ce83812914a70abfb604f4326 and https://stackoverflow.com/questions/22041699/rotate-an-image-without-cropping-in-opencv-in-c
      imageRotationMatrix.at<double>(0, 2) += boundingBox.width / 2.0 - imageSource.cols / 2.0;
      imageRotationMatrix.at<double>(1, 2) += boundingBox.height / 2.0 - imageSource.rows / 2.0;

      // Calculate the new center of the gridMap.
      Position3 newCenter = transform * Position3{(gridMapSource.getPosition().x()), (gridMapSource.getPosition().y()), 0.0};

      // Set the size of the rotated gridMap.
      transformedMap.setGeometry({boundingBox.height * gridMapSource.getResolution(), boundingBox.width * gridMapSource.getResolution()},
                         gridMapSource.getResolution(), Position(newCenter.x(), newCenter.y()));
      firstLayer = false;
    }

    // Rotate the layer.
    imageResult = cv::Mat(boundingBox.size(), CV_32F, std::numeric_limits<double>::quiet_NaN());
    cv::warpAffine(imageSource, imageResult, imageRotationMatrix, boundingBox.size(), cv::INTER_NEAREST, cv::BORDER_TRANSPARENT);

    // Copy result into gridMapLayer. Assumes default start index.
    Matrix resultLayer;
    cv::cv2eigen(imageResult, resultLayer);
    transformedMap.add(layer, resultLayer);
  }

  // Add height translation.
  grid_map::Matrix heightLayer = transformedMap[heightLayerName];
  transformedMap[heightLayerName] = heightLayer.array() + transform.translation().z();

  return transformedMap;
}
}  // namespace grid_map


// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 2018 Oracle and/or its affiliates.
// Contributed and/or modified by Vissarion Fisikopoulos, on behalf of Oracle

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

#ifndef BOOST_GEOMETRY_STRATEGIES_CARTESIAN_DISTANCE_SEGMENT_BOX_HPP
#define BOOST_GEOMETRY_STRATEGIES_CARTESIAN_DISTANCE_SEGMENT_BOX_HPP

#include <boost/geometry/algorithms/detail/distance/segment_to_box.hpp>

namespace boost { namespace geometry
{

namespace strategy { namespace distance
{

template
<
    typename CalculationType = void,
    typename Strategy = pythagoras<CalculationType>
>
struct cartesian_segment_box
{
    template <typename PointOfSegment, typename PointOfBox>
    struct calculation_type
        : promote_floating_point
          <
              typename strategy::distance::services::return_type
                  <
                      Strategy,
                      PointOfSegment,
                      PointOfBox
                  >::type
          >
    {};

    // point-point strategy getters
    struct distance_pp_strategy
    {
        typedef Strategy type;
    };

    inline typename distance_pp_strategy::type get_distance_pp_strategy() const
    {
        return typename distance_pp_strategy::type();
    }
    // point-segment strategy getters
    struct distance_ps_strategy
    {
        typedef projected_point<CalculationType, Strategy> type;
    };

    inline typename distance_ps_strategy::type get_distance_ps_strategy() const
    {
        return typename distance_ps_strategy::type();
    }

    template <typename LessEqual, typename ReturnType,
              typename SegmentPoint, typename BoxPoint>
    inline ReturnType segment_below_of_box(SegmentPoint const& p0,
                                   SegmentPoint const& p1,
                                   BoxPoint const&,
                                   BoxPoint const&,
                                   BoxPoint const&,
                                   BoxPoint const& bottom_right) const
    {

        return geometry::detail::distance::segment_to_box_2D
            <
                ReturnType,
                SegmentPoint,
                BoxPoint,
                cartesian_segment_box<CalculationType, Strategy>
            >::template call_above_of_box
                <
                    typename LessEqual::other
                >(p1, p0, bottom_right, *this);
    }

    template <typename SPoint, typename BPoint>
    static void mirror(SPoint&,
                       SPoint&,
                       BPoint&,
                       BPoint&,
                       BPoint&,
                       BPoint&)
    {}
};

#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
namespace services
{

template <typename CalculationType, typename Strategy>
struct tag<cartesian_segment_box<CalculationType, Strategy> >
{
    typedef strategy_tag_distance_segment_box type;
};

template <typename CalculationType, typename Strategy, typename PS, typename PB>
struct return_type<cartesian_segment_box<CalculationType, Strategy>, PS, PB>
    : cartesian_segment_box<CalculationType, Strategy>::template calculation_type<PS, PB>
{};

template <typename CalculationType, typename Strategy>
struct comparable_type<cartesian_segment_box<CalculationType, Strategy> >
{
    // Define a cartesian_segment_box strategy with its underlying point-point
    // strategy being comparable
    typedef cartesian_segment_box
        <
            CalculationType,
            typename comparable_type<Strategy>::type
        > type;
};

template <typename CalculationType, typename Strategy>
struct get_comparable<cartesian_segment_box<CalculationType, Strategy> >
{
    typedef typename comparable_type
        <
            cartesian_segment_box<CalculationType, Strategy>
        >::type comparable_type;
public :
    static inline comparable_type apply(cartesian_segment_box<CalculationType, Strategy> const& )
    {
        return comparable_type();
    }
};

template <typename CalculationType, typename Strategy, typename PS, typename PB>
struct result_from_distance<cartesian_segment_box<CalculationType, Strategy>, PS, PB>
{
private :
    typedef typename return_type<
                                    cartesian_segment_box
                                    <
                                        CalculationType,
                                        Strategy
                                    >,
                                    PS,
                                    PB
                                 >::type return_type;
public :
    template <typename T>
    static inline return_type apply(cartesian_segment_box<CalculationType,
                                                          Strategy> const& ,
                                    T const& value)
    {
        Strategy s;
        return result_from_distance<Strategy, PS, PB>::apply(s, value);
    }
};

template <typename Segment, typename Box>
struct default_strategy
    <
        segment_tag, box_tag, Segment, Box,
        cartesian_tag, cartesian_tag
    >
{
    typedef cartesian_segment_box<> type;
};

template <typename Box, typename Segment>
struct default_strategy
    <
        box_tag, segment_tag, Box, Segment,
        cartesian_tag, cartesian_tag
    >
{
    typedef typename default_strategy
        <
            segment_tag, box_tag, Segment, Box,
            cartesian_tag, cartesian_tag
        >::type type;
};

}
#endif

}} // namespace strategy::distance

}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_STRATEGIES_CARTESIAN_DISTANCE_SEGMENT_BOX_HPP