#include <iostream>
#include <list>
#include <ctime>
#include <cstdio>

#include <tclap/CmdLine.h>
#include <boost/algorithm/string/join.hpp>

#include "param.h"
#include "neuralClasses.h"
#include "graphClasses.h"
#include "util.h"
#include "RBMDahlFunctions.h"
#include "log_add.h"
#include <cmath>
#include <stdlib.h>

typedef Node <Word_embeddings> word_node;
typedef Node <Context_matrix> context_node;
typedef Node <Hidden_layer> hidden_node;

#include "lossFunctions.h"

//#include<tr1/random>
#include <time.h>
//#include <chrono>
//#include <random>
#include <Eigen/Dense>
#include <Eigen/Core>
#include <stdio.h>
#include <iomanip>
#include <boost/random/uniform_real_distribution.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <omp.h>
#include <math.h>
#include <boost/unordered_map.hpp>
#include <boost/functional.hpp>
#include "gradientCheck.h"

typedef boost::unordered_map<vector<int>, double> vector_map;
typedef boost::unordered_map<vector<int>, vector<double> > vector_vector_map;
typedef boost::unordered_map<int,vector_map > thread_vector_map;

//#define EIGEN_DONT_PARALLELIZE

using namespace std;
using namespace TCLAP;
using namespace Eigen;
using namespace boost::random;

//FUNCTION DECLARATIONS
void inline fProp(param & ,int ,int ,word_node  &,context_node  &,hidden_node &,vector<Matrix<int,Dynamic,1> >&);
void inline fPropValidation(param & ,int ,int ,word_node  &,context_node  &,hidden_node &, vector<Matrix<int,Dynamic,1> >&);

inline void printTimeStamp(time_t startT)
{
    time_t stopT, diffT;

    time(&stopT);
    diffT = difftime(stopT, startT);
    cout << endl << "Time stamp: " << ctime(&stopT);
    cout << diffT << " seconds after started\n\n";
}

int main(int argc, char** argv)
{
    //srand48(time(0));

    srand48(1234);
    //omp_set_num_threads(4);
    //cout<<"the number of threads is "<<omp_get_num_threads()<<endl;

    Eigen::initParallel();
	  unsigned seed = std::time(0);
    //unsigned test_seed = 1234; //for testing only
    mt19937 eng_int (seed);  // mt19937 is a standard mersenne_twister_engine, I'll pass this to sample h given v
    mt19937 eng_real (seed);  // mt19937 is a standard mersenne_twister_engine

    param myParam;
    try{
      // program options //
      CmdLine cmd("Command description message ", ' ' , "1.0");

      ValueArg<string> train_file("", "train_file", "training file" , true, "string", "string", cmd);
      ValueArg<string> words_file("", "words_file", "words file" , true, "string", "string", cmd);
      ValueArg<string> validation_file("", "validation_file", "validation file" , true, "string", "string", cmd);
      ValueArg<string> unigram_probs_file("", "unigram_probs_file", "unigram probs file" , true, "string", "string", cmd);
      ValueArg<string> embeddings_prefix("", "embeddings_prefix", "embedding prefix for the embeddings. Default:embeddings.cpp.epoch" , false, "embeddings.cpp.epoch", "string", cmd);

      ValueArg<int> ngram_size("", "ngram_size", "The size of ngram that you want to consider. Default:3", false, 3, "int", cmd);

      ValueArg<int> n_vocab("", "n_vocab", "The vocabulary size. This has to be supplied by the user", true, 1000, "int", cmd);

      ValueArg<int> n_hidden("", "n_hidden", "The number of hidden nodes. Default 100", false, 100, "int", cmd);
      ValueArg<int> n_threads("", "n_threads", "The number of threads. Default:2", false, 2, "int", cmd);

      ValueArg<int> num_noise_samples("", "num_noise_samples", "The number of noise samples. Default:25", false, 25, "int", cmd);

      ValueArg<int> embedding_dimension("", "embedding_dimension", "The size of the embedding dimension. Default 50", false, 10, "int", cmd);

      ValueArg<int> minibatch_size("", "minibatch_size", "The minibatch size. Default: 64", false, 64, "int", cmd);
      ValueArg<int> validation_minibatch_size("", "validation_minibatch_size", "The validation set minibatch size. Default: 64", false, 64, "int", cmd);

      ValueArg<int> num_epochs("", "num_epochs", "The number of epochs. Default:10 ", false, 10, "int", cmd);

      ValueArg<double> learning_rate("", "learning_rate", "Learning rate for training. Default:0.01", false, 0.01, "double", cmd);

      ValueArg<double> normalization_init("", "normalization_init", "The initial normalization parameter. Default:8.43385", false, 8.43385, "double", cmd);

      ValueArg<bool> use_momentum("", "use_momentum", "Use momentum during training or not. Default:0", false, 0, "bool", cmd);
      ValueArg<double> initial_momentum("", "initial_momentum", ".Initial value of momentum. Default:0.9", false, 0.9, "double", cmd);
      ValueArg<double> final_momentum("", "final_momentum", "Final value of momentum. Default:0.9", false, 0.9, "double", cmd);

      ValueArg<bool> persistent("", "persistent", "Use persistent CD or not. Default:0", false, 0, "bool", cmd);

      ValueArg<double> L2_reg("", "L2_reg", "The L2 regularization weight. Weight decay is only applied to the U parameter. Default:0.00001", false, 0.00001, "double", cmd);

      ValueArg<bool> init_normal("", "init_normal", "Initialize parameters form a normal distribution Default:0", false, 0, "bool", cmd);
      cmd.parse(argc, argv);

      // define program parameters //
      myParam.train_file = train_file.getValue();
      myParam.validation_file= validation_file.getValue();
      myParam.unigram_probs_file= unigram_probs_file.getValue();
      myParam.ngram_size = ngram_size.getValue();
      myParam.n_vocab= n_vocab.getValue();
      myParam.n_hidden= n_hidden.getValue();
      myParam.n_threads  = n_threads.getValue();
      myParam.num_noise_samples = num_noise_samples.getValue();
      myParam.embedding_dimension = embedding_dimension.getValue();
      myParam.minibatch_size = minibatch_size.getValue();
      myParam.validation_minibatch_size = validation_minibatch_size.getValue();
      myParam.num_epochs= num_epochs.getValue();
      myParam.persistent = persistent.getValue();
      myParam.learning_rate = learning_rate.getValue();
      myParam.use_momentum = use_momentum.getValue();
      myParam.embeddings_prefix = embeddings_prefix.getValue();
      myParam.words_file = words_file.getValue();
      myParam.initial_momentum = initial_momentum.getValue();
      myParam.final_momentum = final_momentum.getValue();
      myParam.L2_reg = L2_reg.getValue();
      myParam.init_normal= init_normal.getValue();
      myParam.normalization_init = normalization_init.getValue();

      // print program command to stdout//

      cout << "Command line: " << endl;

      for (int i = 0; i < argc; i++)
      {
        cout << argv[i] << endl;
      }
      cout << endl;

      cout << train_file.getDescription() << " : " << train_file.getValue() << endl;
      cout << unigram_probs_file.getDescription() << " : " << unigram_probs_file.getValue() << endl;
      cout << ngram_size.getDescription() << " : " << ngram_size.getValue() << endl;
      cout << embedding_dimension.getDescription() << " : " << embedding_dimension.getValue() << endl;
      cout << n_hidden.getDescription() << " : " << n_hidden.getValue() << endl;
      cout << n_threads.getDescription() << " : " << n_threads.getValue() << endl;
      cout << num_noise_samples.getDescription() << " : " << num_noise_samples.getValue() << endl;
      cout << n_vocab.getDescription() << " : " << n_vocab.getValue() << endl;
      cout << minibatch_size.getDescription() << " : " << minibatch_size.getValue() << endl;
      cout << validation_minibatch_size.getDescription() << " : " << validation_minibatch_size.getValue() << endl;
      cout << num_epochs.getDescription() << " : " << num_epochs.getValue() << endl;
      cout << persistent.getDescription() << " : " << persistent.getValue() << endl;
      cout << learning_rate.getDescription() << " : " << learning_rate.getValue() << endl;
      cout << use_momentum.getDescription() << " : " << use_momentum.getValue() << endl;
      cout << words_file.getDescription() << " : " << words_file.getValue() << endl;
      cout << initial_momentum.getDescription() << " : " << initial_momentum.getValue() << endl;
      cout << final_momentum.getDescription() << " : " << final_momentum.getValue() << endl;
      cout << L2_reg.getDescription() << " : " << L2_reg.getValue() << endl;
      cout << embeddings_prefix.getDescription() << " : " << embeddings_prefix.getValue() << endl;
      cout << init_normal.getDescription() << " : " << init_normal.getValue() << endl;
      cout << normalization_init.getDescription() << " : " << normalization_init.getValue() << endl;

    }
    catch (TCLAP::ArgException &e)
    {
      cerr << "error: " << e.error() <<  " for arg " << e.argId() << endl;
      exit(1);
    }

    /////////////////////////READING IN THE TRAINING AND VALIDATION DATA///////////////////
    /////////////////////////////////////////////////////////////////////////////////////
    cout<<"train file is "<<myParam.train_file<<endl;
    vector<vector<int> > unshuffled_training_data;
    //cout<<"the size of unshuffled training data is"<<unshuffled_training_data.size()<<endl;
    readTrainFile(myParam,unshuffled_training_data);
	  int training_data_size = unshuffled_training_data.size();
    cout<<"the training data size was "<<training_data_size<<endl;
    vector<vector<int> > validation_set_vector;
    readDataFile(myParam.validation_file,myParam,validation_set_vector);
    cout<<"read the validation file"<<endl;
    int validation_set_size = validation_set_vector.size();
    //cout<<"the validation set size is "<<validation_set_size<<endl;
    //getchar();
	  //now shuffling training data
	  random_shuffle ( unshuffled_training_data.begin(), unshuffled_training_data.end() );

    //now dump the training data to a temp file
    writeTempData(unshuffled_training_data,myParam);

    //clearing the data vector so that we dont' run out of memory
    unshuffled_training_data.clear();
    vector< Matrix<int,Dynamic,1> >shuffled_training_data;
    vector< Matrix<int,Dynamic,1> >validation_set;

    for (int ngram = 0;ngram < myParam.ngram_size;ngram++)
    {
        Matrix<int,Dynamic,1> column ;
        column.setZero(training_data_size);
        shuffled_training_data.push_back(column);
    }
    if (validation_set_size > 0)
    {
        for (int ngram = 0;ngram < myParam.ngram_size;ngram++)
        {
            Matrix<int,Dynamic,1> column_validation ;
            column_validation.setZero(validation_set_vector.size());
            validation_set.push_back(column_validation);
        }

    }

    cout<<"the size of the training data matrix is "<<shuffled_training_data.size()<<endl;
    cout<<"created the training data matrix and the validation set matrix"<<endl;
    //Matrix<int,Dynamic,Dynamic>  validation_set;

    //I have to determine the sizes of the validation set and the training data
    //shuffled_training_data.setZero(training_data_size,myParam.ngram_size);
    //validation_set.setZero(validation_set_vector.size(),myParam.ngram_size);

    //storing the training data in the training matrix and the validation matrix
    cout<<"storing the training data into the eigen matrix"<<endl;

    readTrainFileMatrix("temp.dat",myParam,shuffled_training_data);
    //readTrainFileMatrix("temp.dat",myParam,shuffled_training_data);
    cout<<"storing the validation data into the eigen matrix"<<endl;
    for (int i = 0;i<validation_set_vector.size();i++)
    {
        //string joined = boost::algorithm::join(unshuffled_training_data[i], " ");
        for (int j=0;j<myParam.ngram_size;j++)
        {
            validation_set[j](i) = validation_set_vector[i][j];
        }
    }

    validation_set_vector.clear();
    //cout<<"the training data is "<<endl;
    //cout<<shuffled_training_data<<endl;
    cout<<"the size of the training data is "<<shuffled_training_data.size()<<endl;
    //writeTempDataMatrix(shuffled_training_data, myParam);
    //exit(0);
    vector<string> word_list;
    cout<<"reading words file"<<endl;
    readWordsFile(myParam.words_file, word_list);
    cout<<" the word list had size "<<word_list.size()<<endl;

    ////////////////////////////////////////ALIAS METHOD STUFF//////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////

    //reading the unigram probs

    vector<double> unigram_probs = vector<double>(myParam.n_vocab);
    readUnigramProbs(myParam,unigram_probs);

   //now i have the unigram probs, I need to setup alias method
    vector<int> J(myParam.n_vocab,-1);
    vector<double> q(myParam.n_vocab,0.);
    setupAliasMethod(unigram_probs,J ,q,myParam.n_vocab);
    cout<<"q is "<<q.size()<<endl;
    cout<<"J is "<<J.size()<<endl;

    //for multithreading, I will make copies of q and J,
    //unigram probs, and the random number generators
    //one for each thread

    vector<vector<int> > J_vector;
    vector<vector<double> > q_vector;
    vector<vector<double> > unigram_probs_vector;
    vector<mt19937> eng_int_vector;
    vector<mt19937> eng_real_vector;
    vector<uniform_int_distribution<> >unif_int_vector;
    vector<uniform_real_distribution<> >unif_real_vector;

    for (int i=0;i<myParam.n_threads;i++)
    {
        vector<int> temp_J = J;
        J_vector.push_back(temp_J);
        vector<double> temp_q = q;
        q_vector.push_back(temp_q);
        vector<double> temp_unigram_probs = unigram_probs;
        unigram_probs_vector.push_back(temp_unigram_probs);
        clock_t t = clock()+rand();
        mt19937 eng_int_temp (t);
        eng_int_vector.push_back(eng_int_temp);
        uniform_int_distribution<> unif_int_temp(0, myParam.n_vocab-1);
        unif_int_vector.push_back(unif_int_temp);
        mt19937 eng_real_temp (t);  // mt19937 is a standard mersenne_twister_engine
        eng_real_vector.push_back(eng_real_temp);
        uniform_real_distribution<> unif_real_temp(0.0, 1.0);
        unif_real_vector.push_back(unif_real_temp);
        cout<<"the clock was "<<t<<endl;

    }
    //initalizing the threads again
    for (int i=0;i<myParam.n_threads;i++)
    {
        clock_t t = clock()+rand();
        eng_int_vector[i].seed(t);
        eng_real_vector[i].seed(t);

    }

    /////CREATING THE NEURAL NETWORK GRAPH/////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////

    //initializing the neural LM
    mt19937 init_engine(clock()+rand()); //initializing the engine
    Word_embeddings D(myParam.n_vocab,myParam.embedding_dimension,init_engine,myParam.init_normal,myParam.n_threads,myParam.ngram_size-1);

    Output_word_embeddings D_prime(myParam.n_vocab,myParam.n_hidden,init_engine,myParam.init_normal,myParam.n_threads);

    //creating the context nodes
    Context_matrix network_context_matrix(myParam.embedding_dimension,myParam.embedding_dimension*(myParam.ngram_size-1),init_engine,myParam.init_normal,myParam.n_threads);

    Node <Context_matrix> network_context_node(&network_context_matrix,myParam.n_hidden,myParam.minibatch_size,
                                            myParam.embedding_dimension*(myParam.ngram_size-1),myParam.minibatch_size,myParam.n_hidden,
                                            myParam.validation_minibatch_size);

    //creating the word nodes
    Node<Word_embeddings>network_word_node(&D,myParam.embedding_dimension*(myParam.ngram_size-1),myParam.minibatch_size,-1,-1,myParam.embedding_dimension*(myParam.ngram_size-1),myParam.validation_minibatch_size);

    //creating the hidden layer
    Hidden_layer hidden(myParam.n_hidden,init_engine,myParam.init_normal,myParam.n_threads);

    //creating a node for the hidden layer and for the hidden to output matrix
    hidden_node hidden_layer_node(&hidden,myParam.embedding_dimension,myParam.minibatch_size,myParam.n_hidden,myParam.minibatch_size
                                        ,myParam.embedding_dimension,myParam.validation_minibatch_size);

    ///////////////////////TRAINING THE NEURAL NETWORK////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////
    //testing sample h given v
    //int training_data_size = unshuffled_training_data.size();
    cout<<"the traning data size is "<<training_data_size<<endl;
    int carry_over = (training_data_size%myParam.minibatch_size == 0)? 0:1;
    int num_batches = training_data_size/myParam.minibatch_size + carry_over;
    //get the generated samples
    vector<int>random_nos_int;
    vector <double> random_nos_real;

    //readRandomNosReal("random_nos.real.1000000" ,random_nos_real);
    //readRandomNosInt("random_nos.int" ,random_nos_int);
    int random_nos_int_counter = 0;
    int random_nos_real_counter = 0;

    //cout<<"read random nos double and int"<<endl;
    //getchar();
    cout<<"num training batches is "<<num_batches<<endl;
    //getchar();
    //performing training
    clock_t t;
    //
    double current_momentum = myParam.initial_momentum;
    double momentum_delta = (myParam.final_momentum - myParam.initial_momentum)/(myParam.num_epochs-1);
    double current_learning_rate = myParam.learning_rate;
    setprecision(15);
    double current_validation_ll = -99999999999;
    cout<<"the validation set size was "<<validation_set_size<<endl;
    int num_validation_batches = validation_set_size/myParam.validation_minibatch_size;
    cout<<"num valiation batches is "<<num_validation_batches<<endl;

    //defining a bunch of variables that will be useful for multithreading. These will be declared as firstprivate
    int validation_minibatch_size = myParam.validation_minibatch_size;
    int ngram_size = myParam.ngram_size;
    int n_vocab = myParam.n_vocab;
    int embedding_dimension = myParam.embedding_dimension;
    int minibatch_size = myParam.minibatch_size;
    int num_noise_samples = myParam.num_noise_samples;
    double normalization_init = myParam.normalization_init;

    //creating the map of the normalization constants
    //vector_map c_h;
    vector_vector_map c_h;
    vector<vector_map> thread_c_h;

    for (int thread_id =0 ;thread_id<myParam.n_threads;thread_id++)
    {
        vector_map temp;
        thread_c_h.push_back(temp);
    }

    //initialize the vector map
    for (int i=0;i<training_data_size;i++)
    {
        vector<int> context;//(ngram_size-1);

        //creating the context
        for (int word = 0;word<ngram_size-1;word++)
        {
            context.push_back(shuffled_training_data[word](i));
        }
        if (c_h.find(context) == c_h.end())
        {
            //cout<<"context cache miss"<<endl;
            vector<double> temp(2);
            temp[0] = -normalization_init;
            temp[1] = exp(temp[0]);
            //c_h[context] = -normalization_init;

            c_h[context] = temp;
            for (int thread_id =0 ;thread_id<myParam.n_threads;thread_id++)
            {
                thread_c_h[thread_id][context]  = -normalization_init;
            }

        }

    }

    for (int epoch = 0 ;epoch<myParam.num_epochs;epoch++)
    {
        /*
        //every 5 epochs, print the norm of the paramters
        if (epoch %5 ==0)
        {
            cout<<"the square root of the squared norm of D_prime is "<<D_prime.W.norm()<<endl;
            cout<<"the square root of the squared norm of D is "<<D.W.norm()<<endl;
            cout<<"the square root of the squared norm of the h bias is "<<hidden.h_bias.norm()<<endl;
            for (int word = 0;word<ngram_size-1;word++)
            {
                cout<<"the square root of the squared norm of the context "<<word<<" is "<<contexts[word].U.norm()<<endl;
            }
        }
        */
        cout<<"current learning rate is "<<current_learning_rate<<endl;
        if (epoch %1 ==0 && validation_set_size > 0)
        {
            //////COMPUTING VALIDATION SET PERPLEXITY///////////////////////
            ////////////////////////////////////////////////////////////////
            //first do forward propagation and then compute the probabilities
            double log_likelihood = 0.0;
            vector_map normalization_cache;
            //double min_score = 9999999999999;

            for (int validation_batch =0;validation_batch < num_validation_batches;validation_batch++)
            {
                int validation_minibatch_start_index = myParam.validation_minibatch_size * validation_batch;
                //cout<<"validation minibatch start index is "<<validation_minibatch_start_index<<endl;
                fPropValidation(myParam,validation_minibatch_start_index,myParam.validation_minibatch_size,network_word_node,
                                network_context_node,hidden_layer_node,validation_set);

                Eigen::setNbThreads(1);
                //cout<<"validation minibatch size is "<<myParam.validation_minibatch_size<<endl;
                #pragma omp parallel  firstprivate(validation_minibatch_size,validation_minibatch_start_index,ngram_size,n_vocab,embedding_dimension)
                {
                #pragma omp for reduction(+:log_likelihood)
                for (int valid_id = 0;valid_id < validation_minibatch_size;valid_id++)
                {

                    int thread_id = omp_get_thread_num();
                    //cout<<"thread id is "<<thread_id<<endl;
                    //cout<<"valid id is "<<valid_id<<endl;
                    //cout<<"the empirical output is "<<validation_set[ngram_size-1](valid_id+validation_minibatch_start_index)<<endl;
                    //getchar();
                    int empirical_output = validation_set[ngram_size-1](valid_id+validation_minibatch_start_index);
                    vector<int> context;
                    //cout<<" context is ";
                    for ( int word = 0;word < ngram_size-1;word++)
                    {
                        context.push_back(validation_set[word](valid_id+validation_minibatch_start_index));

                    }
                    //cout<<endl;
                    //getchar();
                    bool found = 1;
                    //I want to access the dictionary only 1 thread at a time. I don't think this needs to be critical
                    //cout<<"in critical section"<<endl;
                    if (normalization_cache.find(context) == normalization_cache.end())
                    {
                        found = 0;
                    }

                    if (found == 0)
                    {
                        //the predicted embedding will be the same per context
                        Matrix<double,Dynamic,1> predicted_embedding = hidden_layer_node.fProp_validation_matrix.col(valid_id);

                        //cout<<"we had a cache miss"<<endl;
                        double empirical_output_score = 0.;
                        double normalization_constant = -99999999999;
                        //we didnt find the context in the cache so we have to compute the normalization constant
                        for (int output_word_id = 0;output_word_id < n_vocab;output_word_id ++)
                        {
                            //now computing the score of all the words
                            double score =  D_prime.W.row(output_word_id).dot(predicted_embedding) +
                                            D_prime.b(output_word_id);
                            //cout<<" score is "<<score<<endl;
                            //cout<<"computed the score "<<endl;
                            if (empirical_output == output_word_id)
                            {
                                empirical_output_score = score;
                            }

                            //log adding the score
                            normalization_constant = Log<double>::add(normalization_constant,score);
                            //cout<<"after adding the normalization constant is "<<normalization_constant<<endl;

                        }
                        //again, since we're updating the cache, it has to be thread safe
                        #pragma omp critical
                        {
						    cout<<"Final normalization constant is "<<normalization_constant<<endl;
                            normalization_cache[context] = normalization_constant;
                        }

                        log_likelihood += empirical_output_score-normalization_constant;

                        //log_likelihood_vector[valid_id] = empirical_output_score-normalization_constant;

                        //computing the score of the actual output
                    }
                    else
                    {
                        //cout<<"we had a cache hit and the normalization constant was "<<normalization_cache[context]<<endl;
                        Matrix<double,Dynamic,1> predicted_embedding = hidden_layer_node.fProp_validation_matrix.col(valid_id);
                        //now computing the score of all the words
                        double empirical_output_score =  D_prime.W.row(empirical_output).dot(predicted_embedding) +
                                                          D_prime.b(empirical_output);
                        //making the code threadsafe by making this section critical since we're reading from the same data structure
                        double normalization_constant = 0.0;
                        # pragma omp critical
                        {
                            normalization_constant = normalization_cache[context];
                        }

                        log_likelihood += empirical_output_score-normalization_constant;
                        //log_likelihood_vector[valid_id] = empirical_output_score-normalization_constant;
                    }

                }
                }
                #pragma omp barrier

            }

            //cout<<"The min score is "<<min_score<<endl;
            cout<<"the log likelihood is "<<log_likelihood<<endl;
            //computing perplexity
            //perplexity =
            if (log_likelihood < current_validation_ll)
            {
                current_learning_rate /= 2; //halving the learning rate if the validation perplexity decreased
            }
            current_validation_ll = log_likelihood;
            //getchar();
            //clearing the validation matrices
            network_context_node.fProp_validation_matrix.setZero();
            network_word_node.fProp_validation_matrix.setZero();
            hidden_layer_node.fProp_validation_matrix.setZero();

        }
        else
        {
            cout<<"Validation set size is 0!"<<endl;
        }

        if (myParam.use_momentum == 0)
        {
            current_momentum = -1;
        }
        cout<<"current momentum is "<<current_momentum<<endl;
        //current_momentum  =-1;
        cout<<"epoch is "<<epoch+1<<endl;
        double average_f = 0.0;
        for(int batch=0;batch<num_batches;batch++)
        {
            //cout<<"batch number is "<<batch<<endl;
            if (batch%1000 == 0)
            {
                cout<<"processed "<<batch<<" batches"<<endl;
            }
            //int cdk = 0; //set this for now
            int current_minibatch_size = ((batch+1)*myParam.minibatch_size <= training_data_size)? myParam.minibatch_size:training_data_size%myParam.minibatch_size;
            int minibatch_start_index = myParam.minibatch_size * batch;
            //cout<<"minibatch start index is "<<minibatch_start_index<<endl;
            double adjusted_learning_rate = current_learning_rate/current_minibatch_size;
            //cout<<"the adjusted learning rate is "<<adjusted_learning_rate<<endl;
            /*
            if (batch == rand() % num_batches)
            {
                cout<<"we are checking the gradient in batch "<<batch<<endl;
                /////////////////////////CHECKING GRADIENTS////////////////////////////////////////
                gradientChecking(myParam,minibatch_start_index,current_minibatch_size,word_nodes,context_nodes,hidden_layer_node,hidden_layer_to_output_node,
                              shuffled_training_data,c_h,unif_real_vector,eng_real_vector,unif_int_vector,eng_int_vector,unigram_probs_vector,
                              q_vector,J_vector,D_prime);
            }
            */
            ///////////////////FORWARD PROPAGATION/////////////////////////
            ////////////////////////////////////////////////////////////////
            fProp(myParam,minibatch_start_index,current_minibatch_size,network_word_node,network_context_node,hidden_layer_node,shuffled_training_data);
            /*
            if (epoch%2==0 && epoch>=1)
            {
                cout<<"the hidden node activations are "<<hidden_layer_node.fProp_matrix<<endl;
                getchar();
            }
            */

            /////////////////COMPUTING THE NCE LOSS FUNCTION//////////////////////////////////
            /////////////////////////////////////////////////////////////////////////////////
            //////////////////BACK PROPAGATION//////////////////////////////////
            ////////////////////////////////////////////////////////////////
            //computing the loss function
            //now computing the loss function
            //Matrix<double,Dynamic,Dynamic> output_gradient;
            Matrix<double,Dynamic,Dynamic> minibatch_predicted_embeddings(myParam.embedding_dimension,myParam.minibatch_size);
            Matrix<double,Dynamic,1> minibatch_positive_weights(myParam.minibatch_size);
            Matrix<double,Dynamic,Dynamic> minibatch_negative_weights(myParam.minibatch_size,myParam.num_noise_samples);
            Matrix<int,Dynamic,Dynamic> minibatch_negative_samples(myParam.minibatch_size,myParam.num_noise_samples);

            //for each thread, storing the training data locations

            //output_gradient.setZero(myParam.embedding_dimension,current_minibatch_size);

            //int thread_id = 0; //for now, this is a proxy. When I'm multithreading this code, the thread ID will change
            //creating the unordered map for each thread
            //thread_vector_map c_h_gradient;
            vector<vector_map> c_h_gradient;
            for (int thread_id =0 ;thread_id<myParam.n_threads;thread_id++)
            {
                vector_map temp;
                c_h_gradient.push_back(temp);
            }
            clock_t t;
            //c_h_gradient += minibatch_positive_weights(train_id)
            //parallelizing the creation with multithreading
            //omp_set_num_threads(myParam.n_threads);
            //Eigen::initParallel();
            Eigen::setNbThreads(1);
            t = clock();
            #pragma omp parallel firstprivate(minibatch_size,minibatch_start_index,ngram_size,n_vocab,embedding_dimension, \
                                    num_noise_samples,normalization_init)
            {
              /*
              #pragma omp master
              {
                  cout<<"num threads is "<<omp_get_num_threads()<<endl;
              }
              */
              //cout<<"thread id is "<<thread_id<<endl;

            #pragma omp for reduction(+:average_f) schedule(static,100)
            for (int train_id = 0;train_id < minibatch_size;train_id++)
            {
                //clock_t t = clock();
                //double sample_f = 0.;
                int thread_id = omp_get_thread_num();
                int output_word = shuffled_training_data[ngram_size-1](minibatch_start_index+train_id);
                //int output_word = (*thread_data_col_locations[thread_id][ngram_size-1])(minibatch_start_index+train_id);
                //cout<<"output word is "<<output_word<<endl;
                Matrix<double,Dynamic,1> predicted_embedding=hidden_layer_node.fProp_matrix.col(train_id);

                vector<int> context;//(ngram_size-1);

                //creating the context
                for (int word = 0;word<ngram_size-1;word++)
                {
                    context.push_back(shuffled_training_data[word](minibatch_start_index+train_id));
                    //context.push_back((*thread_data_col_locations[thread_id][word])(minibatch_start_index+train_id));
                }

                //double log_inv_normalization_const_h = 0.;
                //getting a normalization constant and making it threasafe

                //this region does not need to be critical because its just a read
                //log_inv_normalization_const_h = c_h[context];

                //double inv_normalization_const_h = exp(log_inv_normalization_const_h);

                double inv_normalization_const_h = c_h[context][1];
                //cout<<"The normalization constant is "<<inv_normalization_const_h<<endl;
                //setting the gradient for that context to 0;
                //double c_h_gradient = 0.0;

                minibatch_predicted_embeddings.col(train_id) = predicted_embedding;
                double score = D_prime.W.row(output_word).dot(predicted_embedding) + D_prime.b(output_word);
                double unnorm_positive_prob = exp(score);
                double positive_prob = unnorm_positive_prob*inv_normalization_const_h;
                //cout<<"unnorm positive prob was "<<unnorm_positive_prob<<endl;
                minibatch_positive_weights(train_id) = num_noise_samples*unigram_probs_vector[thread_id][output_word]/
                                                       (unnorm_positive_prob*inv_normalization_const_h + num_noise_samples * unigram_probs_vector[thread_id][output_word]) ;
                //cout<<"the score was "<<score<<endl;
                //cout<<"the positive weight was "<< minibatch_positive_weights(train_id)<<endl;

                //sample_f += log(positive_prob/(positive_prob + num_noise_samples * unigram_probs_vector[thread_id][output_word]));

                if (c_h_gradient[thread_id].find(context) == c_h_gradient[thread_id].end())
                {
                    c_h_gradient[thread_id][context] = minibatch_positive_weights(train_id);
                }
                else
                {
                    //cout<<"we got a repeat!"<<endl;
                    c_h_gradient[thread_id][context] += minibatch_positive_weights(train_id);
                }

                for (int sample_id = 0;sample_id <num_noise_samples;sample_id++)
                {

                    int mixture_component = unif_int_vector[thread_id](eng_int_vector[thread_id]);
                    //cout<<"mixture component was "<<mixture_component<<endl;
                    double p = unif_real_vector[thread_id](eng_real_vector[thread_id]);
                    int sample ;
                    //cout<<"computing sample"<<endl;
                    //cout<<"remaining bernoulli item is "<<J_vector[thread_id][mixture_component]<<endl;
                    if (q_vector[thread_id][mixture_component] >= p)
                    {
                        sample = mixture_component;
                    }
                    else
                    {
                        sample = J_vector[thread_id][mixture_component];
                    }

                    //vector<int> context(ngram_size-1);

                    //cout<<"the sample was "<<sample<<endl;
                    assert (sample >= 0);
                    minibatch_negative_samples(train_id,sample_id) = sample;
                    double negative_score = D_prime.W.row(sample).dot(predicted_embedding) + D_prime.b(sample);
                    double negative_unnorm_prob = exp(negative_score);
                    double negative_prob = negative_unnorm_prob*inv_normalization_const_h;
                    minibatch_negative_weights(train_id,sample_id) = negative_unnorm_prob*inv_normalization_const_h/
                                                                    (negative_unnorm_prob*inv_normalization_const_h + num_noise_samples * unigram_probs_vector[thread_id][sample]);
                    //sample_f += log(num_noise_samples * unigram_probs_vector[thread_id][sample]/( num_noise_samples * unigram_probs_vector[thread_id][sample] + negative_prob));
                    c_h_gradient[thread_id][context] -= minibatch_negative_weights(train_id,sample_id);
                }
                //average_f += sample_f;

            }
            //getchar();
            }
            #pragma omp barrier
            //cout<<"the time taken to compute the noise samples was "<<clock()-t<<endl;
            //updating the normalization constants
            for (int thread_id=0;thread_id<myParam.n_threads;thread_id++)
            {
                vector_map::iterator it;
                for (it = c_h_gradient[thread_id].begin();it != c_h_gradient[thread_id].end();it++)
                {
                    c_h[(*it).first][0] += adjusted_learning_rate * (*it).second;
                    c_h[(*it).first][1] = exp(c_h[(*it).first][0]);

                }
            }

            //cout<<"the time taken to update normalization constants was "<<clock()-t<<endl;
            //t = clock();
            //first comput the backprop gradient
            Matrix<double,Dynamic,Dynamic> context_bProp_matrix;//(myParam.embedding_dimension,current_minibatch_size);
            context_bProp_matrix.setZero(myParam.n_hidden,current_minibatch_size);
            D_prime.bProp(shuffled_training_data[myParam.ngram_size-1],minibatch_positive_weights,
                            minibatch_negative_samples,minibatch_negative_weights,
                            context_bProp_matrix,minibatch_start_index,current_minibatch_size,myParam.num_noise_samples);

            D_prime.computeGradient(minibatch_predicted_embeddings,shuffled_training_data[myParam.ngram_size-1],minibatch_positive_weights,
                            minibatch_negative_samples,minibatch_negative_weights,
                            minibatch_start_index,current_minibatch_size,myParam.num_noise_samples,adjusted_learning_rate,current_momentum);

            //now doing backprop on the hidden node
            hidden_layer_node.param->bPropRectifiedLinear(context_bProp_matrix,hidden_layer_node.bProp_matrix,hidden_layer_node.fProp_matrix);

            //now doing backprop on the context matrices
            network_context_node.param->bProp(hidden_layer_node.bProp_matrix,network_context_node.bProp_matrix);
            //cout<<"the time taken to do bprop on the context matrix is "<<clock()-t<<endl;
            //t = clock();
            network_context_node.param->computeGradientOmp(hidden_layer_node.bProp_matrix,network_word_node.fProp_matrix,adjusted_learning_rate,current_momentum,myParam.L2_reg);

            network_word_node.param->computeGradient(network_context_node.bProp_matrix,shuffled_training_data,minibatch_start_index,
                                                        current_minibatch_size,adjusted_learning_rate,current_momentum,myParam.L2_reg,myParam.ngram_size-1);

            network_context_node.fProp_matrix.setZero();
            network_context_node.bProp_matrix.setZero();
            network_word_node.fProp_matrix.setZero();
            hidden_layer_node.fProp_matrix.setZero();
            hidden_layer_node.bProp_matrix.setZero();
            //network_context_node.fProp_validation_matrix.setZero();
            //network_word_node.fProp_validation_matrix.setZero();
            //hidden_layer_node.fProp_validation_matrix.setZero();

        }
        //cout<<"the average f for the training data was "<<average_f/training_data_size<<endl;
        current_momentum += momentum_delta;
        //writing the parameters after every epoch
        stringstream ss;//create a stringstream
        ss << epoch;//add number to the stream
        //return ss.str();//return a string with the contents of the stream
        cout<<"..Writing the parameters.."<<endl;
        //first write the output word representations and bias
        string D_prime_W_output_file = "D_prime_W." + ss.str();
        writeMatrix(D_prime.W,D_prime_W_output_file);
        string D_prime_b_output_file = "D_prime_b." + ss.str();
        writeVector(D_prime.b,D_prime_b_output_file);

        //then writing the input word embeddings
        string D_W_output_file = "D_W." + ss.str();
        writeMatrix(D.W,D_W_output_file);
        string U_output_file = "U."+ss.str();
        writeMatrix(network_context_matrix.U,U_output_file);
    }
    return 0;
}

void inline fProp(param & myParam,int minibatch_start_index,int current_minibatch_size,word_node &network_word_node,
            context_node &network_context_node,hidden_node &hidden_layer_node,vector<Matrix<int,Dynamic,1> >&data)
{
    /////FORWARD PROPAGATION/////////////
    //doing forward propagation first with word nodes
    omp_set_num_threads(1);
    //clock_t t = clock();
    network_word_node.param->fPropOmp(data,network_word_node.fProp_matrix,minibatch_start_index,current_minibatch_size,myParam.ngram_size-1);
    //cout<<"time for fprop for the word embeddings was "<<clock()-t<<endl;

    omp_set_num_threads(myParam.n_threads);
    //Eigen::setNbThreads(myParam.n_threads);
    //t = clock();
    network_context_node.param->fProp(network_word_node.fProp_matrix,network_context_node.fProp_matrix);
    //cout<<"time for fprop for the context matrix was "<<clock()-t<<endl;

    //doing forward prop with the hidden nodes
    hidden_layer_node.param->fPropRectifiedLinear(network_context_node.fProp_matrix,hidden_layer_node.fProp_matrix);

}

void inline fPropValidation(param & myParam,int start_index,int validation_set_size,word_node &network_word_node,
                           context_node &network_context_node,hidden_node &hidden_layer_node, vector<Matrix<int,Dynamic,1> >&data)
{
    /////FORWARD PROPAGATION/////////////
    omp_set_num_threads(1);
    network_word_node.param->fPropOmp(data,network_word_node.fProp_validation_matrix,start_index,validation_set_size,myParam.ngram_size-1);
    //Eigen::setNbThreads(myParam.n_threads);
    omp_set_num_threads(myParam.n_threads);
    network_context_node.param->fProp(network_word_node.fProp_validation_matrix,network_context_node.fProp_validation_matrix);

    //doing forward prop with the hidden nodes
    hidden_layer_node.param->fPropRectifiedLinear(network_context_node.fProp_validation_matrix,hidden_layer_node.fProp_validation_matrix);

}


#include <iostream>

#include <armadillo>

#include <plinkio/plinkio.h>

#include <gzstream/gzutil.hpp>
#include <besiq/io/covariates.hpp>
#include <cpp-argparse/OptionParser.h>

#include <plink/plink_file.hpp>
#include <besiq/model_matrix.hpp>
#include <glm/glm.hpp>
#include <glm/models/normal.hpp>
#include <glm/models/binomial.hpp>

#include "gene_environment.hpp"

using namespace arma;
using namespace optparse;

const std::string USAGE = "besiq-env [-c covariates.csv] [-m method] environment_file genotype_plink_prefix";
const std::string VERSION = "Besiq 0.0.1";
const std::string DESCRIPTION = "A tool for inferring variant-environment interactions.";
const std::string EPILOG = "";

void run_env(genotype_matrix_ptr genotypes, const arma::vec &phenotype, const arma::mat &cov, const arma::mat &env, const std::vector<std::string> &env_names, const arma::uvec &missing, glm_model &model, std::ostream &out)
{
    out << "variable\tbeta\tse_beta\tpvalue\tN\n";
    gene_environment ge( genotypes, env, phenotype, env_names, false );
    ge.impute_missing( );
    arma::vec cent_phenotype = ge.get_centered_phenotype( );
    arma::uvec indices = arma::zeros<arma::uvec>( 1 );
    size_t var = 0;
    arma::uvec minors = arma::zeros<arma::uvec>( genotypes->size( ) );
    for(int i = 0; i < genotypes->size( ) + env.n_cols; i++)
    {
        indices[0] = i;
        glm_info result;
        arma::mat mm = ge.get_active( indices );
        arma::vec beta = glm_fit( mm, cent_phenotype, missing, model, result );
        bool valid_minor = true;
        if( i < genotypes->size( ) )
        {
            minors[ i ] = ge.compute_num_minor( i );
            valid_minor = minors[ i ] >= 10;
        }

        if( valid_minor && result.converged && result.success )
        {
            out << ge.get_name(var) << "\t" <<
                beta[ 0 ] << "\t" <<
                result.se_beta[ 0 ] << "\t" <<
                result.p_value[ 0 ] << "\t" <<
                arma::sum( 1 - missing ) << "\n";
        }
        else
        {
            out << ge.get_name(var) << "\t" <<
                "NA" << "\t" <<
                "NA" << "\t" <<
                "NA" << "\t" <<
                arma::sum( 1 - missing ) << "\n";
        }
        var++;
    }

    indices = arma::zeros<arma::uvec>( 3 );
    for(int i = 0; i < genotypes->size( ); i++)
    {
        snp_row &row = genotypes->get_row( i );
        for(int j = 0; j < env.n_cols; j++)
        {
            size_t interaction_index = genotypes->size( ) + env.n_cols + j * genotypes->size( ) + i;
            indices[ 0 ] = i;
            indices[ 1 ] = genotypes->size( ) + j;
            indices[ 2 ] = interaction_index;

            arma::mat mm = ge.get_active( indices );
            glm_info result;
            arma::vec beta = glm_fit( mm, cent_phenotype, missing, model, result );

            if( minors[ i ] >= 10 && result.converged && result.success )
            {
                out << ge.get_name( interaction_index ) << "\t" <<
                    beta[ 2 ] << "\t" <<
                    result.se_beta[ 2 ] << "\t" <<
                    result.p_value[ 2 ] << "\t" <<
                    arma::sum( 1 - missing ) << "\n";
            }
            else
            {
                out << ge.get_name( interaction_index ) << "\t" <<
                    "NA" << "\t" <<
                    "NA" << "\t" <<
                    "NA" << "\t" <<
                    arma::sum( 1 - missing ) << "\n";
            }
        }
    }
}

int
main(int argc, char *argv[])
{
    OptionParser parser = OptionParser( ).usage( USAGE )
                                         .version( VERSION )
                                         .description( DESCRIPTION )
                                         .epilog( EPILOG );

    char const* const model_choices[] = { "binomial", "normal" };
    char const* const link_choices[] = { "logit", "logc", "odds", "identity", "log" };

    parser.add_option( "-m", "--model" ).choices( &model_choices[ 0 ], &model_choices[ 2 ] ).metavar( "model" ).help( "The model to use for the phenotype, 'binomial' or 'normal', default = 'binomial'." ).set_default( "normal" );
    parser.add_option( "-p", "--pheno" ).help( "Read phenotypes from this file instead of a plink file." );
    parser.add_option( "-n", "--mpheno" ).help( "Name of the phenotype to use." );
    parser.add_option( "-e", "--menv" ).help( "Name of the environment variable to use." );
    parser.add_option( "-c", "--cov" ).action( "store" ).type( "string" ).metavar( "filename" ).help( "Performs the analysis by including the covariates in this file." );
    parser.add_option( "-o", "--out" ).help( "The output file that will contain the results (binary)." );

    Values options = parser.parse_args( argc, argv );
    std::vector<std::string> args = parser.args( );
    if( args.size( ) != 2 )
    {
        std::cerr << "besiq: error: Genotypes or environment factor is missing." << std::endl;
        parser.print_help( );
        exit( 1 );
    }

    /* Read all genotypes */
    plink_file_ptr genotype_file = open_plink_file( args[ 1 ] );
    genotype_matrix_ptr genotypes = create_genotype_matrix( genotype_file );
    std::vector<std::string> locus_names = genotype_file->get_locus_names( );

    /* Read additional data  */
    arma::vec phenotype;
    arma::uvec missing;
    arma::mat cov;
    missing = zeros<uvec>( genotype_file->get_samples( ).size( ) );
    std::vector<std::string> order = genotype_file->get_sample_iids( );
    if( options.is_set( "pheno" ) )
    {
        std::ifstream phenotype_file( options[ "pheno" ].c_str( ) );
        phenotype = parse_phenotypes( phenotype_file, missing, order, options[ "mpheno" ] );
    }
    else
    {
        phenotype = create_phenotype_vector( genotype_file->get_samples( ), missing );
    }
    phenotype.elem( arma::find_nonfinite( phenotype ) ).zeros( );

    /* Read covariates */
    if( options.is_set( "cov" ) )
    {
        std::ifstream covariate_file( options[ "cov" ].c_str( ) );
        cov = parse_covariate_matrix( covariate_file, missing, order );
    }

    /* Read environment factor */
    std::vector<std::string> env_names;
    std::ifstream environment_file( args[ 0 ].c_str( ) );
    arma::uvec env_missing = arma::zeros<arma::uvec>( missing.n_elem );
    arma::mat E = parse_env( environment_file, env_missing, order, &env_names, options[ "menv" ] );

    /* XXX: Implement proper log file. */
    arma::set_stream_err1( std::cerr );
    arma::set_stream_err2( std::cerr );

    /* Open output stream */
    std::ofstream output_file;
    if( options.is_set( "out" ) )
    {
        output_file.open( options[ "out" ].c_str( ) );
    }
    std::ostream &out = options.is_set( "out" ) ? output_file : std::cout;

    /* Create GLM */
    glm_model *model = NULL;
    if( options[ "model" ] == "binomial" )
    {
        std::string link = "logit";
        if( options.is_set( "link_function" ) )
        {
            link = options[ "link_function" ];
        }

        model= new binomial( link );
    }
    else if( options[ "model" ] == "normal" )
    {
        std::string link = "identity";
        if( options.is_set( "link_function" ) )
        {
            link = options[ "link_function" ];
        }

        model = new normal( link );
    }

    run_env( genotypes, phenotype, cov, E, env_names, missing, *model, out );

    if( model != NULL )
    {
        delete model;
    }

    return 0;
}


#include <cctbx/boost_python/flex_fwd.h>

#include <boost/python/def.hpp>
#include <boost/python/args.hpp>

#include <cctbx/maptbx/grid_indices_around_sites.h>
#include <boost/unordered_set.hpp>
#include <vector>

namespace cctbx { namespace maptbx { namespace boost_python {

namespace {

  struct grid_indices_around_sites_unordered_set :
         grid_indices_around_sites_enumerator
  {
    boost::unordered_set<unsigned> buffer;

    virtual
    void
    next_point(
      std::size_t i_grid)
    {
      buffer.insert(i_grid);
    }
  };

  boost::shared_ptr<std::vector<unsigned> >
  grid_indices_around_sites_wrapper(
    uctbx::unit_cell const& unit_cell,
    af::tiny<int, 3> const& fft_n_real,
    af::tiny<int, 3> const& fft_m_real,
    af::const_ref<scitbx::vec3<double> > const& sites_cart,
    af::const_ref<double> const& site_radii)
  {
    grid_indices_around_sites_unordered_set gias;
    gias.enumerate(unit_cell, fft_n_real, fft_m_real, sites_cart, site_radii);
    boost::shared_ptr<std::vector<unsigned> >
      result(new std::vector<unsigned>());
    std::size_t n = gias.buffer.size();
    result->reserve(n);
    boost::unordered_set<unsigned>::const_iterator p = gias.buffer.begin();
    for(std::size_t i=0;i!=n;i++) result->push_back(*p++);
    std::sort(result->begin(), result->end());
    return result;
  }

} // namespace <anonymous>

  void wrap_grid_indices_around_sites()
  {
    using namespace boost::python;
    def("grid_indices_around_sites",
      grid_indices_around_sites_wrapper, (
        arg("unit_cell"),
        arg("fft_n_real"),
        arg("fft_m_real"),
        arg("sites_cart"),
        arg("site_radii")));
  }

}}} // namespace cctbx::maptbx::boost_python


/*
    Copyright 2013 Christian Henning
    Use, modification and distribution are 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_GIL_IO_UNIT_TEST_MANDEL_HPP
#define BOOST_GIL_IO_UNIT_TEST_MANDEL_HPP

#include <boost/gil/gil_all.hpp>

using namespace std;
using namespace boost;
using namespace gil;

// Models a Unary Function
template <typename P>   // Models PixelValueConcept
struct mandelbrot_fn {
    typedef point2<std::ptrdiff_t>    point_t;

    typedef mandelbrot_fn        const_t;
    typedef P                    value_type;
    typedef value_type           reference;
    typedef value_type           const_reference;
    typedef point_t              argument_type;
    typedef reference            result_type;
    BOOST_STATIC_CONSTANT(bool, is_mutable=false);

    value_type                    _in_color,_out_color;
    point_t                       _img_size;
    static const int MAX_ITER=100;        // max number of iterations

    mandelbrot_fn() {}
    mandelbrot_fn(const point_t& sz, const value_type& in_color, const value_type& out_color) : _in_color(in_color), _out_color(out_color), _img_size(sz) {}

    std::ptrdiff_t width()  { return _img_size.x; }
    std::ptrdiff_t height() { return _img_size.y; }

    result_type operator()(const point_t& p) const {
        // normalize the coords to (-2..1, -1.5..1.5)
        // (actually make y -1.0..2 so it is asymmetric, so we can verify some view factory methods)
        double t=get_num_iter(point2<double>(p.x/(double)_img_size.x*3-2, p.y/(double)_img_size.y*3-1.0f));//1.5f));
        t=pow(t,0.2);

        value_type ret;
        for (int k=0; k<num_channels<P>::value; ++k)
            ret[k]=(typename channel_type<P>::type)(_in_color[k]*t + _out_color[k]*(1-t));
        return ret;
    }

private:
    double get_num_iter(const point2<double>& p) const {
        point2<double> Z(0,0);
        for (int i=0; i<MAX_ITER; ++i) {
            Z = point2<double>(Z.x*Z.x - Z.y*Z.y + p.x, 2*Z.x*Z.y + p.y);
            if (Z.x*Z.x + Z.y*Z.y > 4)
                return i/(double)MAX_ITER;
        }
        return 0;
    }
};

template< typename Pixel >
struct mandel_view
{
    typedef mandelbrot_fn< Pixel > deref_t;
    typedef virtual_2d_locator<deref_t,false> locator_t;
    typedef image_view<locator_t> my_virt_view_t;

    typedef my_virt_view_t type;
};

template< typename Pixel >
typename mandel_view< Pixel >::type create_mandel_view( unsigned int width
                                                      , unsigned int height
                                                      , const Pixel& in
                                                      , const Pixel& out
                                                      )
{
    typedef typename mandel_view< Pixel >::type      view_t;
    typedef typename mandel_view< Pixel >::deref_t   deref_t;
    typedef typename mandel_view< Pixel >::locator_t locator_t;

    point_t dims( width, height );
    return view_t( dims
                 , locator_t( point_t( 0, 0 )
                            , point_t( 1, 1 )
                            , deref_t( dims
                                     , in
                                     , out
                                     )
                            )
                 );
}

#endif // BOOST_GIL_IO_UNIT_TEST_MANDEL_HPP


#include "kernel_viscosity.hpp"

#include "kernel_test.hpp"

#include <boost/test/unit_test.hpp>

using namespace GooBalls;
using namespace d2;
using namespace Physics;

namespace utf = boost::unit_test;

typedef Viscosity KKernel;

BOOST_AUTO_TEST_CASE(viscosity_zero_border, *utf::tolerance(0.001)){
    KKernel k;
    testZeroBorder(k, 100);
}

// TODO: Viscosity kernel doesn't work
/*
BOOST_AUTO_TEST_CASE(viscosity_zero_border_gradient, *utf::tolerance(0.01)){
    KKernel k;
    testZeroBorderGradient(k, 100);
}

BOOST_AUTO_TEST_CASE(viscosity_normalization, *utf::tolerance(0.0001)){
    KKernel k;
    testNormalization(k, 100);
}
*/
BOOST_AUTO_TEST_CASE(viscosity_monotonicity, *utf::tolerance(0.0001)){
    KKernel k;
    testMonotonicity(k, 10);
}

BOOST_AUTO_TEST_CASE(viscosity_radial_symmetric){
    KKernel k;
    testRadialSymmetry(k, 100);
}

/*
BOOST_AUTO_TEST_CASE(viscosity_lap_grad_finite_diff, *utf::tolerance(0.011)){
    KKernel k;
    testLaplacianFromGradientFiniteDifferences(k, 100);
}

BOOST_AUTO_TEST_CASE(viscosity_grad_finite_diff, *utf::tolerance(0.01)){
    KKernel k;
    testGradientFiniteDifference(k, 100);
}

BOOST_AUTO_TEST_CASE(viscosity_lap_finite_diff, *utf::tolerance(0.00001)){
    KKernel k;
    testLaplacianFiniteDifference(k, 100);
}
*/


//
// Copyright © 2020 Arm Ltd. All rights reserved.
// SPDX-License-Identifier: MIT
//

#include <boost/test/unit_test.hpp>
#include "ParserFlatbuffersFixture.hpp"
#include "../TfLiteParser.hpp"

#include <string>
#include <iostream>

BOOST_AUTO_TEST_SUITE(TensorflowLiteParser)

struct DivFixture : public ParserFlatbuffersFixture
{
    explicit DivFixture(const std::string & inputShape1,
                        const std::string & inputShape2,
                        const std::string & outputShape,
                        const std::string & activation="NONE")
    {
        m_JsonString = R"(
            {
                "version": 3,
                "operator_codes": [ { "builtin_code": "DIV" } ],
                "subgraphs": [ {
                    "tensors": [
                        {
                            "shape": )" + inputShape1 + R"(,
                            "type": "FLOAT32",
                            "buffer": 0,
                            "name": "inputTensor1",
                            "quantization": {
                                "min": [ 0.0 ],
                                "max": [ 255.0 ],
                                "scale": [ 1.0 ],
                                "zero_point": [ 0 ],
                            }
                        },
                        {
                            "shape": )" + inputShape2 + R"(,
                            "type": "FLOAT32",
                            "buffer": 1,
                            "name": "inputTensor2",
                            "quantization": {
                                "min": [ 0.0 ],
                                "max": [ 255.0 ],
                                "scale": [ 1.0 ],
                                "zero_point": [ 0 ],
                            }
                        },
                        {
                            "shape": )" + outputShape + R"( ,
                            "type": "FLOAT32",
                            "buffer": 2,
                            "name": "outputTensor",
                            "quantization": {
                                "min": [ 0.0 ],
                                "max": [ 255.0 ],
                                "scale": [ 1.0 ],
                                "zero_point": [ 0 ],
                            }
                        }
                    ],
                    "inputs": [ 0, 1 ],
                    "outputs": [ 2 ],
                    "operators": [
                        {
                            "opcode_index": 0,
                            "inputs": [ 0, 1 ],
                            "outputs": [ 2 ],
                            "builtin_options_type": "DivOptions",
                            "builtin_options": {
                                "fused_activation_function": )" + activation + R"(
                            },
                            "custom_options_format": "FLEXBUFFERS"
                        }
                    ],
                } ],
                "buffers" : [
                    { },
                    { }
                ]
            }
        )";
        Setup();
    }
};

struct SimpleDivFixture : public DivFixture
{
    SimpleDivFixture() : DivFixture("[ 1, 2, 2, 3 ]", "[ 1, 2, 2, 3 ]", "[ 1, 2, 2, 3 ]") {}
};

BOOST_FIXTURE_TEST_CASE(ParseDiv, SimpleDivFixture)
{
    using armnn::DataType;
    float Inf = std::numeric_limits<float>::infinity();
    float NaN = std::numeric_limits<float>::quiet_NaN();

    RunTest<4, DataType::Float32>(0, {{ "inputTensor1", { 0.0f,  1.0f,  2.0f,
                                                          3.0f,  4.0f,  5.0f,
                                                          6.0f,  7.0f,  8.0f,
                                                          9.0f, 10.0f, -11.0f } },
                                      { "inputTensor2", { 0.0f,  0.0f,  4.0f,
                                                          3.0f,  40.0f,  5.0f,
                                                          6.0f,  7.0f,  8.0f,
                                                          9.0f,  10.0f,  11.0f} } },
                                     {{ "outputTensor", { NaN,   Inf,  0.5f,
                                                          1.0f,  0.1f, 1.0f,
                                                          1.0f,  1.0f, 1.0f,
                                                          1.0f,  1.0f, -1.0f } } });
}

struct DynamicDivFixture : public DivFixture
{
    DynamicDivFixture() : DivFixture("[ 1, 2, 2, 3 ]", "[ 1, 2, 2, 3 ]", "[  ]") {}
};

BOOST_FIXTURE_TEST_CASE(ParseDynamicDiv, DynamicDivFixture)
{
    using armnn::DataType;
    float Inf = std::numeric_limits<float>::infinity();
    float NaN = std::numeric_limits<float>::quiet_NaN();

    RunTest<4, DataType::Float32, DataType::Float32>(0, {{ "inputTensor1", { 0.0f,  1.0f,  2.0f,
                                                            3.0f,  4.0f,  5.0f,
                                                            6.0f,  7.0f,  8.0f,
                                                            9.0f, 10.0f, -11.0f } },
                                      { "inputTensor2", { 0.0f,  0.0f,  4.0f,
                                                            3.0f,  40.0f,  5.0f,
                                                            6.0f,  7.0f,  8.0f,
                                                            9.0f,  10.0f,  11.0f} } },
                                  {{ "outputTensor", { NaN,   Inf,  0.5f,
                                                         1.0f,  0.1f, 1.0f,
                                                         1.0f,  1.0f, 1.0f,
                                                         1.0f,  1.0f, -1.0f } } }, true);
}

BOOST_AUTO_TEST_SUITE_END()


#include <boost/numeric/odeint/stepper/explicit_error_generic_rk.hpp>


#pragma once
//===----------------------------------------------------------------------===//
#include <dtl/bitmap/iterator.hpp>
#include <dtl/dtl.hpp>
#include <dtl/math.hpp>

#include <boost/dynamic_bitset.hpp>

#include <iomanip>
#include <memory>
//===----------------------------------------------------------------------===//
namespace dtl {
//===----------------------------------------------------------------------===//
/// Applies a fixed size partitioning to the given bitmap.
template<
    /// The (compressed) bitmap type.
    typename B,
    /// The partition size in bits.
    std::size_t P>
class part {
  static_assert(dtl::is_power_of_two(P),
      "The partition size must be a power of two.");

protected:
  /// Pointer to the partitions.
  std::vector<std::unique_ptr<B>> parts_;
  /// The (total) length of the bitmap.
  std::size_t n_;

public:
  using bitmap_type = B;
  static constexpr std::size_t part_bitlength = P;

  /// C'tor (similar to all other implementations)
  explicit part(const boost::dynamic_bitset<$u32>& bitmap)
      : parts_(), n_(bitmap.size()) {
    const std::size_t part_cnt =
        (bitmap.size() + (part_bitlength - 1)) / part_bitlength;

    for (std::size_t p = 0; p < part_cnt; ++p) {
      // TODO avoid copy, or at least make it faster
      boost::dynamic_bitset<$u32> b(part_bitlength);
      const std::size_t p_begin = p * part_bitlength;
      const std::size_t p_end = std::min((p + 1) * part_bitlength, n_);
      auto i = (p_begin == 0)
          ? bitmap.find_first()
          : bitmap.find_next(p_begin - 1);
      while (i < p_end && i != boost::dynamic_bitset<$u32>::npos) {
        b[i % part_bitlength] = true;
        i = bitmap.find_next(i);
      }
      // Compress the current partition.
      auto compressed_part_ptr = std::make_unique<B>(b);
      parts_.push_back(std::move(compressed_part_ptr));
    }
  }

  part(const part& other) = delete;
  part(part&& other) noexcept = default;
  part& operator=(const part& other) = delete;
  part& operator=(part&& other) noexcept = default;

  /// Return the name of the implementation.
  static std::string
  name() noexcept {
    return std::string("part<")
        + B::name() + std::string(",")
        + std::to_string(dtl::log_2(part_bitlength))
        + std::string(">");
  }

  /// Returns the length of the original bitmap.
  std::size_t __forceinline__
  size() const noexcept {
    return n_;
  }

  /// Return the size in bytes.
  std::size_t __forceinline__
  size_in_bytes() const noexcept {
    std::size_t s = 0;
    for (std::size_t p = 0; p < parts_.size(); ++p) {
      const auto& part_ptr = parts_[p];
      s += part_ptr->size_in_bytes();
    }
    s += parts_.size() * sizeof(void*);
    return s;
  }

  /// Returns the name of the instance including the most important parameters
  /// in JSON.
  std::string
  info() const noexcept {
    return "{\"name\":\"" + name() + "\""
        + ",\"n\":" + std::to_string(n_)
        + ",\"part_cnt\":" + std::to_string(parts_.size())
        + ",\"size\":" + std::to_string(size_in_bytes())
        + "}";
  }

  /// For debugging purposes.
  void
  print(std::ostream& os) const noexcept {
    for (std::size_t i = 0; i < parts_.size(); ++i) {
      os << std::setw(4) << i << ": " << *parts_[i] << std::endl;
    }
  }

  //===--------------------------------------------------------------------===//
  // Read related functions.
  //===--------------------------------------------------------------------===//
  /// Returns the value of the bit at the given position.
  u1 __forceinline__
  test(const std::size_t pos) const noexcept {
    assert(pos < n_);
    const auto part_idx = pos / part_bitlength;
    //    if (part_idx >= parts_.size()) {
    //      return false;
    //    }
    const auto& part_ptr = parts_[part_idx];
    return part_ptr->test(pos % part_bitlength);
  }

  //===--------------------------------------------------------------------===//
  /// 1-run iterator for partitioned bitmaps.
  template<run_iterator_type iter_type>
  class iter {
    /// Reference to the outer instance.
    const part& part_bitmap_;

    /// The iterator type of the nested bitmap.
    using nested_iter_type =
        typename obtain_run_iterator<bitmap_type, iter_type>::type;

    // Hack to place nested iterators on heap memory.
    struct heap_iter {
      nested_iter_type iter;

      heap_iter(B* bitmap)
          : iter(obtain_run_iterator<bitmap_type, iter_type>::from(*bitmap)) {}
      heap_iter(const heap_iter& other) = delete;
      heap_iter(heap_iter&& other) noexcept = delete;
      heap_iter& operator=(const heap_iter& other) = delete;
      heap_iter& operator=(heap_iter&& other) noexcept = delete;
      ~heap_iter() = default;
    };

    //===------------------------------------------------------------------===//
    // Iterator state
    //===------------------------------------------------------------------===//
    /// The current partition.
    std::size_t current_part_idx_;
    /// The nested iterator (of the current partition).
    std::unique_ptr<heap_iter> part_iter_;
    /// Points to the beginning of a 1-fill.
    $u64 pos_;
    /// The length of the current 1-fill
    $u64 length_;
    //===------------------------------------------------------------------===//

  public:
    explicit iter(const part& part)
        : part_bitmap_(part),
          current_part_idx_(0),
          part_iter_(nullptr) {
      // Instantiate the nested iterator.
      while (true) {
        auto& current_part = part_bitmap_.parts_[current_part_idx_];
        assert(current_part != nullptr);
        part_iter_ = std::make_unique<heap_iter>(current_part.get());

        if (part_iter_->iter.end()) {
          // The current partition is empty. Continue with the next partition,
          // if there is any.
          ++current_part_idx_;
          if (current_part_idx_ == part_bitmap_.parts_.size()) {
            // The entire bitmap is 0.
            pos_ = part_bitmap_.n_;
            length_ = 0;
            return;
          }
          continue;
        }
        else {
          // Update the iterator state.
          pos_ = (current_part_idx_ * part_bitlength) + part_iter_->iter.pos();
          length_ = part_iter_->iter.length();
          return;
        }
      }
    }

    iter(iter&&) = default;

    /// Advance to the next partition if there is any.
    void __forceinline__
    next_part() { // TODO should be private
      part_iter_ = nullptr;
      while (true) {
        ++current_part_idx_;
        if (current_part_idx_ >= part_bitmap_.parts_.size()) {
          // Reached the end.
          pos_ = part_bitmap_.n_;
          length_ = 0;
          return;
        }

        auto& current_part = part_bitmap_.parts_[current_part_idx_];

        // Instantiate the iterator of the current partition.
        assert(current_part != nullptr);
        part_iter_ = std::make_unique<heap_iter>(current_part.get());
        if (part_iter_->iter.end()) {
          // The current partition is empty. Continue with the next partition,
          // if there is any.
          continue;
        }
        else {
          // Update the iterator state.
          pos_ = (current_part_idx_ * part_bitlength) + part_iter_->iter.pos();
          length_ = part_iter_->iter.length();
          return;
        }
      }
    }

    void __forceinline__
    next() {
      assert(!end());
      assert(part_iter_.get() != nullptr);
      assert(!part_iter_->iter.end());

      part_iter_->iter.next();
      if (!part_iter_->iter.end()) {
        // Update the iterator state.
        pos_ = (current_part_idx_ * part_bitlength) + part_iter_->iter.pos();
        length_ = part_iter_->iter.length();
        return;
      }

      // Advance to the next partition if there is any.
      next_part();
    }

    void __forceinline__
    skip_to(const std::size_t to_pos) {
      if (to_pos < (pos_ + length_)) {
        length_ -= to_pos - pos_;
        pos_ = to_pos;
        return;
      }

      const auto dst_part = to_pos / part_bitlength;
      if (dst_part != current_part_idx_) {
        assert(dst_part > 0);
        // Skip to the destination partition.
        current_part_idx_ = dst_part - 1;
        next_part();
        // Check if we have reached the end or if we skipped over the
        // destination position.
        if (end() || pos_ > to_pos) {
          return;
        }
      }
      assert(current_part_idx_ == (to_pos / part_bitlength));

      // Skip within the current partition, if necessary.
      if (pos_ < to_pos) {
        part_iter_->iter.skip_to(to_pos % part_bitlength);
        if (!part_iter_->iter.end()) {
          // Update the iterator state.
          pos_ = (current_part_idx_ * part_bitlength) + part_iter_->iter.pos();
          length_ = part_iter_->iter.length();
          return;
        }
        else {
          // We skipped over the destination partition because there was no
          // 1-run. Thus, we simply forward the iterator to the next partition
          // and return.
          next_part();
          return;
        }
      }
    }

    u1 __forceinline__
    end() const noexcept {
      return length_ == 0;
    }

    u64 __forceinline__
    pos() const noexcept {
      return pos_;
    }

    u64 __forceinline__
    length() const noexcept {
      return length_;
    }
  };

  using skip_iter_type = iter<run_iterator_type::SKIP>;
  using scan_iter_type = iter<run_iterator_type::SCAN>;

  skip_iter_type __forceinline__
  it() const {
    return skip_iter_type(*this);
  }

  scan_iter_type __forceinline__
  scan_it() const {
    return scan_iter_type(*this);
  }
  //===--------------------------------------------------------------------===//
};
//===----------------------------------------------------------------------===//
} // namespace dtl


// Copyright (c) 2016
// Author: Chrono Law
#include <std.hpp>
using namespace std;

#include <boost/iterator/counting_iterator.hpp>

#include <boost/iterator/transform_iterator.hpp>
using namespace boost;

///////////////////////////////////////

void case1()
{
    typedef counting_iterator<int> ci_t;

    vector<int> v;
    std::copy(ci_t(0), ci_t(10), back_inserter(v));

    using std::placeholders::_1;

    auto f = bind(plus<int>(), _1, 5);

    std::copy(
        make_transform_iterator(v.begin(), f),
        make_transform_iterator(v.end(), f),
        ostream_iterator<int>(cout, " "));
}

///////////////////////////////////////

int main()
{
    std::cout << "hello transform_iterator" << std::endl;

    case1();
}


#include <engine/Vectormath.hpp>
#include <engine/Manifoldmath.hpp>
#include <utility/Constants.hpp>
#include <utility/Logging.hpp>
#include <utility/Exception.hpp>

#include <Eigen/Dense>

#include <array>
#include <algorithm>

using namespace Utility;
using Utility::Constants::Pi;

#ifndef SPIRIT_USE_CUDA

namespace Engine
{
namespace Vectormath
{
    void get_random_vector(std::uniform_real_distribution<scalar> & distribution, std::mt19937 & prng, Vector3 & vec)
    {
        for (int dim = 0; dim < 3; ++dim)
        {
            vec[dim] = distribution(prng);
        }
    }
    void get_random_vectorfield(std::mt19937 & prng, vectorfield & xi)
    {
        // PRNG gives RN [-1,1] -> multiply with epsilon
        auto distribution = std::uniform_real_distribution<scalar>(-1, 1);
        // TODO: parallelization of this is actually not quite so trivial
        #pragma omp parallel for
        for (unsigned int i = 0; i < xi.size(); ++i)
        {
            get_random_vector(distribution, prng, xi[i]);
        }
    }

    void get_random_vector_unitsphere(std::uniform_real_distribution<scalar> & distribution, std::mt19937 & prng, Vector3 & vec)
    {
        scalar v_z = distribution(prng);
        scalar phi = distribution(prng)*Pi;

        scalar r_xy = std::sqrt(1 - v_z*v_z);

        vec[0] = r_xy * std::cos(phi);
        vec[1] = r_xy * std::sin(phi);
        vec[2] = v_z;
    }
    void get_random_vectorfield_unitsphere(std::mt19937 & prng, vectorfield & xi)
    {
        // PRNG gives RN [-1,1] -> multiply with epsilon
        auto distribution = std::uniform_real_distribution<scalar>(-1, 1);
        // TODO: parallelization of this is actually not quite so trivial
        #pragma omp parallel for
        for (unsigned int i = 0; i < xi.size(); ++i)
        {
            get_random_vector_unitsphere(distribution, prng, xi[i]);
        }
    }

    /////////////////////////////////////////////////////////////////

    void fill(scalarfield & sf, scalar s)
    {
        #pragma omp parallel for
        for (unsigned int i = 0; i<sf.size(); ++i)
            sf[i] = s;
    }
    void fill(scalarfield & sf, scalar s, const intfield & mask)
    {
        #pragma omp parallel for
        for (unsigned int i=0; i<sf.size(); ++i)
            sf[i] = mask[i]*s;
    }

    void scale(scalarfield & sf, scalar s)
    {
        #pragma omp parallel for
        for (unsigned int i = 0; i<sf.size(); ++i)
            sf[i] *= s;
    }

    void add(scalarfield & sf, scalar s)
    {
        #pragma omp parallel for
        for (unsigned int i = 0; i<sf.size(); ++i)
            sf[i] += s;
    }

    scalar sum(const scalarfield & sf)
    {
        scalar ret = 0;
        #pragma omp parallel for reduction(+:ret)
        for (unsigned int i = 0; i<sf.size(); ++i)
            ret += sf[i];
        return ret;
    }

    scalar mean(const scalarfield & sf)
    {
        scalar ret = sum(sf)/sf.size();
        return ret;
    }

    void set_range(scalarfield & sf, scalar sf_min, scalar sf_max)
    {
        #pragma omp parallel for
        for (unsigned int i = 0; i<sf.size(); ++i)
            sf[i] = std::min( std::max( sf_min, sf[i] ), sf_max );
    }

    void fill(vectorfield & vf, const Vector3 & v)
    {
        #pragma omp parallel for
        for (unsigned int i=0; i<vf.size(); ++i)
            vf[i] = v;
    }
    void fill(vectorfield & vf, const Vector3 & v, const intfield & mask)
    {
        #pragma omp parallel for
        for (unsigned int i=0; i<vf.size(); ++i)
            vf[i] = mask[i]*v;
    }

    void normalize_vectors(vectorfield & vf)
    {
        #pragma omp parallel for
        for (unsigned int i=0; i<vf.size(); ++i)
            vf[i].normalize();
    }

    void norm( const vectorfield & vf, scalarfield & norm )
    {
        for (unsigned int i=0; i<vf.size(); ++i)
            norm[i] = vf[i].norm();
    }

    std::pair<scalar, scalar> minmax_component(const vectorfield & v1)
    {
        scalar minval=1e6, maxval=-1e6;
        std::pair<scalar, scalar> minmax;
        #pragma omp parallel for reduction(min: minval) reduction(max : maxval)
        for (unsigned int i = 0; i < v1.size(); ++i)
        {
            for (int dim = 0; dim < 3; ++dim)
            {
                if (v1[i][dim] < minval) minval = v1[i][dim];
                if (v1[i][dim] > maxval) maxval = v1[i][dim];
            }
        }
        minmax.first = minval;
        minmax.second = maxval;
        return minmax;
    }

    scalar  max_abs_component(const vectorfield & vf)
    {
        // We want the Maximum of Absolute Values of all force components on all images
        scalar absmax = 0;
        // Find minimum and maximum values
        std::pair<scalar,scalar> minmax = minmax_component(vf);
        // Maximum of absolute values
        absmax = std::max(absmax, std::abs(minmax.first));
        absmax = std::max(absmax, std::abs(minmax.second));
        // Return
        return absmax;
    }

    void scale(vectorfield & vf, const scalar & sc)
    {
        #pragma omp parallel for
        for (unsigned int i=0; i<vf.size(); ++i)
            vf[i] *= sc;
    }

    void scale(vectorfield & vf, const scalarfield & sf, bool inverse)
    {
        if( inverse )
        {
            #pragma omp parallel for
            for (unsigned int i=0; i<vf.size(); ++i)
                vf[i] /= sf[i];
        }
        else
        {
            #pragma omp parallel for
            for (unsigned int i=0; i<vf.size(); ++i)
                vf[i] *= sf[i];
        }
    }

    Vector3 sum(const vectorfield & vf)
    {
        Vector3 ret = { 0,0,0 };
        #pragma omp parallel for reduction(+:ret)
        for (unsigned int i = 0; i<vf.size(); ++i)
            ret += vf[i];
        return ret;
    }

    Vector3 mean(const vectorfield & vf)
    {
        Vector3 ret = sum(vf)/vf.size();
        return ret;
    }

    void divide( const scalarfield & numerator, const scalarfield & denominator, scalarfield & out )
    {
        #pragma omp parallel for
        for (unsigned int i=0; i<out.size(); ++i)
            out[i] = numerator[i] / denominator[i];
    }

    // computes the inner product of two vectorfields v1 and v2
    scalar dot(const vectorfield & v1, const vectorfield & v2)
    {
        scalar ret = 0;
        #pragma omp parallel for reduction(+:ret)
        for (unsigned int i = 0; i<v1.size(); ++i)
            ret += v1[i].dot(v2[i]);
        return ret;
    }

    // computes the inner products of vectors in vf1 and vf2
    // vf1 and vf2 are vectorfields
    void dot(const vectorfield & vf1, const vectorfield & vf2, scalarfield & out)
    {
        #pragma omp parallel for
        for (unsigned int i=0; i<vf1.size(); ++i)
            out[i] = vf1[i].dot(vf2[i]);
    }

    // computes the product of scalars in s1 and s2
    // s1 and s2 are scalarfields
    void dot( const scalarfield & s1, const scalarfield & s2, scalarfield & out )
    {
        #pragma omp parallel for
        for (unsigned int i=0; i<s1.size(); i++)
            out[i] = s1[i] * s2[i];
    }

    // computes the vector (cross) products of vectors in v1 and v2
    // v1 and v2 are vector fields
    void cross(const vectorfield & v1, const vectorfield & v2, vectorfield & out)
    {
        #pragma omp parallel for
        for (unsigned int i=0; i<v1.size(); ++i)
            out[i] = v1[i].cross(v2[i]);
    }

    // out[i] += c*a
    void add_c_a(const scalar & c, const Vector3 & vec, vectorfield & out)
    {
        #pragma omp parallel for
        for(unsigned int idx = 0; idx < out.size(); ++idx)
            out[idx] += c*vec;
    }
    // out[i] += c*a[i]
    void add_c_a(const scalar & c, const vectorfield & vf, vectorfield & out)
    {
        #pragma omp parallel for
        for(unsigned int idx = 0; idx < out.size(); ++idx)
            out[idx] += c*vf[idx];
    }
    void add_c_a(const scalar & c, const vectorfield & vf, vectorfield & out, const intfield & mask)
    {
        #pragma omp parallel for
        for (unsigned int idx = 0; idx < out.size(); ++idx)
            out[idx] += mask[idx] * c*vf[idx];
    }
    // out[i] += c[i]*a[i]
    void add_c_a( const scalarfield & c, const vectorfield & vf, vectorfield & out )
    {
        #pragma omp parallel for
        for( unsigned int idx = 0; idx < out.size(); ++idx )
            out[idx] += c[idx] * vf[idx];
    }

    // out[i] = c*a
    void set_c_a(const scalar & c, const Vector3 & vec, vectorfield & out)
    {
        #pragma omp parallel for
        for(unsigned int idx = 0; idx < out.size(); ++idx)
            out[idx] = c*vec;
    }
    // out[i] = c*a
    void set_c_a(const scalar & c, const Vector3 & vec, vectorfield & out, const intfield & mask)
    {
        #pragma omp parallel for
        for(unsigned int idx = 0; idx < out.size(); ++idx)
            out[idx] = mask[idx]*c*vec;
    }

    // out[i] = c*a[i]
    void set_c_a(const scalar & c, const vectorfield & vf, vectorfield & out)
    {
        #pragma omp parallel for
        for(unsigned int idx = 0; idx < out.size(); ++idx)
            out[idx] = c*vf[idx];
    }
    // out[i] = c*a[i]
    void set_c_a(const scalar & c, const vectorfield & vf, vectorfield & out, const intfield & mask)
    {
        #pragma omp parallel for
        for(unsigned int idx = 0; idx < out.size(); ++idx)
            out[idx] = mask[idx] * c*vf[idx];
    }
    // out[i] = c[i]*a[i]
    void set_c_a( const scalarfield & c, const vectorfield & vf, vectorfield & out )
    {
        #pragma omp parallel for
        for( unsigned int idx=0; idx < out.size(); ++idx)
            out[idx] = c[idx] * vf[idx];
    }

    // out[i] += c * a*b[i]
    void add_c_dot(const scalar & c, const Vector3 & vec, const vectorfield & vf, scalarfield & out)
    {
        #pragma omp parallel for
        for(unsigned int idx = 0; idx < out.size(); ++idx)
            out[idx] += c*vec.dot(vf[idx]);
    }
    // out[i] += c * a[i]*b[i]
    void add_c_dot(const scalar & c, const vectorfield & vf1, const vectorfield & vf2, scalarfield & out)
    {
        #pragma omp parallel for
        for(unsigned int idx = 0; idx < out.size(); ++idx)
            out[idx] += c*vf1[idx].dot(vf2[idx]);
    }

    // out[i] = c * a*b[i]
    void set_c_dot(const scalar & c, const Vector3 & a, const vectorfield & b, scalarfield & out)
    {
        #pragma omp parallel for
        for(unsigned int idx = 0; idx < out.size(); ++idx)
            out[idx] = c*a.dot(b[idx]);
    }
    // out[i] = c * a[i]*b[i]
    void set_c_dot(const scalar & c, const vectorfield & a, const vectorfield & b, scalarfield & out)
    {
        #pragma omp parallel for
        for(unsigned int idx = 0; idx < out.size(); ++idx)
            out[idx] = c*a[idx].dot(b[idx]);
    }

    // out[i] += c * a x b[i]
    void add_c_cross(const scalar & c, const Vector3 & a, const vectorfield & b, vectorfield & out)
    {
        #pragma omp parallel for
        for(unsigned int idx = 0; idx < out.size(); ++idx)
            out[idx] += c*a.cross(b[idx]);
    }
    // out[i] += c * a[i] x b[i]
    void add_c_cross(const scalar & c, const vectorfield & a, const vectorfield & b, vectorfield & out)
    {
        #pragma omp parallel for
        for(unsigned int idx = 0; idx < out.size(); ++idx)
            out[idx] += c*a[idx].cross(b[idx]);
    }
    // out[i] += c[i] * a[i] x b[i]
    void add_c_cross(const scalarfield & c, const vectorfield & a, const vectorfield & b, vectorfield & out)
    {
        #pragma omp parallel for
        for (unsigned int idx = 0; idx < out.size(); ++idx)
            out[idx] += c[idx] * a[idx].cross(b[idx]);
    }

    // out[i] = c * a x b[i]
    void set_c_cross(const scalar & c, const Vector3 & a, const vectorfield & b, vectorfield & out)
    {
        #pragma omp parallel for
        for(unsigned int idx = 0; idx < out.size(); ++idx)
            out[idx] = c*a.cross(b[idx]);
    }
    // out[i] = c * a[i] x b[i]
    void set_c_cross(const scalar & c, const vectorfield & a, const vectorfield & b, vectorfield & out)
    {
        #pragma omp parallel for
        for(unsigned int idx = 0; idx < out.size(); ++idx)
            out[idx] = c*a[idx].cross(b[idx]);
    }

    scalar max_norm(const vectorfield & vf)
    {
        scalar max_norm = 0;
        #pragma omp parallel for reduction(max : max_norm)
        for(int i=0; i<vf.size(); i++)
        {
            max_norm = std::max(max_norm, vf[i].squaredNorm());
        }
        return sqrt(max_norm);
    }
}
}

#endif

namespace Engine
{
namespace Vectormath
{
    scalar angle(const Vector3 & v1, const Vector3 & v2)
    {
        scalar r = v1.dot(v2);
        // Prevent NaNs from occurring
        r = std::fmax(-1.0, std::fmin(1.0, r));
        // Angle
        return std::acos(r);
    }

    /////////////////////////////////////////////////////////////////
    scalar solid_angle_1(const Vector3 & v1, const Vector3 & v2, const Vector3 & v3)
    {
        // Get sign
        scalar pm = v1.dot(v2.cross(v3));
        if (pm != 0) pm /= std::abs(pm);

        // angle
        scalar solid_angle = ( 1 + v1.dot(v2) + v2.dot(v3) + v3.dot(v1) ) /
                            std::sqrt( 2 * (1+v1.dot(v2)) * (1+v2.dot(v3)) * (1+v3.dot(v1)) );
        if (solid_angle == 1)
            solid_angle = 0;
        else
            solid_angle = pm * 2 * std::acos(solid_angle);

        return solid_angle;
    }

    scalar solid_angle_2(const Vector3 & v1, const Vector3 & v2, const Vector3 & v3)
    {
        // Using the solid angle formula by Oosterom and Strackee (note we assume vectors to be normalized to 1)
        // https://en.wikipedia.org/wiki/Solid_angle#Tetrahedron

        scalar x = v1.dot(v2.cross(v3));
        scalar y = 1 + v1.dot(v2) + v1.dot(v3) + v2.dot(v3);
        scalar solid_angle = 2 * std::atan2( x , y );

        return solid_angle;
    }

    void rotate(const Vector3 & v, const Vector3 & axis, const scalar & angle, Vector3 & v_out)
    {
        v_out = v * std::cos(angle) + axis.cross(v) * std::sin(angle) +
                axis * axis.dot(v) * (1 - std::cos(angle));
    }

    // XXX: should we add test for that function since it's calling the already tested rotat()
    void rotate( const vectorfield & v, const vectorfield & axis, const scalarfield & angle,
                    vectorfield & v_out )
    {
        for( unsigned int i=0; i<v_out.size(); i++)
            rotate( v[i], axis[i], angle[i], v_out[i] );
    }

    Vector3 decompose(const Vector3 & v, const std::vector<Vector3> & basis)
    {
        Eigen::Ref<const Matrix3> A = Eigen::Map<const Matrix3>(basis[0].data());
        return A.colPivHouseholderQr().solve(v);
    }

    /////////////////////////////////////////////////////////////////

    std::array<scalar,3> Magnetization(const vectorfield & vf)
    {
        Vector3 vfmean = mean(vf);
        std::array<scalar, 3> M{vfmean[0], vfmean[1], vfmean[2]};
        return M;
    }

    scalar TopologicalCharge(const vectorfield & vf, const Data::Geometry & geometry, const intfield & boundary_conditions)
    {
        // This implementations assumes
        // 1. No basis atom lies outside the cell spanned by the basis vectors of the lattice
        // 2. The geometry is a plane in x and y and spanned by the first 2 basis_vectors of the lattice
        // 3. The first basis atom lies at (0,0)

        const auto & positions = geometry.positions;
        scalar charge = 0;

        // Compute Delaunay for unitcell + basis with neighbouring lattice sites in directions a, b, and a+b
        std::vector<Data::vector2_t> basis_cell_points(geometry.n_cell_atoms + 3);
        for(int i = 0; i < geometry.n_cell_atoms; i++)
        {
            basis_cell_points[i].x = double(positions[i][0]);
            basis_cell_points[i].y = double(positions[i][1]);
        }

        // To avoid cases where the basis atoms lie on the boundary of the convex hull the corners of the parallelogram
        // spanned by the lattice sites 0, a, b and a+b are stretched away from the center for the triangulation
        scalar stretch_factor = 0.1;

        // For the rare case where the first basis atoms does not lie at (0,0,0)
        Vector3 basis_offset = positions[0];

        Vector3 ta = geometry.lattice_constant * geometry.bravais_vectors[0];
        Vector3 tb = geometry.lattice_constant * geometry.bravais_vectors[1];
        Vector3 tc = geometry.lattice_constant * geometry.bravais_vectors[2];

        // basis_cell_points[0] coincides with the '0' lattice site (plus basis_offset)
        basis_cell_points[0].x -= stretch_factor * (ta + tb)[0];
        basis_cell_points[0].y -= stretch_factor * (ta + tb)[1];

        // a+b
        basis_cell_points[geometry.n_cell_atoms].x   = double((ta + tb + positions[0] + stretch_factor * (ta + tb))[0]);
        basis_cell_points[geometry.n_cell_atoms].y   = double((ta + tb + positions[0] + stretch_factor * (ta + tb))[1]);
        // b
        basis_cell_points[geometry.n_cell_atoms+1].x = double((tb + positions[0] - stretch_factor * (ta - tb))[0]);
        basis_cell_points[geometry.n_cell_atoms+1].y = double((tb + positions[0] - stretch_factor * (ta - tb))[1]);
        // a
        basis_cell_points[geometry.n_cell_atoms+2].x = double((ta + positions[0] + stretch_factor * (ta - tb))[0]);
        basis_cell_points[geometry.n_cell_atoms+2].y = double((ta + positions[0] + stretch_factor * (ta - tb))[1]);

        std::vector<Data::triangle_t> triangulation;
        triangulation = Data::compute_delaunay_triangulation_2D(basis_cell_points);

        for(Data::triangle_t tri : triangulation)
        {
            // Compute the sign of this triangle
            Vector3 triangle_normal;
            vectorfield tri_positions(3);
            for(int i=0; i<3; i++)
                tri_positions[i] = {scalar(basis_cell_points[tri[i]].x), scalar(basis_cell_points[tri[i]].y), scalar(0)};
            triangle_normal = (tri_positions[0]-tri_positions[1]).cross(tri_positions[0] - tri_positions[2]);
            triangle_normal.normalize();
            scalar sign = triangle_normal[2]/std::abs(triangle_normal[2]);

            // We try to apply the Delaunay triangulation at each bravais-lattice point
            // For each corner of the triangle we check wether it is "allowed" (which means either inside the simulation box or permitted by periodic boundary conditions)
            // Then we can add the top charge for all trios of spins connected by this triangle
            for(int b = 0; b < geometry.n_cells[1]; ++b)
            {
                for(int a = 0; a < geometry.n_cells[0]; ++a)
                {
                    std::array<Vector3, 3> tri_spins;
                    // bools to check wether it is allowed to take the next lattice site in direction a, b or a+b
                    bool a_next_allowed = (a+1 < geometry.n_cells[0] || boundary_conditions[0]);
                    bool b_next_allowed = (b+1 < geometry.n_cells[1] || boundary_conditions[1]);
                    bool valid_triangle = true;
                    for(int i = 0; i<3; ++i)
                    {
                        int idx;
                        if(tri[i] < geometry.n_cell_atoms) // tri[i] is an index of a basis atom, no wrap around can occur
                        {
                            idx = (tri[i] + a * geometry.n_cell_atoms + b * geometry.n_cell_atoms * geometry.n_cells[0]);
                        }
                        else if (tri[i] == geometry.n_cell_atoms + 2 && a_next_allowed) // Translation by a
                        {
                            idx = ((a + 1) % geometry.n_cells[0]) * geometry.n_cell_atoms + b * geometry.n_cell_atoms * geometry.n_cells[0];
                        }
                        else if (tri[i] == geometry.n_cell_atoms + 1 && b_next_allowed) // Translation by b
                        {
                            idx = a * geometry.n_cell_atoms + ((b + 1) % geometry.n_cells[1]) * geometry.n_cell_atoms * geometry.n_cells[0];
                        }
                        else if (tri[i] == geometry.n_cell_atoms && a_next_allowed && b_next_allowed) // Translation by a + b
                        {
                            idx = ((a + 1) % geometry.n_cells[0]) * geometry.n_cell_atoms + ((b + 1) % geometry.n_cells[1]) * geometry.n_cell_atoms * geometry.n_cells[0];
                        }
                        else // Translation not allowed, skip to next triangle
                        {
                            valid_triangle = false;
                            break;
                        }
                        tri_spins[i] = vf[idx];
                    }
                    if(valid_triangle)
                        charge += sign * solid_angle_2(tri_spins[0], tri_spins[1], tri_spins[2]);
                }
            }
        }
        return charge / (4*Pi);
    }

    void get_gradient_distribution(const Data::Geometry & geometry, Vector3 gradient_direction, scalar gradient_start, scalar gradient_inclination, scalarfield & distribution, scalar range_min, scalar range_max)
    {
        // Ensure a normalized direction vector
        gradient_direction.normalize();

        // Basic linear gradient distribution
        set_c_dot(gradient_inclination, gradient_direction, geometry.positions, distribution);

        // Get the minimum (i.e. starting point) of the distribution
        scalar bmin = geometry.bounds_min.dot(gradient_direction);
        scalar bmax = geometry.bounds_max.dot(gradient_direction);
        scalar dist_min = std::min(bmin, bmax);
        // Set the starting point
        add(distribution, gradient_start - gradient_inclination*dist_min);

        // Cut off negative values
        set_range(distribution, range_min, range_max);
    }

    void directional_gradient(const vectorfield & vf, const Data::Geometry & geometry, const intfield & boundary_conditions, const Vector3 & direction, vectorfield & gradient)
    {
        // std::cout << "start gradient" << std::endl;
        vectorfield translations = { { 0,0,0 }, { 0,0,0 }, { 0,0,0 } };
        auto& n_cells = geometry.n_cells;

        neighbourfield neigh;

        // TODO: calculate Neighbours outside iterations
        // Neighbours::get_Neighbours(geometry, neigh);

        // TODO: proper usage of neighbours
        // Hardcoded neighbours - for spin current in a rectangular lattice
        neigh = neighbourfield(0);
        Neighbour neigh_tmp;
        neigh_tmp.i = 0;
        neigh_tmp.j = 0;
        neigh_tmp.idx_shell = 0;

        neigh_tmp.translations[0] = 1;
        neigh_tmp.translations[1] = 0;
        neigh_tmp.translations[2] = 0;
        neigh.push_back(neigh_tmp);

        neigh_tmp.translations[0] = -1;
        neigh_tmp.translations[1] = 0;
        neigh_tmp.translations[2] = 0;
        neigh.push_back(neigh_tmp);

        neigh_tmp.translations[0] = 0;
        neigh_tmp.translations[1] = 1;
        neigh_tmp.translations[2] = 0;
        neigh.push_back(neigh_tmp);

        neigh_tmp.translations[0] = 0;
        neigh_tmp.translations[1] = -1;
        neigh_tmp.translations[2] = 0;
        neigh.push_back(neigh_tmp);

        neigh_tmp.translations[0] = 0;
        neigh_tmp.translations[1] = 0;
        neigh_tmp.translations[2] = 1;
        neigh.push_back(neigh_tmp);

        neigh_tmp.translations[0] = 0;
        neigh_tmp.translations[1] = 0;
        neigh_tmp.translations[2] = -1;
        neigh.push_back(neigh_tmp);

        // Loop over vectorfield
        for(unsigned int ispin = 0; ispin < vf.size(); ++ispin)
        {
            auto translations_i = translations_from_idx(n_cells, geometry.n_cell_atoms, ispin); // transVec of spin i
            // int k = i%geometry.n_cell_atoms; // index within unit cell - k=0 for all cases used in the thesis
            scalar n = 0;

            gradient[ispin].setZero();

            std::vector<Vector3> euclidean { {1,0,0}, {0,1,0}, {0,0,1} };
            std::vector<Vector3> contrib = { {0, 0, 0}, {0, 0, 0}, {0, 0, 0} };
            Vector3 proj = {0, 0, 0};
            Vector3 projection_inv = {0, 0, 0};

            // TODO: both loops together.

            // Loop over neighbours of this vector to calculate contributions of finite differences to current direction
            for( unsigned int j = 0; j < neigh.size(); ++j )
            {
                if( boundary_conditions_fulfilled(geometry.n_cells, boundary_conditions, translations_i, neigh[j].translations) )
                {
                    // Index of neighbour
                    int ineigh = idx_from_translations(n_cells, geometry.n_cell_atoms, translations_i, neigh[j].translations);
                    if( ineigh >= 0 )
                    {
                        auto d = geometry.positions[ineigh] - geometry.positions[ispin];
                        for( int dim=0; dim<3; ++dim )
                        {
                            proj[dim] += std::abs(euclidean[dim].dot(d.normalized()));
                        }
                    }
                }
            }
            for( int dim=0; dim<3; ++dim )
            {
                if( std::abs(proj[dim]) > 1e-10 )
                    projection_inv[dim] = 1.0/proj[dim];
            }
            // Loop over neighbours of this vector to calculate finite differences
            for( unsigned int j = 0; j < neigh.size(); ++j )
            {
                if( boundary_conditions_fulfilled(geometry.n_cells, boundary_conditions, translations_i, neigh[j].translations) )
                {
                    // Index of neighbour
                    int ineigh = idx_from_translations(n_cells, geometry.n_cell_atoms, translations_i, neigh[j].translations);
                    if( ineigh >= 0 )
                    {
                        auto d = geometry.positions[ineigh] - geometry.positions[ispin];
                        for( int dim=0; dim<3; ++dim )
                        {
                            contrib[dim] += euclidean[dim].dot(d) / d.dot(d) * ( vf[ineigh] - vf[ispin] );
                        }
                    }
                }
            }

            for( int dim=0; dim<3; ++dim )
            {
                gradient[ispin] += direction[dim]*projection_inv[dim] * contrib[dim];
            }
        }
    }
}
}