/*
 *
 * Copyright (c) Kresimir Fresl 2002
 *
 * Permission to copy, modify, use and distribute this software
 * for any non-commercial or commercial purpose is granted provided
 * that this license appear on all copies of the software source code.
 *
 * Author assumes no responsibility whatsoever for its use and makes
 * no guarantees about its quality, correctness or reliability.
 *
 * Author acknowledges the support of the Faculty of Civil Engineering,
 * University of Zagreb, Croatia.
 *
 */

#ifndef BOOST_NUMERIC_BINDINGS_CBLAS3_OVERLOADS_HPP
#define BOOST_NUMERIC_BINDINGS_CBLAS3_OVERLOADS_HPP

#include <complex>
#include <boost/numeric/bindings/atlas/cblas_inc.hpp>
#include <boost/numeric/bindings/traits/type.hpp>

namespace boost { namespace numeric { namespace bindings {

  namespace atlas { namespace detail {

    // C <- alpha * op (A) * op (B) + beta * C

    inline
    void gemm (CBLAS_ORDER const Order,
               CBLAS_TRANSPOSE const TransA, CBLAS_TRANSPOSE const TransB,
               int const M, int const N, int const K,
               float const alpha, float const* A, int const lda,
               float const* B, int const ldb,
               float const beta, float* C, int const ldc)
    {
      cblas_sgemm (Order, TransA, TransB, M, N, K,
                   alpha, A, lda,
                   B, ldb,
                   beta, C, ldc);
    }

    inline
    void gemm (CBLAS_ORDER const Order,
               CBLAS_TRANSPOSE const TransA, CBLAS_TRANSPOSE const TransB,
               int const M, int const N, int const K,
               double const alpha, double const* A, int const lda,
               double const* B, int const ldb,
               double const beta, double* C, int const ldc)
    {
      cblas_dgemm (Order, TransA, TransB, M, N, K,
                   alpha, A, lda,
                   B, ldb,
                   beta, C, ldc);
    }

    inline
    void gemm (CBLAS_ORDER const Order,
               CBLAS_TRANSPOSE const TransA, CBLAS_TRANSPOSE const TransB,
               int const M, int const N, int const K,
               traits::complex_f const& alpha,
               traits::complex_f const* A, int const lda,
               traits::complex_f const* B, int const ldb,
               traits::complex_f const& beta,
               traits::complex_f* C, int const ldc)
    {
      cblas_cgemm (Order, TransA, TransB, M, N, K,
                   static_cast<void const*> (&alpha),
                   static_cast<void const*> (A), lda,
                   static_cast<void const*> (B), ldb,
                   static_cast<void const*> (&beta),
                   static_cast<void*> (C), ldc);
    }

    inline
    void gemm (CBLAS_ORDER const Order,
               CBLAS_TRANSPOSE const TransA, CBLAS_TRANSPOSE const TransB,
               int const M, int const N, int const K,
               traits::complex_d const& alpha,
               traits::complex_d const* A, int const lda,
               traits::complex_d const* B, int const ldb,
               traits::complex_d const& beta,
               traits::complex_d* C, int const ldc)
    {
      cblas_zgemm (Order, TransA, TransB, M, N, K,
                   static_cast<void const*> (&alpha),
                   static_cast<void const*> (A), lda,
                   static_cast<void const*> (B), ldb,
                   static_cast<void const*> (&beta),
                   static_cast<void*> (C), ldc);
    }

    // C <- alpha * A * B + beta * C
    // C <- alpha * B * A + beta * C
    // A == A^T

    inline
    void symm (CBLAS_ORDER const Order, CBLAS_SIDE const Side,
               CBLAS_UPLO const Uplo, int const M, int const N,
               float const alpha, float const* A, int const lda,
               float const* B, int const ldb,
               float const beta, float* C, int const ldc)
    {
      cblas_ssymm (Order, Side, Uplo, M, N,
                   alpha, A, lda,
                   B, ldb,
                   beta, C, ldc);
    }

    inline
    void symm (CBLAS_ORDER const Order, CBLAS_SIDE const Side,
               CBLAS_UPLO const Uplo, int const M, int const N,
               double const alpha, double const* A, int const lda,
               double const* B, int const ldb,
               double const beta, double* C, int const ldc)
    {
      cblas_dsymm (Order, Side, Uplo, M, N,
                   alpha, A, lda,
                   B, ldb,
                   beta, C, ldc);
    }

    inline
    void symm (CBLAS_ORDER const Order, CBLAS_SIDE const Side,
               CBLAS_UPLO const Uplo, int const M, int const N,
               traits::complex_f const& alpha,
               traits::complex_f const* A, int const lda,
               traits::complex_f const* B, int const ldb,
               traits::complex_f const& beta,
               traits::complex_f* C, int const ldc)
    {
      cblas_csymm (Order, Side, Uplo, M, N,
                   static_cast<void const*> (&alpha),
                   static_cast<void const*> (A), lda,
                   static_cast<void const*> (B), ldb,
                   static_cast<void const*> (&beta),
                   static_cast<void*> (C), ldc);
    }

    inline
    void symm (CBLAS_ORDER const Order, CBLAS_SIDE const Side,
               CBLAS_UPLO const Uplo, int const M, int const N,
               traits::complex_d const& alpha,
               traits::complex_d const* A, int const lda,
               traits::complex_d const* B, int const ldb,
               traits::complex_d const& beta,
               traits::complex_d* C, int const ldc)
    {
      cblas_zsymm (Order, Side, Uplo, M, N,
                   static_cast<void const*> (&alpha),
                   static_cast<void const*> (A), lda,
                   static_cast<void const*> (B), ldb,
                   static_cast<void const*> (&beta),
                   static_cast<void*> (C), ldc);
    }

    // C <- alpha * A * B + beta * C
    // C <- alpha * B * A + beta * C
    // A == A^H

    inline
    void hemm (CBLAS_ORDER const Order, CBLAS_SIDE const Side,
               CBLAS_UPLO const Uplo, int const M, int const N,
               traits::complex_f const& alpha,
               traits::complex_f const* A, int const lda,
               traits::complex_f const* B, int const ldb,
               traits::complex_f const& beta,
               traits::complex_f* C, int const ldc)
    {
      cblas_chemm (Order, Side, Uplo, M, N,
                   static_cast<void const*> (&alpha),
                   static_cast<void const*> (A), lda,
                   static_cast<void const*> (B), ldb,
                   static_cast<void const*> (&beta),
                   static_cast<void*> (C), ldc);
    }

    inline
    void hemm (CBLAS_ORDER const Order, CBLAS_SIDE const Side,
               CBLAS_UPLO const Uplo, int const M, int const N,
               traits::complex_d const& alpha,
               traits::complex_d const* A, int const lda,
               traits::complex_d const* B, int const ldb,
               traits::complex_d const& beta,
               traits::complex_d* C, int const ldc)
    {
      cblas_zhemm (Order, Side, Uplo, M, N,
                   static_cast<void const*> (&alpha),
                   static_cast<void const*> (A), lda,
                   static_cast<void const*> (B), ldb,
                   static_cast<void const*> (&beta),
                   static_cast<void*> (C), ldc);
    }

    // C <- alpha * A * A^T + beta * C
    // C <- alpha * A^T * A + beta * C
    // C == C^T

    inline
    void syrk (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
               CBLAS_TRANSPOSE const Trans, int const N, int const K,
               float const alpha, float const* A, int const lda,
               float const beta, float* C, int const ldc)
    {
      cblas_ssyrk (Order, Uplo, Trans, N, K, alpha, A, lda, beta, C, ldc);
    }

    inline
    void syrk (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
               CBLAS_TRANSPOSE const Trans, int const N, int const K,
               double const alpha, double const* A, int const lda,
               double const beta, double* C, int const ldc)
    {
      cblas_dsyrk (Order, Uplo, Trans, N, K, alpha, A, lda, beta, C, ldc);
    }

    inline
    void syrk (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
               CBLAS_TRANSPOSE const Trans, int const N, int const K,
               traits::complex_f const& alpha,
               traits::complex_f const* A, int const lda,
               traits::complex_f const& beta,
               traits::complex_f* C, int const ldc)
    {
      cblas_csyrk (Order, Uplo, Trans, N, K,
                   static_cast<void const*> (&alpha),
                   static_cast<void const*> (A), lda,
                   static_cast<void const*> (&beta),
                   static_cast<void*> (C), ldc);
    }

    inline
    void syrk (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
               CBLAS_TRANSPOSE const Trans, int const N, int const K,
               traits::complex_d const& alpha,
               traits::complex_d const* A, int const lda,
               traits::complex_d const& beta,
               traits::complex_d* C, int const ldc)
    {
      cblas_zsyrk (Order, Uplo, Trans, N, K,
                   static_cast<void const*> (&alpha),
                   static_cast<void const*> (A), lda,
                   static_cast<void const*> (&beta),
                   static_cast<void*> (C), ldc);
    }

    // C <- alpha * A * B^T + conj(alpha) * B * A^T + beta * C
    // C <- alpha * A^T * B + conj(alpha) * B^T * A + beta * C
    // C == C^T

    inline
    void syr2k (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
                CBLAS_TRANSPOSE const Trans, int const N, int const K,
                float const alpha, float const* A, int const lda,
                float const* B, int const ldb,
                float const beta, float* C, int const ldc)
    {
      cblas_ssyr2k (Order, Uplo, Trans, N, K,
                    alpha, A, lda, B, ldb, beta, C, ldc);
    }

    inline
    void syr2k (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
                CBLAS_TRANSPOSE const Trans, int const N, int const K,
                double const alpha, double const* A, int const lda,
                double const* B, int const ldb,
                double const beta, double* C, int const ldc)
    {
      cblas_dsyr2k (Order, Uplo, Trans, N, K,
                    alpha, A, lda, B, ldb, beta, C, ldc);
    }

    inline
    void syr2k (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
                CBLAS_TRANSPOSE const Trans, int const N, int const K,
                traits::complex_f const& alpha,
                traits::complex_f const* A, int const lda,
                traits::complex_f const* B, int const ldb,
                traits::complex_f const& beta,
                traits::complex_f* C, int const ldc)
    {
      cblas_csyr2k (Order, Uplo, Trans, N, K,
                    static_cast<void const*> (&alpha),
                    static_cast<void const*> (A), lda,
                    static_cast<void const*> (B), ldb,
                    static_cast<void const*> (&beta),
                    static_cast<void*> (C), ldc);
    }

    inline
    void syr2k (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
                CBLAS_TRANSPOSE const Trans, int const N, int const K,
                traits::complex_d const& alpha,
                traits::complex_d const* A, int const lda,
                traits::complex_d const* B, int const ldb,
                traits::complex_d const& beta,
                traits::complex_d* C, int const ldc)
    {
      cblas_zsyr2k (Order, Uplo, Trans, N, K,
                    static_cast<void const*> (&alpha),
                    static_cast<void const*> (A), lda,
                    static_cast<void const*> (B), ldb,
                    static_cast<void const*> (&beta),
                    static_cast<void*> (C), ldc);
    }

    // C <- alpha * A * A^H + beta * C
    // C <- alpha * A^H * A + beta * C
    // C == C^H

    inline
    void herk (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
               CBLAS_TRANSPOSE const Trans, int const N, int const K,
               float alpha, traits::complex_f const* A, int const lda,
               float beta, traits::complex_f* C, int const ldc)
    {
      cblas_cherk (Order, Uplo, Trans, N, K,
                   alpha, static_cast<void const*> (A), lda,
                   beta, static_cast<void*> (C), ldc);
    }

    inline
    void herk (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
               CBLAS_TRANSPOSE const Trans, int const N, int const K,
               double alpha, traits::complex_d const* A, int const lda,
               double beta, traits::complex_d* C, int const ldc)
    {
      cblas_zherk (Order, Uplo, Trans, N, K,
                   alpha, static_cast<void const*> (A), lda,
                   beta, static_cast<void*> (C), ldc);
    }

    // C <- alpha * A * B^H + conj(alpha) * B * A^H + beta * C
    // C <- alpha * A^H * B + conj(alpha) * B^H * A + beta * C
    // C == C^H

    inline
    void her2k (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
                CBLAS_TRANSPOSE const Trans, int const N, int const K,
                traits::complex_f const& alpha,
                traits::complex_f const* A, int const lda,
                traits::complex_f const* B, int const ldb,
                float beta, traits::complex_f* C, int const ldc)
    {
      cblas_cher2k (Order, Uplo, Trans, N, K,
                    static_cast<void const*> (&alpha),
                    static_cast<void const*> (A), lda,
                    static_cast<void const*> (B), ldb,
                    beta, static_cast<void*> (C), ldc);
    }

    inline
    void her2k (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
                CBLAS_TRANSPOSE const Trans, int const N, int const K,
                traits::complex_d const& alpha,
                traits::complex_d const* A, int const lda,
                traits::complex_d const* B, int const ldb,
                double beta, traits::complex_d* C, int const ldc)
    {
      cblas_zher2k (Order, Uplo, Trans, N, K,
                    static_cast<void const*> (&alpha),
                    static_cast<void const*> (A), lda,
                    static_cast<void const*> (B), ldb,
                    beta, static_cast<void*> (C), ldc);
    }

  }} // namepaces detail & atlas

}}}

#endif // BOOST_NUMERIC_BINDINGS_CBLAS3_OVERLOADS_HPP


/*
 * van_der_pol_stiff.cpp
 *
 * Created on: Dec 12, 2011
 *
 * Copyright 2011 Rajeev Singh
 *
 * 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 <iostream>
#include <fstream>
#include <utility>

#include <boost/numeric/odeint.hpp>

#include <boost/phoenix/core.hpp>
#include <boost/phoenix/operator.hpp>

using namespace std;
using namespace boost::numeric::odeint;
namespace phoenix = boost::phoenix;

const double mu = 1000.0;

typedef boost::numeric::ublas::vector< double > vector_type;
typedef boost::numeric::ublas::matrix< double > matrix_type;

struct vdp_stiff
{
    void operator()( const vector_type &x , vector_type &dxdt , double t )
    {
        dxdt[0] = x[1];
        dxdt[1] = -x[0] - mu * x[1] * (x[0]*x[0]-1.0);
    }
};

struct vdp_stiff_jacobi
{
    void operator()( const vector_type &x , matrix_type &J , const double &t , vector_type &dfdt )
    {
        J(0, 0) = 0.0;
        J(0, 1) = 1.0;
        J(1, 0) = -1.0 - 2.0*mu * x[0] * x[1];
        J(1, 1) = -mu * ( x[0] * x[0] - 1.0);

        dfdt[0] = 0.0;
        dfdt[1] = 0.0;
    }
};

int main( int argc , char **argv )
{
    //[ integrate_stiff_system
    vector_type x( 2 );
    /* initialize random seed: */
    srand ( time(NULL) );

    // initial conditions
    for (int i=0; i<2; i++)
        x[i] = 1.0; //(1.0 * rand()) / RAND_MAX;

    size_t num_of_steps = integrate_const( make_dense_output< rosenbrock4< double > >( 1.0e-6 , 1.0e-6 ) ,
            make_pair( vdp_stiff() , vdp_stiff_jacobi() ) ,
            x , 0.0 , 1000.0 , 1.0
            , cout << phoenix::arg_names::arg2 << " " << phoenix::arg_names::arg1[0] << " " << phoenix::arg_names::arg1[1] << "\n"
            );
    //]
    clog << num_of_steps << endl;

    //[ integrate_stiff_system_alternative

    vector_type x2( 2 );
    // initial conditions
    for (int i=0; i<2; i++)
        x2[i] = 1.0; //(1.0 * rand()) / RAND_MAX;

    size_t num_of_steps2 = integrate_const( make_dense_output< runge_kutta_dopri5< vector_type > >( 1.0e-6 , 1.0e-6 ) ,
            vdp_stiff() , x2 , 0.0 , 1000.0 , 1.0
            , cout << phoenix::arg_names::arg2 << " " << phoenix::arg_names::arg1[0] << " " << phoenix::arg_names::arg1[1] << "\n"
            );
    //]
    clog << num_of_steps2 << endl;

    return 0;
}


#include <string>
#include <algorithm>
#include <vector>
#include <array>
#include <memory>
#include <map>
#include <cassert>
#include <fstream>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_real_distribution.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>
#include "DatasetAR.h"
#include "GaussSeq.h"
#include "ZeroSeq.h"
#include "utils.h"
using utils::my_float;
using boost::random::uniform_real_distribution;

/*
 *  Member variables
 */
boost::random::mt19937 DatasetAR::gen {};

/*
 *  Helper functions
 *
 *  All "noise setters" require the target sequence be defined.
 */
void DatasetAR::noise_zero() {
    for (auto i = 0; i < utils::FEAT_COUNT; i++) {
        feats.push_back(std::make_unique<ZeroSeq>());
    }
    feats[utils::SALIENT_IND] = std::make_unique<ARSeq>(this->target);
}
void DatasetAR::noise_one() {
    for (auto i = 0; i < utils::FEAT_COUNT; i++) {
        feats.push_back(std::make_unique<GaussSeq>());
    }
    feats[utils::SALIENT_IND] = std::make_unique<ARSeq>(this->target);
}
void DatasetAR::noise_two() {
    uniform_real_distribution<my_float> u_mean(-10, 10);
    uniform_real_distribution<my_float> u_std(0.1, 5);

    for (auto i = 0; i < utils::FEAT_COUNT; i++) {
        feats.push_back(std::make_unique<GaussSeq>(u_mean(gen), u_std(gen)));
    }
    feats[utils::SALIENT_IND] = std::make_unique<ARSeq>(this->target);
}
void DatasetAR::noise_three() {
    uniform_real_distribution<my_float> u_const(0, 1);
    uniform_real_distribution<my_float> u_coeff(-0.9, 0.9);
    uniform_real_distribution<my_float> u_seed(-1, 1);
    std::vector<my_float> seed_vect {u_seed(gen),};
    std::map<int, my_float> coeff_map {
        {1, u_coeff(gen)},
    };

    for (auto i = 0; i < utils::FEAT_COUNT; i++) {
        // prep seq backing
        coeff_map[1] = u_coeff(gen);
        seed_vect[0] = u_seed(gen);

        ARSeq in_seq(coeff_map, u_const(gen));
        in_seq.seed_prev_vals(seed_vect);

        feats.push_back(std::make_unique<ARSeq>(in_seq));
    }
    feats[utils::SALIENT_IND] = std::make_unique<ARSeq>(this->target);
}
void DatasetAR::noise_four() {
    uniform_real_distribution<my_float> u_const(-1000000, -999999);
    uniform_real_distribution<my_float> u_coeff(1.1, 2);
    uniform_real_distribution<my_float> u_seed(0, 0.0000001);
    std::vector<my_float> seed_vect {u_seed(gen),};
    std::map<int, my_float> coeff_map {
        {1, u_coeff(gen)},
    };

    for (auto i = 0; i < utils::FEAT_COUNT; i++) {
        // prep seq backing
        coeff_map[1] = u_coeff(gen);
        seed_vect[0] = u_seed(gen);

        ARSeq in_seq(coeff_map, u_const(gen));
        in_seq.seed_prev_vals(seed_vect);

        feats.push_back(std::make_unique<ARSeq>(in_seq));
    }
    feats[utils::SALIENT_IND] = std::make_unique<ARSeq>(this->target);
}

/*
 *  Constructors and destructors
 */
DatasetAR::DatasetAR(std::string file_name, ARSeq target_seq):
    fname{file_name.append(".csv")},
    target{target_seq}
    {};

DatasetAR::DatasetAR(std::string file_name, ARSeq target_seq, unsigned char type):
    fname{file_name.append(".csv")},
    noise_type{type},
    target{target_seq} {
    set_noise(type);
}

/*
 *  Member functions
 */
void DatasetAR::set_noise(unsigned char type) {
    this->noise_type = type;

    switch (type) {
        case 0: this->noise_zero();
                break;
        case 1: this->noise_one();
                break;
        case 2: this->noise_two();
                break;
        case 3: this->noise_three();
                break;
        case 4: this->noise_four();
                break;
    }
    this->prev_targ_val = feats[utils::SALIENT_IND]->next();
}
std::array<std::array<my_float, utils::FEAT_COUNT + 1>, utils::TIME_STEP>
    DatasetAR::generate_normalized_dat() {
    namespace mp = boost::multiprecision;
    constexpr int T = utils::TIME_STEP;
    constexpr int F = utils::FEAT_COUNT;
    // observe storage[feat][time] indexing
    std::array<std::array<my_float, T + 1>, F> storage {};
    std::array<my_float, T + 1> targ_storage {};            // storage for target

    /*
     *  Prepare progress bar
     */

    // offset used to determine when to update progress bar
    int offset_mod = static_cast<int>(F / 10);
    int offset_counter {0};
    // print initial progress bar
    std::cout << "[";
    for (auto i = 0; i < 10; i++) {
        std::cout << " ";
    }
    std::cout << "]\r";

    /*
     *  Store non-normalized values
     */

    // Store time-delayed values
    // If noise type 3 or 4, then there is one previous value as a ARSeq.
    // Otherwise, simply invoke next()
    if (this->noise_type == 3 || this->noise_type == 4) {
        for (auto f = 0; f < F; f++) {
            if (f == utils::SALIENT_IND)
                targ_storage[0] = this->prev_targ_val;
            else
                storage[f][0] = dynamic_cast<ARSeq*>(feats[f].get())->get_prev_val()[0];
        }
    } else {
        for (auto f = 0; f < F; f++) {
            if (f == utils::SALIENT_IND)
                targ_storage[0] = this->prev_targ_val;
            else
                storage[f][0] = feats[f]->next();
        }
    }

    // Store current values (those non-negative)
    for (auto f = 0; f < F; f++) {
        // print progress bar
        if ((f - 1) % offset_mod == 0) {
            offset_counter++;

            std::cout << "[";
            for (auto i = 0; i < offset_counter; i++) {
                std::cout << "X";
            }
            for (auto i = 0; i < 10 - offset_counter; i++) {
                std::cout << " ";
            }
            std::cout << "]\r";
        }

        // write and record sequences
        for (int t = 1; t < T + 1; t++) {
            if (f != utils::SALIENT_IND)
                storage[f][t] = feats[f]->next();
            else {
                // target column
                this->prev_targ_val = feats[utils::SALIENT_IND]->next();
                targ_storage[t] = this->prev_targ_val;
            }
        }
    }

    /*
     *  Normalize array values according to utils::NORM_VAL via min-max norm
     */
    my_float feat_min {0};
    my_float feat_max {0};
    // observe normalized_feats[time][feat] indexing
    std::array<std::array<my_float, F + 1>, T> normalized_feats {};

    // set normalized non-targ vals
    for (auto f = 0; f < F; f++) {
        if (f != utils::SALIENT_IND) {
            // if we have zero values, no need to rescale
            if (this->noise_type == 0) {
                for (auto t = 0; t < T; t++) {
                    normalized_feats[t][f] = storage[f][t];
                }
            } else {
                feat_min = *std::min_element(storage[f].begin(), storage[f].end());
                feat_max = *std::max_element(storage[f].begin(), storage[f].end());

                for (auto t = 0; t < T; t++) {
                    if (feat_max - feat_min == 0)
                        normalized_feats[t][f] = (utils::NORM_VAL * 2 * (storage[f][t] - feat_min)) -
                            utils::NORM_VAL;
                    else
                        normalized_feats[t][f] = (utils::NORM_VAL * 2 * ((storage[f][t] - feat_min) /
                            (feat_max - feat_min))) - utils::NORM_VAL;
                }
            }
        } else {
            feat_min = *std::min_element(targ_storage.begin(), targ_storage.end());
            feat_max = *std::max_element(targ_storage.begin(), targ_storage.end());

            for (auto t = 0; t < T; t++) {
                if (feat_max - feat_min == 0) {
                    normalized_feats[t][utils::SALIENT_IND] = (utils::NORM_VAL * 2 * (targ_storage[t] - feat_min)) -
                        utils::NORM_VAL;
                    normalized_feats[t][F] = (utils::NORM_VAL * 2 * (targ_storage[t + 1] - feat_min)) -
                        utils::NORM_VAL;
                } else {
                    normalized_feats[t][utils::SALIENT_IND] = (utils::NORM_VAL * 2 * ((targ_storage[t] - feat_min) /
                        (feat_max - feat_min))) - utils::NORM_VAL;
                    normalized_feats[t][F] = (utils::NORM_VAL * 2 * ((targ_storage[t + 1] - feat_min) /
                        (feat_max - feat_min))) - utils::NORM_VAL;
                }
            }
        }
    }

    return normalized_feats;
}

void DatasetAR::write_csv() {
    // was the noise value assigned?
    assert(this->noise_type != 10);

    // storage for normalize values
    constexpr int T = utils::TIME_STEP;
    constexpr int F = utils::FEAT_COUNT;
    std::array<std::array<my_float, F + 1>, T> storage = generate_normalized_dat();

    // initialize csv
    std::ofstream synth_data {this->fname};
    synth_data << std::setprecision(25);

    // prepare labels
    for (auto i = 0; i < utils::FEAT_COUNT + 2; i++) {
        if (i == 0)
            synth_data << "Time,";
        else if (i == utils::FEAT_COUNT + 1)
            synth_data << "Target\n";
        else
            synth_data << "Feature_" << i << ",";
    }

    // write values to csv
    for (auto t = 0; t < T; t++) {
        for (auto f = 0; f < F + 2; f++) {
            // time column
            if (f == 0)
                synth_data << t << ",";
            // target column
            else if (f == utils::FEAT_COUNT + 1)
                synth_data << storage[t][f - 1] << "\n";
            // data column
            else
                synth_data << storage[t][f - 1] << ",";
        }
    }
    std::cout << "\nFinished writing " << this->fname << "!\n";
    synth_data.close();
}


//
// Created by jk on 2020/3/8.
//
// #define R_BUILD
#ifdef R_BUILD
#include <Rcpp.h>
#include <RcppEigen.h>
// [[Rcpp::depends(RcppEigen)]]
using namespace Rcpp;
#else
#include <Eigen/Eigen>
#include "List.h"
#endif
#include <algorithm>
#include <vector>
#include <iostream>

using namespace std;

void Normalize(Eigen::MatrixXd &X, Eigen::VectorXd &y, Eigen::VectorXd &weights, Eigen::VectorXd &meanx, double &meany, Eigen::VectorXd &normx)
{
    int n = X.rows();
    int p = X.cols();
    Eigen::VectorXd tmp(n);
    for (int i = 0; i < p; i++)
    {
        meanx(i) = weights.dot(X.col(i)) / double(n);
    }
    meany = (y.dot(weights)) / double(n);
    for (int i = 0; i < p; i++)
    {
        X.col(i) = X.col(i).array() - meanx(i);
    }
    y = y.array() - meany;

    for (int i = 0; i < p; i++)
    {
        tmp = X.col(i);
        tmp = tmp.array().square();
        normx(i) = sqrt(weights.dot(tmp));
    }
    for (int i = 0; i < p; i++)
    {
        X.col(i) = sqrt(double(n)) * X.col(i) / normx(i);
    }
}

void Normalize(Eigen::MatrixXd &X, Eigen::MatrixXd &y, Eigen::VectorXd &weights, Eigen::VectorXd &meanx, Eigen::VectorXd &meany, Eigen::VectorXd &normx)
{
    // cout << "multigaussian normal" << endl;
    int n = X.rows();
    int p = X.cols();
    Eigen::VectorXd tmp(n);
    for (int i = 0; i < p; i++)
    {
        meanx(i) = weights.dot(X.col(i)) / double(n);
    }
    meany = y.transpose() * weights / double(n);
    // cout << "meany: " << meany << endl;
    for (int i = 0; i < p; i++)
    {
        X.col(i) = X.col(i).array() - meanx(i);
    }

    for (int i = 0; i < n; i++)
    {
        y.row(i) = y.row(i) - meany;
    }
    // y = y.array() - meany;

    for (int i = 0; i < p; i++)
    {
        tmp = X.col(i);
        tmp = tmp.array().square();
        normx(i) = sqrt(weights.dot(tmp));
    }
    for (int i = 0; i < p; i++)
    {
        X.col(i) = sqrt(double(n)) * X.col(i) / normx(i);
    }
}

void Normalize3(Eigen::MatrixXd &X, Eigen::VectorXd &weights, Eigen::VectorXd &meanx, Eigen::VectorXd &normx)
{
    int n = X.rows();
    int p = X.cols();
    Eigen::VectorXd tmp(n);
    for (int i = 0; i < p; i++)
    {
        meanx(i) = weights.dot(X.col(i)) / double(n);
    }
    for (int i = 0; i < p; i++)
    {
        X.col(i) = X.col(i).array() - meanx(i);
    }
    for (int i = 0; i < p; i++)
    {
        tmp = X.col(i);
        tmp = tmp.array().square();
        normx(i) = sqrt(weights.dot(tmp));
    }
    for (int i = 0; i < p; i++)
    {
        X.col(i) = sqrt(double(n)) * X.col(i) / normx(i);
    }
}

void Normalize4(Eigen::MatrixXd &X, Eigen::VectorXd &weights, Eigen::VectorXd &normx)
{
    int n = X.rows();
    int p = X.cols();
    Eigen::VectorXd tmp(n);
    for (int i = 0; i < p; i++)
    {
        tmp = X.col(i);
        tmp = tmp.array().square();
        normx(i) = sqrt(weights.dot(tmp));
    }
    for (int i = 0; i < p; i++)
    {
        X.col(i) = sqrt(double(n)) * X.col(i) / normx(i);
    }
}

void Normalize(Eigen::SparseMatrix<double> &X, Eigen::VectorXd &y, Eigen::VectorXd &weights, Eigen::VectorXd &meanx, double &meany, Eigen::VectorXd &normx) { return; }
void Normalize(Eigen::SparseMatrix<double> &X, Eigen::MatrixXd &y, Eigen::VectorXd &weights, Eigen::VectorXd &meanx, Eigen::VectorXd &meany, Eigen::VectorXd &normx) { return; }
void Normalize3(Eigen::SparseMatrix<double> &X, Eigen::VectorXd &y, Eigen::VectorXd &meanx, Eigen::VectorXd &normx) { return; }
void Normalize4(Eigen::SparseMatrix<double> &X, Eigen::VectorXd &y, Eigen::VectorXd &normx) { return; }


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

#ifndef ITL_FSM_INCLUDE
#define ITL_FSM_INCLUDE

#include <boost/numeric/mtl/operation/two_norm.hpp>

namespace itl {

/// Folded spectrum method
/** Computed and named as in http://en.wikipedia.org/wiki/Folded_spectrum_method **/
template < typename LinearOperator, typename VectorSpace, typename EigenValue,
	   typename Damping, typename Iteration >
int fsm(const LinearOperator& H, VectorSpace& phi, EigenValue eps, Damping alpha, Iteration& iter)
{
    VectorSpace v1(H * phi - eps * phi);
    for (; !iter.finished(v1); ++iter) {
	VectorSpace v2(H * v1 - eps * v1);
	phi-= alpha * v2;
	phi/= two_norm(phi);
	v1= H * phi - eps * phi;
    }
    return iter;
}

} // namespace itl

#endif // ITL_FSM_INCLUDE


/*
@copyright Louis Dionne 2014
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#include <boost/hana/detail/assert.hpp>
#include <boost/hana/detail/constexpr.hpp>
#include <boost/hana/integral.hpp>
#include <boost/hana/list/instance.hpp>
#include <boost/hana/range.hpp>
using namespace boost::hana;
using namespace literals;

int main() {
    //! [main]
    BOOST_HANA_CONSTEXPR_LAMBDA auto negative = [](auto x) {
        return x < int_<0>;
    };

    BOOST_HANA_CONSTANT_ASSERT(
        drop_while(negative, range(int_<-3>, int_<6>)) == range(int_<0>, int_<6>)
    );

    BOOST_HANA_CONSTANT_ASSERT(
        drop_while(negative, list(1_c, -2_c, 4_c, 5_c)) == list(1_c, -2_c, 4_c, 5_c)
    );
    //! [main]
}