#ifndef SPIRIT_USE_CUDA

#include <engine/Hamiltonian_Heisenberg.hpp>
#include <engine/Vectormath.hpp>
#include <engine/Neighbours.hpp>
#include <data/Spin_System.hpp>
#include <utility/Constants.hpp>

#include <Eigen/Dense>

using namespace Data;
using namespace Utility;
using Utility::Constants::mu_B;
using Utility::Constants::mu_0;
using Utility::Constants::Pi;
using Engine::Vectormath::check_atom_type;
using Engine::Vectormath::idx_from_pair;

namespace Engine
{
    // Construct a Heisenberg Hamiltonian with pairs
    Hamiltonian_Heisenberg::Hamiltonian_Heisenberg(
        scalarfield mu_s,
        scalar external_field_magnitude, Vector3 external_field_normal,
        intfield anisotropy_indices, scalarfield anisotropy_magnitudes, vectorfield anisotropy_normals,
        pairfield exchange_pairs, scalarfield exchange_magnitudes,
        pairfield dmi_pairs, scalarfield dmi_magnitudes, vectorfield dmi_normals,
        scalar ddi_radius,
        quadrupletfield quadruplets, scalarfield quadruplet_magnitudes,
        std::shared_ptr<Data::Geometry> geometry,
        intfield boundary_conditions
    ) :
        Hamiltonian(boundary_conditions),
        geometry(geometry),
        mu_s(mu_s),
        external_field_magnitude(external_field_magnitude * mu_B), external_field_normal(external_field_normal),
        anisotropy_indices(anisotropy_indices), anisotropy_magnitudes(anisotropy_magnitudes), anisotropy_normals(anisotropy_normals),
        exchange_pairs_in(exchange_pairs), exchange_magnitudes_in(exchange_magnitudes), exchange_shell_magnitudes(0),
        dmi_pairs_in(dmi_pairs), dmi_magnitudes_in(dmi_magnitudes), dmi_normals_in(dmi_normals), dmi_shell_magnitudes(0), dmi_shell_chirality(0),
        quadruplets(quadruplets), quadruplet_magnitudes(quadruplet_magnitudes),
        ddi_cutoff_radius(ddi_radius)
    {
        // Generate interaction pairs, constants etc.
        this->Update_Interactions();
    }

    // Construct a Heisenberg Hamiltonian from shells
    Hamiltonian_Heisenberg::Hamiltonian_Heisenberg(
        scalarfield mu_s,
        scalar external_field_magnitude, Vector3 external_field_normal,
        intfield anisotropy_indices, scalarfield anisotropy_magnitudes, vectorfield anisotropy_normals,
        scalarfield exchange_shell_magnitudes,
        scalarfield dmi_shell_magnitudes, int dm_chirality,
        scalar ddi_radius,
        quadrupletfield quadruplets, scalarfield quadruplet_magnitudes,
        std::shared_ptr<Data::Geometry> geometry,
        intfield boundary_conditions
    ) :
        Hamiltonian(boundary_conditions),
        geometry(geometry),
        mu_s(mu_s),
        external_field_magnitude(external_field_magnitude * mu_B), external_field_normal(external_field_normal),
        anisotropy_indices(anisotropy_indices), anisotropy_magnitudes(anisotropy_magnitudes), anisotropy_normals(anisotropy_normals),
        exchange_pairs_in(0), exchange_magnitudes_in(0), exchange_shell_magnitudes(exchange_shell_magnitudes),
        dmi_pairs_in(0), dmi_magnitudes_in(0), dmi_normals_in(0), dmi_shell_magnitudes(dmi_shell_magnitudes), dmi_shell_chirality(dm_chirality),
        quadruplets(quadruplets), quadruplet_magnitudes(quadruplet_magnitudes),
        ddi_cutoff_radius(ddi_radius)
    {
        // Generate interaction pairs, constants etc.
        this->Update_Interactions();
    }

    void Hamiltonian_Heisenberg::Update_Interactions()
    {
        #if defined(SPIRIT_USE_OPENMP)
        // When parallelising (cuda or openmp), we need all neighbours per spin
        const bool use_redundant_neighbours = true;
        #else
        // When running on a single thread, we can ignore redundant neighbours
        const bool use_redundant_neighbours = false;
        #endif

        // Exchange
        this->exchange_pairs      = pairfield(0);
        this->exchange_magnitudes = scalarfield(0);
        if( exchange_shell_magnitudes.size() > 0 )
        {
            // Generate Exchange neighbours
            intfield exchange_shells(0);
            Neighbours::Get_Neighbours_in_Shells(*geometry, exchange_shell_magnitudes.size(), exchange_pairs, exchange_shells, use_redundant_neighbours);
            for (unsigned int ipair = 0; ipair < exchange_pairs.size(); ++ipair)
            {
                this->exchange_magnitudes.push_back(exchange_shell_magnitudes[exchange_shells[ipair]]);
            }
        }
        else
        {
            // Use direct list of pairs
            this->exchange_pairs      = this->exchange_pairs_in;
            this->exchange_magnitudes = this->exchange_magnitudes_in;
            if( use_redundant_neighbours )
            {
                for (int i = 0; i < exchange_pairs_in.size(); ++i)
                {
                    auto& p = exchange_pairs_in[i];
                    auto& t = p.translations;
                    this->exchange_pairs.push_back(Pair{p.j, p.i, {-t[0], -t[1], -t[2]}});
                    this->exchange_magnitudes.push_back(exchange_magnitudes_in[i]);
                }
            }
        }

        // DMI
        this->dmi_pairs      = pairfield(0);
        this->dmi_magnitudes = scalarfield(0);
        this->dmi_normals    = vectorfield(0);
        if( dmi_shell_magnitudes.size() > 0 )
        {
            // Generate DMI neighbours and normals
            intfield dmi_shells(0);
            Neighbours::Get_Neighbours_in_Shells(*geometry, dmi_shell_magnitudes.size(), dmi_pairs, dmi_shells, use_redundant_neighbours);
            for (unsigned int ineigh = 0; ineigh < dmi_pairs.size(); ++ineigh)
            {
                this->dmi_normals.push_back(Neighbours::DMI_Normal_from_Pair(*geometry, dmi_pairs[ineigh], this->dmi_shell_chirality));
                this->dmi_magnitudes.push_back(dmi_shell_magnitudes[dmi_shells[ineigh]]);
            }
        }
        else
        {
            // Use direct list of pairs
            this->dmi_pairs      = this->dmi_pairs_in;
            this->dmi_magnitudes = this->dmi_magnitudes_in;
            this->dmi_normals    = this->dmi_normals_in;
            if( use_redundant_neighbours )
            {
                for (int i = 0; i < dmi_pairs_in.size(); ++i)
                {
                    auto& p = dmi_pairs_in[i];
                    auto& t = p.translations;
                    this->dmi_pairs.push_back(Pair{p.j, p.i, {-t[0], -t[1], -t[2]}});
                    this->dmi_magnitudes.push_back(dmi_magnitudes_in[i]);
                    this->dmi_normals.push_back(-dmi_normals_in[i]);
                }
            }
        }

        // Dipole-dipole
        this->ddi_pairs      = Engine::Neighbours::Get_Pairs_in_Radius(*this->geometry, this->ddi_cutoff_radius);
        this->ddi_magnitudes = scalarfield(this->ddi_pairs.size());
        this->ddi_normals    = vectorfield(this->ddi_pairs.size());

        scalar magnitude;
        Vector3 normal;

        for (unsigned int i = 0; i < this->ddi_pairs.size(); ++i)
        {
            Engine::Neighbours::DDI_from_Pair(
                *this->geometry,
                { this->ddi_pairs[i].i, this->ddi_pairs[i].j, this->ddi_pairs[i].translations },
                this->ddi_magnitudes[i], this->ddi_normals[i]);
        }

        // Update, which terms still contribute
        this->Update_Energy_Contributions();
    }

    void Hamiltonian_Heisenberg::Update_Energy_Contributions()
    {
        this->energy_contributions_per_spin = std::vector<std::pair<std::string, scalarfield>>(0);

        // External field
        if (this->external_field_magnitude > 0)
        {
            this->energy_contributions_per_spin.push_back({"Zeeman", scalarfield(0)});
            this->idx_zeeman = this->energy_contributions_per_spin.size()-1;
        }
        else this->idx_zeeman = -1;
        // Anisotropy
        if (this->anisotropy_indices.size() > 0)
        {
            this->energy_contributions_per_spin.push_back({"Anisotropy", scalarfield(0) });
            this->idx_anisotropy = this->energy_contributions_per_spin.size()-1;
        }
        else this->idx_anisotropy = -1;
        // Exchange
        if (this->exchange_pairs.size() > 0)
        {
            this->energy_contributions_per_spin.push_back({"Exchange", scalarfield(0) });
            this->idx_exchange = this->energy_contributions_per_spin.size()-1;
        }
        else this->idx_exchange = -1;
        // DMI
        if (this->dmi_pairs.size() > 0)
        {
            this->energy_contributions_per_spin.push_back({"DMI", scalarfield(0) });
            this->idx_dmi = this->energy_contributions_per_spin.size()-1;
        }
        else this->idx_dmi = -1;
        // Dipole-Dipole
        if (this->ddi_pairs.size() > 0)
        {
            this->energy_contributions_per_spin.push_back({"DD", scalarfield(0) });
            this->idx_ddi = this->energy_contributions_per_spin.size()-1;
        }
        else this->idx_ddi = -1;
        // Quadruplets
        if (this->quadruplets.size() > 0)
        {
            this->energy_contributions_per_spin.push_back({"Quadruplets", scalarfield(0) });
            this->idx_quadruplet = this->energy_contributions_per_spin.size()-1;
        }
        else this->idx_quadruplet = -1;
    }

    void Hamiltonian_Heisenberg::Energy_Contributions_per_Spin(const vectorfield & spins, std::vector<std::pair<std::string, scalarfield>> & contributions)
    {
        if (contributions.size() != this->energy_contributions_per_spin.size())
        {
            contributions = this->energy_contributions_per_spin;
        }

        int nos = spins.size();
        for (auto& contrib : contributions)
        {
            // Allocate if not already allocated
            if (contrib.second.size() != nos) contrib.second = scalarfield(nos, 0);
            // Otherwise set to zero
            else Vectormath::fill(contrib.second, 0);
        }

        // External field
        if (this->idx_zeeman >=0 )     E_Zeeman(spins, contributions[idx_zeeman].second);

        // Anisotropy
        if (this->idx_anisotropy >=0 ) E_Anisotropy(spins, contributions[idx_anisotropy].second);

        // Exchange
        if (this->idx_exchange >=0 )   E_Exchange(spins, contributions[idx_exchange].second);
        // DMI
        if (this->idx_dmi >=0 )        E_DMI(spins,contributions[idx_dmi].second);
        // DD
        if (this->idx_ddi >=0 )        E_DDI(spins, contributions[idx_ddi].second);
        // Quadruplets
        if (this->idx_quadruplet >=0 ) E_Quadruplet(spins, contributions[idx_quadruplet].second);
    }

    void Hamiltonian_Heisenberg::E_Zeeman(const vectorfield & spins, scalarfield & Energy)
    {
        const int N = geometry->n_cell_atoms;

        #pragma omp parallel for
        for (int icell = 0; icell < geometry->n_cells_total; ++icell)
        {
            for (int ibasis = 0; ibasis < N; ++ibasis)
            {
                int ispin = icell*N + ibasis;
                if (check_atom_type(this->geometry->atom_types[ispin]))
                    Energy[ispin] -= this->mu_s[ibasis] * this->external_field_magnitude * this->external_field_normal.dot(spins[ispin]);
            }
        }
    }

    void Hamiltonian_Heisenberg::E_Anisotropy(const vectorfield & spins, scalarfield & Energy)
    {
        const int N = geometry->n_cell_atoms;

        #pragma omp parallel for
        for (int icell = 0; icell < geometry->n_cells_total; ++icell)
        {
            for (int iani = 0; iani < anisotropy_indices.size(); ++iani)
            {
                int ispin = icell*N + anisotropy_indices[iani];
                if (check_atom_type(this->geometry->atom_types[ispin]))
                    Energy[ispin] -= this->anisotropy_magnitudes[iani] * std::pow(anisotropy_normals[iani].dot(spins[ispin]), 2.0);
            }
        }
    }

    void Hamiltonian_Heisenberg::E_Exchange(const vectorfield & spins, scalarfield & Energy)
    {
        #pragma omp parallel for
        for (unsigned int icell = 0; icell < geometry->n_cells_total; ++icell)
        {
            for (unsigned int i_pair = 0; i_pair < exchange_pairs.size(); ++i_pair)
            {
                int ispin = exchange_pairs[i_pair].i + icell*geometry->n_cell_atoms;
                int jspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, exchange_pairs[i_pair]);
                if (jspin >= 0)
                {
                    Energy[ispin] -= 0.5 * exchange_magnitudes[i_pair] * spins[ispin].dot(spins[jspin]);
                    #ifndef _OPENMP
                    Energy[jspin] -= 0.5 * exchange_magnitudes[i_pair] * spins[ispin].dot(spins[jspin]);
                    #endif
                }
            }
        }
    }

    void Hamiltonian_Heisenberg::E_DMI(const vectorfield & spins, scalarfield & Energy)
    {
        #pragma omp parallel for
        for (unsigned int icell = 0; icell < geometry->n_cells_total; ++icell)
        {
            for (unsigned int i_pair = 0; i_pair < dmi_pairs.size(); ++i_pair)
            {
                int ispin = dmi_pairs[i_pair].i + icell*geometry->n_cell_atoms;
                int jspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, dmi_pairs[i_pair]);
                if (jspin >= 0)
                {
                    Energy[ispin] -= 0.5 * dmi_magnitudes[i_pair] * dmi_normals[i_pair].dot(spins[ispin].cross(spins[jspin]));
                    #ifndef _OPENMP
                    Energy[jspin] -= 0.5 * dmi_magnitudes[i_pair] * dmi_normals[i_pair].dot(spins[ispin].cross(spins[jspin]));
                    #endif
                }
            }
        }
    }

    void Hamiltonian_Heisenberg::E_DDI(const vectorfield & spins, scalarfield & Energy)
    {
        // The translations are in angstr�m, so the |r|[m] becomes |r|[m]*10^-10
        const scalar mult = mu_0 * std::pow(mu_B, 2) / ( 4*Pi * 1e-30 );

        scalar result = 0.0;

        for (unsigned int i_pair = 0; i_pair < ddi_pairs.size(); ++i_pair)
        {
            if (ddi_magnitudes[i_pair] > 0.0)
            {
                for (int da = 0; da < geometry->n_cells[0]; ++da)
                {
                    for (int db = 0; db < geometry->n_cells[1]; ++db)
                    {
                        for (int dc = 0; dc < geometry->n_cells[2]; ++dc)
                        {
                            std::array<int, 3 > translations = { da, db, dc };
                            int i = ddi_pairs[i_pair].i;
                            int j = ddi_pairs[i_pair].j;
                            int ispin = i + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations);
                            int jspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, ddi_pairs[i_pair]);
                            if (jspin >= 0)
                            {
                                Energy[ispin] -= 0.5 * this->mu_s[i] * this->mu_s[j] * mult / std::pow(ddi_magnitudes[i_pair], 3.0) *
                                    (3 * spins[ispin].dot(ddi_normals[i_pair]) * spins[ispin].dot(ddi_normals[i_pair]) - spins[ispin].dot(spins[ispin]));
                                Energy[jspin] -= 0.5 * this->mu_s[i] * this->mu_s[j] * mult / std::pow(ddi_magnitudes[i_pair], 3.0) *
                                    (3 * spins[ispin].dot(ddi_normals[i_pair]) * spins[ispin].dot(ddi_normals[i_pair]) - spins[ispin].dot(spins[ispin]));
                            }
                        }
                    }
                }
            }
        }
    }// end DipoleDipole

    void Hamiltonian_Heisenberg::E_Quadruplet(const vectorfield & spins, scalarfield & Energy)
    {
        for (unsigned int iquad = 0; iquad < quadruplets.size(); ++iquad)
        {
            for (int da = 0; da < geometry->n_cells[0]; ++da)
            {
                for (int db = 0; db < geometry->n_cells[1]; ++db)
                {
                    for (int dc = 0; dc < geometry->n_cells[2]; ++dc)
                    {
                        std::array<int, 3 > translations = { da, db, dc };
                        int ispin = quadruplets[iquad].i + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations);
                        int jspin = quadruplets[iquad].j + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_j);
                        int kspin = quadruplets[iquad].k + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_k);
                        int lspin = quadruplets[iquad].l + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_l);

                        if ( check_atom_type(this->geometry->atom_types[ispin]) && check_atom_type(this->geometry->atom_types[jspin]) &&
                                check_atom_type(this->geometry->atom_types[kspin]) && check_atom_type(this->geometry->atom_types[lspin]) )
                        {
                            Energy[ispin] -= 0.25*quadruplet_magnitudes[iquad] * (spins[ispin].dot(spins[jspin])) * (spins[kspin].dot(spins[lspin]));
                            Energy[jspin] -= 0.25*quadruplet_magnitudes[iquad] * (spins[ispin].dot(spins[jspin])) * (spins[kspin].dot(spins[lspin]));
                            Energy[kspin] -= 0.25*quadruplet_magnitudes[iquad] * (spins[ispin].dot(spins[jspin])) * (spins[kspin].dot(spins[lspin]));
                            Energy[lspin] -= 0.25*quadruplet_magnitudes[iquad] * (spins[ispin].dot(spins[jspin])) * (spins[kspin].dot(spins[lspin]));
                        }
                    }
                }
            }
        }
    }

    scalar Hamiltonian_Heisenberg::Energy_Single_Spin(int ispin_in, const vectorfield & spins)
    {
        int icell  = ispin_in / this->geometry->n_cell_atoms;
        int ibasis = ispin_in - icell*this->geometry->n_cell_atoms;
        scalar Energy = 0;

        // External field
        if (this->idx_zeeman >= 0)
        {
            if (check_atom_type(this->geometry->atom_types[ispin_in]))
                Energy -= this->mu_s[ibasis] * this->external_field_magnitude * this->external_field_normal.dot(spins[ispin_in]);
        }

        // Anisotropy
        if (this->idx_anisotropy >= 0)
        {
            for (int iani = 0; iani < anisotropy_indices.size(); ++iani)
            {
                if (anisotropy_indices[iani] == ibasis)
                {
                    if (check_atom_type(this->geometry->atom_types[ispin_in]))
                        Energy -= this->anisotropy_magnitudes[iani] * std::pow(anisotropy_normals[iani].dot(spins[ispin_in]), 2.0);
                }
            }
        }

        // Exchange
        if (this->idx_exchange >= 0)
        {
            for (unsigned int ipair = 0; ipair < exchange_pairs.size(); ++ipair)
            {
                if (exchange_pairs[ipair].i == ibasis)
                {
                    int ispin = exchange_pairs[ipair].i + icell*geometry->n_cell_atoms;
                    int jspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, exchange_pairs[ipair]);
                    if (jspin >= 0)
                    {
                        Energy -= 0.5 * this->exchange_magnitudes[ipair] * spins[ispin].dot(spins[jspin]);
                    }
                    #ifndef _OPENMP
                    jspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, exchange_pairs[ipair], true);
                    if (jspin >= 0)
                    {
                        Energy -= 0.5 * this->exchange_magnitudes[ipair] * spins[ispin].dot(spins[jspin]);
                    }
                    #endif
                }
            }
        }

        // DMI
        if (this->idx_dmi >= 0)
        {
            for (unsigned int ipair = 0; ipair < dmi_pairs.size(); ++ipair)
            {
                if (dmi_pairs[ipair].i == ibasis)
                {
                    int ispin = dmi_pairs[ipair].i + icell*geometry->n_cell_atoms;
                    int jspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, dmi_pairs[ipair]);
                    if (jspin >= 0)
                    {
                        Energy -= 0.5 * this->dmi_magnitudes[ipair] * this->dmi_normals[ipair].dot(spins[ispin].cross(spins[jspin]));
                    }
                    #ifndef _OPENMP
                    jspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, dmi_pairs[ipair], true);
                    if (jspin >= 0)
                    {
                        Energy += 0.5 * this->dmi_magnitudes[ipair] * this->dmi_normals[ipair].dot(spins[ispin].cross(spins[jspin]));
                    }
                    #endif
                }
            }
        }

        // DDI
        if (this->idx_ddi >= 0)
        {
            for (unsigned int ipair = 0; ipair < ddi_pairs.size(); ++ipair)
            {
                if (ddi_pairs[ipair].i == ibasis)
                {
                    // The translations are in angstr�m, so the |r|[m] becomes |r|[m]*10^-10
                    const scalar mult = 0.5 * this->mu_s[ddi_pairs[ipair].i] * this->mu_s[ddi_pairs[ipair].j]
                        * Utility::Constants::mu_0 * std::pow(Utility::Constants::mu_B, 2) / ( 4*Utility::Constants::Pi * 1e-30 );

                    int ispin = ddi_pairs[ipair].i + icell*geometry->n_cell_atoms;
                    int jspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, ddi_pairs[ipair]);

                    if (jspin >= 0)
                    {
                        Energy -= mult / std::pow(this->ddi_magnitudes[ipair], 3.0) *
                            (3 * spins[ispin].dot(this->ddi_normals[ipair]) * spins[ispin].dot(this->ddi_normals[ipair]) - spins[ispin].dot(spins[ispin]));

                    }
                    #ifndef _OPENMP
                    jspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, dmi_pairs[ipair], true);
                    if (jspin >= 0)
                    {
                        Energy += mult / std::pow(this->ddi_magnitudes[ipair], 3.0) *
                            (3 * spins[ispin].dot(this->ddi_normals[ipair]) * spins[ispin].dot(this->ddi_normals[ipair]) - spins[ispin].dot(spins[ispin]));
                    }
                    #endif
                }
            }
        }

        // Quadruplets
        if (this->idx_quadruplet >= 0)
        {
            for (unsigned int iquad = 0; iquad < quadruplets.size(); ++iquad)
            {
                auto translations = Vectormath::translations_from_idx(geometry->n_cells, geometry->n_cell_atoms, icell);
                int ispin = quadruplets[iquad].i + icell*geometry->n_cell_atoms;
                int jspin = quadruplets[iquad].j + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_j);
                int kspin = quadruplets[iquad].k + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_k);
                int lspin = quadruplets[iquad].l + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_l);

                if ( check_atom_type(this->geometry->atom_types[ispin]) && check_atom_type(this->geometry->atom_types[jspin]) &&
                     check_atom_type(this->geometry->atom_types[kspin]) && check_atom_type(this->geometry->atom_types[lspin]) )
                {
                    Energy -= 0.25*quadruplet_magnitudes[iquad] * (spins[ispin].dot(spins[jspin])) * (spins[kspin].dot(spins[lspin]));
                }

                #ifndef _OPENMP
                // TODO: mirrored quadruplet when unique quadruplets are used
                // jspin = quadruplets[iquad].j + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_j, true);
                // kspin = quadruplets[iquad].k + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_k, true);
                // lspin = quadruplets[iquad].l + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_l, true);

                // if ( check_atom_type(this->geometry->atom_types[ispin]) && check_atom_type(this->geometry->atom_types[jspin]) &&
                //      check_atom_type(this->geometry->atom_types[kspin]) && check_atom_type(this->geometry->atom_types[lspin]) )
                // {
                //     Energy -= 0.25*quadruplet_magnitudes[iquad] * (spins[ispin].dot(spins[jspin])) * (spins[kspin].dot(spins[lspin]));
                // }
                #endif
            }
        }

        return Energy;
    }

    void Hamiltonian_Heisenberg::Gradient(const vectorfield & spins, vectorfield & gradient)
    {
        // Set to zero
        Vectormath::fill(gradient, {0,0,0});

        // External field
        Gradient_Zeeman(gradient);

        // Anisotropy
        Gradient_Anisotropy(spins, gradient);

        // Exchange
        this->Gradient_Exchange(spins, gradient);
        // DMI
        this->Gradient_DMI(spins, gradient);
        // DD
        this->Gradient_DDI(spins, gradient);

        // Quadruplets
        this->Gradient_Quadruplet(spins, gradient);
    }

    void Hamiltonian_Heisenberg::Gradient_Zeeman(vectorfield & gradient)
    {
        const int N = geometry->n_cell_atoms;

        #pragma omp parallel for
        for (int icell = 0; icell < geometry->n_cells_total; ++icell)
        {
            for (int ibasis = 0; ibasis < N; ++ibasis)
            {
                int ispin = icell*N + ibasis;
                if (check_atom_type(this->geometry->atom_types[ispin]))
                    gradient[ispin] -= this->mu_s[ibasis] * this->external_field_magnitude * this->external_field_normal;
            }
        }
    }

    void Hamiltonian_Heisenberg::Gradient_Anisotropy(const vectorfield & spins, vectorfield & gradient)
    {
        const int N = geometry->n_cell_atoms;

        #pragma omp parallel for
        for (int icell = 0; icell < geometry->n_cells_total; ++icell)
        {
            for (int iani = 0; iani < anisotropy_indices.size(); ++iani)
            {
                int ispin = icell*N + anisotropy_indices[iani];
                if (check_atom_type(this->geometry->atom_types[ispin]))
                    gradient[ispin] -= 2.0 * this->anisotropy_magnitudes[iani] * this->anisotropy_normals[iani] * anisotropy_normals[iani].dot(spins[ispin]);
            }
        }
    }

    void Hamiltonian_Heisenberg::Gradient_Exchange(const vectorfield & spins, vectorfield & gradient)
    {
        #pragma omp parallel for
        for (int icell = 0; icell < geometry->n_cells_total; ++icell)
        {
            for (unsigned int i_pair = 0; i_pair < exchange_pairs.size(); ++i_pair)
            {
                int ispin = exchange_pairs[i_pair].i + icell*geometry->n_cell_atoms;
                int jspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, exchange_pairs[i_pair]);
                if (jspin >= 0)
                {
                    gradient[ispin] -= exchange_magnitudes[i_pair] * spins[jspin];
                    #ifndef _OPENMP
                    gradient[jspin] -= exchange_magnitudes[i_pair] * spins[ispin];
                    #endif
                }
            }
        }
    }

    void Hamiltonian_Heisenberg::Gradient_DMI(const vectorfield & spins, vectorfield & gradient)
    {
        #pragma omp parallel for
        for (int icell = 0; icell < geometry->n_cells_total; ++icell)
        {
            for (unsigned int i_pair = 0; i_pair < dmi_pairs.size(); ++i_pair)
            {
                int ispin = dmi_pairs[i_pair].i + icell*geometry->n_cell_atoms;
                int jspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, dmi_pairs[i_pair]);
                if (jspin >= 0)
                {
                    gradient[ispin] -= dmi_magnitudes[i_pair] * spins[jspin].cross(dmi_normals[i_pair]);
                    #ifndef _OPENMP
                    gradient[jspin] += dmi_magnitudes[i_pair] * spins[ispin].cross(dmi_normals[i_pair]);
                    #endif
                }
            }
        }
    }

    void Hamiltonian_Heisenberg::Gradient_DDI(const vectorfield & spins, vectorfield & gradient)
    {
        // The translations are in angstr�m, so the |r|[m] becomes |r|[m]*10^-10
        const scalar mult = mu_0 * std::pow(mu_B, 2) / ( 4*Pi * 1e-30 );

        for (unsigned int i_pair = 0; i_pair < ddi_pairs.size(); ++i_pair)
        {
            if (ddi_magnitudes[i_pair] > 0.0)
            {
                for (int da = 0; da < geometry->n_cells[0]; ++da)
                {
                    for (int db = 0; db < geometry->n_cells[1]; ++db)
                    {
                        for (int dc = 0; dc < geometry->n_cells[2]; ++dc)
                        {
                            scalar skalar_contrib = mult / std::pow(ddi_magnitudes[i_pair], 3.0);
                            std::array<int, 3 > translations = { da, db, dc };

                            int i = ddi_pairs[i_pair].i;
                            int j = ddi_pairs[i_pair].j;
                            int ispin = i + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations);
                            int jspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, ddi_pairs[i_pair]);
                            if (jspin >= 0)
                            {
                                gradient[ispin] -= this->mu_s[j] * skalar_contrib * (3 * ddi_normals[i_pair] * spins[jspin].dot(ddi_normals[i_pair]) - spins[jspin]);
                                gradient[jspin] -= this->mu_s[i] * skalar_contrib * (3 * ddi_normals[i_pair] * spins[ispin].dot(ddi_normals[i_pair]) - spins[ispin]);
                            }
                        }
                    }
                }
            }
        }
    }//end Field_DipoleDipole

    void Hamiltonian_Heisenberg::Gradient_Quadruplet(const vectorfield & spins, vectorfield & gradient)
    {
        for (unsigned int iquad = 0; iquad < quadruplets.size(); ++iquad)
        {
            int i = quadruplets[iquad].i;
            int j = quadruplets[iquad].j;
            int k = quadruplets[iquad].k;
            int l = quadruplets[iquad].l;
            for (int da = 0; da < geometry->n_cells[0]; ++da)
            {
                for (int db = 0; db < geometry->n_cells[1]; ++db)
                {
                    for (int dc = 0; dc < geometry->n_cells[2]; ++dc)
                    {
                        std::array<int, 3 > translations = { da, db, dc };
                        int ispin = i + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations);
                        int jspin = j + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_j);
                        int kspin = k + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_k);
                        int lspin = l + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_l);

                        if ( check_atom_type(this->geometry->atom_types[ispin]) && check_atom_type(this->geometry->atom_types[jspin]) &&
                                check_atom_type(this->geometry->atom_types[kspin]) && check_atom_type(this->geometry->atom_types[lspin]) )
                        {
                            gradient[ispin] -= quadruplet_magnitudes[iquad] * spins[jspin] * (spins[kspin].dot(spins[lspin]));
                            gradient[jspin] -= quadruplet_magnitudes[iquad] * spins[ispin] * (spins[kspin].dot(spins[lspin]));
                            gradient[kspin] -= quadruplet_magnitudes[iquad] * (spins[ispin].dot(spins[jspin])) * spins[lspin];
                            gradient[lspin] -= quadruplet_magnitudes[iquad] * (spins[ispin].dot(spins[jspin])) * spins[kspin];
                        }
                    }
                }
            }
        }
    }

    void Hamiltonian_Heisenberg::Hessian(const vectorfield & spins, MatrixX & hessian)
    {
        int nos = spins.size();

        // Set to zero
        hessian.setZero();

        // Single Spin elements
        for (int da = 0; da < geometry->n_cells[0]; ++da)
        {
            for (int db = 0; db < geometry->n_cells[1]; ++db)
            {
                for (int dc = 0; dc < geometry->n_cells[2]; ++dc)
                {
                    std::array<int, 3 > translations = { da, db, dc };
                    int icell = Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations);
                    for (int alpha = 0; alpha < 3; ++alpha)
                    {
                        for ( int beta = 0; beta < 3; ++beta )
                        {
                            for (unsigned int i = 0; i < anisotropy_indices.size(); ++i)
                            {
                                if ( check_atom_type(this->geometry->atom_types[anisotropy_indices[i]]) )
                                {
                                    int idx_i = 3 * icell + anisotropy_indices[i] + alpha;
                                    int idx_j = 3 * icell + anisotropy_indices[i] + beta;
                                    // scalar x = -2.0*this->anisotropy_magnitudes[i] * std::pow(this->anisotropy_normals[i][alpha], 2);
                                    hessian( idx_i, idx_j ) += -2.0 * this->anisotropy_magnitudes[i] *
                                                                    this->anisotropy_normals[i][alpha] *
                                                                    this->anisotropy_normals[i][beta];
                                }
                            }
                        }
                    }
                }
            }
        }

        // Spin Pair elements
        // Exchange
        for (int da = 0; da < geometry->n_cells[0]; ++da)
        {
            for (int db = 0; db < geometry->n_cells[1]; ++db)
            {
                for (int dc = 0; dc < geometry->n_cells[2]; ++dc)
                {
                    std::array<int, 3 > translations = { da, db, dc };
                    for (unsigned int i_pair = 0; i_pair < this->exchange_pairs.size(); ++i_pair)
                    {
                        int ispin = exchange_pairs[i_pair].i + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations);
                        int jspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, exchange_pairs[i_pair]);
                        if (jspin >= 0)
                        {
                            for (int alpha = 0; alpha < 3; ++alpha)
                            {
                                int i = 3 * ispin + alpha;
                                int j = 3 * jspin + alpha;

                                hessian(i, j) += -exchange_magnitudes[i_pair];
                                #ifndef _OPENMP
                                hessian(j, i) += -exchange_magnitudes[i_pair];
                                #endif
                            }
                        }
                    }
                }
            }
        }

        // DMI
        for (int da = 0; da < geometry->n_cells[0]; ++da)
        {
            for (int db = 0; db < geometry->n_cells[1]; ++db)
            {
                for (int dc = 0; dc < geometry->n_cells[2]; ++dc)
                {
                    std::array<int, 3 > translations = { da, db, dc };
                    for (unsigned int i_pair = 0; i_pair < this->dmi_pairs.size(); ++i_pair)
                    {
                        int ispin = dmi_pairs[i_pair].i + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations);
                        int jspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, dmi_pairs[i_pair]);
                        if (jspin >= 0)
                        {
                            int i = 3*ispin;
                            int j = 3*jspin;

                            hessian(i+2, j+1) +=  dmi_magnitudes[i_pair] * dmi_normals[i_pair][0];
                            hessian(i+1, j+2) += -dmi_magnitudes[i_pair] * dmi_normals[i_pair][0];
                            hessian(i, j+2)   +=  dmi_magnitudes[i_pair] * dmi_normals[i_pair][1];
                            hessian(i+2, j)   += -dmi_magnitudes[i_pair] * dmi_normals[i_pair][1];
                            hessian(i+1, j)   +=  dmi_magnitudes[i_pair] * dmi_normals[i_pair][2];
                            hessian(i, j+1)   += -dmi_magnitudes[i_pair] * dmi_normals[i_pair][2];

                            #ifndef _OPENMP
                            hessian(j+1, i+2) +=  dmi_magnitudes[i_pair] * dmi_normals[i_pair][0];
                            hessian(j+2, i+1) += -dmi_magnitudes[i_pair] * dmi_normals[i_pair][0];
                            hessian(j+2, i)   +=  dmi_magnitudes[i_pair] * dmi_normals[i_pair][1];
                            hessian(j, i+2)   += -dmi_magnitudes[i_pair] * dmi_normals[i_pair][1];
                            hessian(j, i+1)   +=  dmi_magnitudes[i_pair] * dmi_normals[i_pair][2];
                            hessian(j+1, i)   += -dmi_magnitudes[i_pair] * dmi_normals[i_pair][2];
                            #endif
                        }
                    }
                }
            }
        }

        //// Dipole-Dipole
        //for (unsigned int i_pair = 0; i_pair < this->DD_indices.size(); ++i_pair)
        //{
        //	// indices
        //	int idx_1 = DD_indices[i_pair][0];
        //	int idx_2 = DD_indices[i_pair][1];
        //	// prefactor
        //	scalar prefactor = 0.0536814951168
        //		* this->mu_s[idx_1] * this->mu_s[idx_2]
        //		/ std::pow(DD_magnitude[i_pair], 3);
        //	// components
        //	for (int alpha = 0; alpha < 3; ++alpha)
        //	{
        //		for (int beta = 0; beta < 3; ++beta)
        //		{
        //			int idx_h = idx_1 + alpha*nos + 3 * nos*(idx_2 + beta*nos);
        //			if (alpha == beta)
        //				hessian[idx_h] += prefactor;
        //			hessian[idx_h] += -3.0*prefactor*DD_normal[i_pair][alpha] * DD_normal[i_pair][beta];
        //		}
        //	}
        //}

        // Quadruplets
    }

    // Hamiltonian name as string
    static const std::string name = "Heisenberg";
    const std::string& Hamiltonian_Heisenberg::Name() { return name; }
}

#endif


/*
 * parser_date.hpp
 *
 *  Created on: 19.02.2016
 *      Author: mgresens
 */

#pragma once

#include <boost/date_time/posix_time/posix_time_types.hpp>

namespace hessian {
namespace parser {
namespace date {

static const date_t EPOCH(boost::gregorian::date(1970, 1, 1));

template <typename Duration>
struct date_action
{
	template <typename C>
	void operator()(C& ctx) const
	{
		x3::_val(ctx) = EPOCH + Duration(x3::_attr(ctx));
	};
};

const x3::rule<class date_rule, date_t> date_rule("date");
const x3::rule<class date1_rule, date_t> date1_rule;
const x3::rule<class date2_rule, date_t> date2_rule;

const auto date_rule_def = date1_rule | date2_rule;
const auto date1_rule_def = x3::lit('J') >> x3::big_qword [date_action<boost::posix_time::milliseconds>()];
const auto date2_rule_def = x3::lit('K') >> x3::big_dword [date_action<boost::posix_time::minutes>()];

BOOST_SPIRIT_DEFINE(date_rule, date1_rule, date2_rule);

}

using date::date_rule;

}
}


/*ckwg +29
 * Copyright 2015-2020 by Kitware, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 *  * Neither name of Kitware, Inc. nor the names of any contributors may be used
 *    to endorse or promote products derived from this software without specific
 *    prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

// The immediately following copyright notice applies to code in the methods
// Triangulate_DLT, find_optimal_image_points and triangulate_fast_two_view.

 // Copyright (C) 2013 The Regents of the University of California (Regents).
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are
 // met:
 //
 //     * Redistributions of source code must retain the above copyright
 //       notice, this list of conditions and the following disclaimer.
 //
 //     * Redistributions in binary form must reproduce the above
 //       copyright notice, this list of conditions and the following
 //       disclaimer in the documentation and/or other materials provided
 //       with the distribution.
 //
 //     * Neither the name of The Regents or University of California nor the
 //       names of its contributors may be used to endorse or promote products
 //       derived from this software without specific prior written permission.
 //
 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 // POSSIBILITY OF SUCH DAMAGE.
 //
 // Please contact the author of this library if you have any questions.
 // Author: Chris Sweeney (cmsweeney@cs.ucsb.edu)

/**
 * \file
 * \brief Implementation of triangulation function
 */

#include "triangulate.h"
#include <Eigen/SVD>
#include <arrows/mvg/epipolar_geometry.h>

namespace kwiver {
namespace arrows {
namespace mvg {

// Triangulates 2 views
void
Triangulate_DLT( kwiver::vital::matrix_3x4d const& pose1,
                 kwiver::vital::matrix_3x4d const& pose2,
                 kwiver::vital::vector_2d const& point1,
                 kwiver::vital::vector_2d const& point2,
                 kwiver::vital::vector_4d &triangulated_point)
{
  // code modified from code found at
  // https://github.com/sweeneychris/TheiaSfM/blob/master/src/theia/sfm/triangulation/triangulation.h

  kwiver::vital::matrix_4x4d design_matrix;
  design_matrix.row(0) = point1[0] * pose1.row(2) - pose1.row(0);
  design_matrix.row(1) = point1[1] * pose1.row(2) - pose1.row(1);
  design_matrix.row(2) = point2[0] * pose2.row(2) - pose2.row(0);
  design_matrix.row(3) = point2[1] * pose2.row(2) - pose2.row(1);

  // Extract nullspace.
  Eigen::JacobiSVD<Eigen::Matrix<double,4,4>> svd(design_matrix, Eigen::ComputeFullV);
  triangulated_point = svd.matrixV().rightCols<1>();
}

// Given either a fundamental or essential matrix and two corresponding images
// points such that ematrix * point2 produces a line in the first image,
// this method finds corrected image points such that
// corrected_point1^t * ematrix * corrected_point2 = 0.
void
find_optimal_image_points(kwiver::vital::essential_matrix_sptr ematrix,
                          const vital::vector_2d &point1,
                          const vital::vector_2d &point2,
                          vital::vector_2d &corrected_point1,
                          vital::vector_2d &corrected_point2)
{
  // code modified from code found at
  // https://github.com/sweeneychris/TheiaSfM/blob/master/src/theia/sfm/triangulation/triangulation.cc
  auto E = ematrix->matrix();

  vital::vector_3d point1_homog = point1.homogeneous();
  vital::vector_3d point2_homog = point2.homogeneous();

  // A helper matrix to isolate certain coordinates.
  Eigen::Matrix<double, 2, 3> s_matrix;
  s_matrix << 1, 0, 0, 0, 1, 0;

  const Eigen::Matrix2d e_submatrix = E.topLeftCorner<2, 2>();

  // The epipolar line from one image point in the other image.
  vital::vector_2d epipolar_line1 = s_matrix * E * point2_homog;
  vital::vector_2d epipolar_line2 = s_matrix * E.transpose() * point1_homog;

  const double a = epipolar_line1.transpose() * e_submatrix * epipolar_line2;
  const double b =
    (epipolar_line1.squaredNorm() + epipolar_line2.squaredNorm()) / 2.0;
  const double c = point1_homog.transpose() * E * point2_homog;

  const double d = sqrt(b * b - a * c);

  double lambda = c / (b + d);
  epipolar_line1 -= e_submatrix * lambda * epipolar_line1;
  epipolar_line2 -= e_submatrix.transpose() * lambda * epipolar_line2;

  lambda *=
    (2.0 * d) / (epipolar_line1.squaredNorm() + epipolar_line2.squaredNorm());

  corrected_point1 =
    (point1_homog - s_matrix.transpose() * lambda * epipolar_line1)
    .hnormalized();
  corrected_point2 =
    (point2_homog - s_matrix.transpose() * lambda * epipolar_line2)
    .hnormalized();
}

template <typename T>
Eigen::Matrix<T, 3, 1>
triangulate_fast_two_view(const vital::simple_camera_perspective &camera0,
                          const vital::simple_camera_perspective &camera1,
                          const Eigen::Matrix<T, 2, 1> &point0,
                          const Eigen::Matrix<T, 2, 1> &point1)
{
  // code modified from code found at
  // https://github.com/sweeneychris/TheiaSfM/blob/master/src/theia/sfm/triangulation/triangulation.cc

  auto E = essential_matrix_from_cameras(camera0, camera1);

  const vital::vector_2d pt0 = camera0.get_intrinsics()->unmap(point0.template cast<double>());
  const vital::vector_2d pt1 = camera1.get_intrinsics()->unmap(point1.template cast<double>());

  vital::vector_2d corrected_pt0, corrected_pt1;

  find_optimal_image_points(E, pt0, pt1, corrected_pt0, corrected_pt1);

  vital::vector_4d triangulated_point;
  Triangulate_DLT(camera0.pose_matrix(), camera1.pose_matrix(), corrected_pt0, corrected_pt1, triangulated_point);
  return triangulated_point.hnormalized().template cast<T>();
}

/// Triangulate a 3D point from a set of cameras and 2D image points
template <typename T>
Eigen::Matrix<T,3,1>
triangulate_inhomog(const std::vector<vital::simple_camera_perspective >& cameras,
                    const std::vector<Eigen::Matrix<T,2,1> >& points)
{
  typedef Eigen::Matrix<T,2,1> vector_2;
  typedef Eigen::Matrix<T,3,1> vector_3;
  typedef Eigen::Matrix<T,3,3> matrix_3x3;
  typedef Eigen::Matrix<T, Eigen::Dynamic, 3> data_matrix_t;
  typedef Eigen::Matrix<T, Eigen::Dynamic, 1> data_vector_t;
  const unsigned int num_rows = 2*static_cast<unsigned int>(points.size());
  data_matrix_t A(num_rows, 3);
  data_vector_t b(num_rows);
  for ( unsigned int i=0; i<points.size(); ++i )
  {
    // the camera
    const vital::simple_camera_perspective& cam = cameras[i];
    const matrix_3x3 R(cam.get_rotation().matrix().cast<T>());
    const vector_3 t(cam.translation().cast<T>());
    // the point in normalized coordinates
    const vital::vector_2d p2d = points[i].template cast<double>();
    const vector_2 pt = cam.get_intrinsics()->unmap(p2d).template cast<T>();
    A(2*i,   0) = R(0,0) - pt.x() * R(2,0);
    A(2*i,   1) = R(0,1) - pt.x() * R(2,1);
    A(2*i,   2) = R(0,2) - pt.x() * R(2,2);
    A(2*i+1, 0) = R(1,0) - pt.y() * R(2,0);
    A(2*i+1, 1) = R(1,1) - pt.y() * R(2,1);
    A(2*i+1, 2) = R(1,2) - pt.y() * R(2,2);
    b[2*i  ] = t.z()*pt.x() - t.x();
    b[2*i+1] = t.z()*pt.y() - t.y();
  }
  Eigen::JacobiSVD<data_matrix_t> svd(A, Eigen::ComputeFullU |
                                         Eigen::ComputeFullV);
  return svd.solve(b);
}

/// Triangulate a homogeneous 3D point from a set of cameras and 2D image points
template <typename T>
Eigen::Matrix<T,4,1>
triangulate_homog(const std::vector<vital::simple_camera_perspective >& cameras,
                  const std::vector<Eigen::Matrix<T,2,1> >& points)
{
  typedef Eigen::Matrix<T,2,1> vector_2;
  typedef Eigen::Matrix<T,3,1> vector_3;
  typedef Eigen::Matrix<T,3,3> matrix_3x3;
  typedef Eigen::Matrix<T, Eigen::Dynamic, 4> data_matrix_t;
  const unsigned int num_rows = 2*static_cast<unsigned int>(points.size());
  data_matrix_t A(num_rows, 4);
  for ( unsigned int i=0; i<points.size(); ++i )
  {
    // the camera
    const vital::simple_camera_perspective& cam = cameras[i];
    const matrix_3x3 R(cam.get_rotation().matrix().cast<T>());
    const vector_3 t(cam.translation().cast<T>());
    // the point in normalized coordinates
    const vital::vector_2d p2d = points[i].template cast<double>();
    const vector_2 pt = cam.get_intrinsics()->unmap(p2d).template cast<T>();
    A(2*i,   0) = R(0,0) - pt.x() * R(2,0);
    A(2*i,   1) = R(0,1) - pt.x() * R(2,1);
    A(2*i,   2) = R(0,2) - pt.x() * R(2,2);
    A(2*i,   3) = t.x()  - pt.x() * t.z();
    A(2*i+1, 0) = R(1,0) - pt.y() * R(2,0);
    A(2*i+1, 1) = R(1,1) - pt.y() * R(2,1);
    A(2*i+1, 2) = R(1,2) - pt.y() * R(2,2);
    A(2*i+1, 3) = t.y()  - pt.y() * t.z();
  }
  Eigen::JacobiSVD<data_matrix_t > svd(A, Eigen::ComputeFullV);
  return svd.matrixV().col(3);
}

/// Triangulate a 3D point from a set of RPC cameras and 2D image points
template <typename T>
Eigen::Matrix<T,3,1>
triangulate_rpc(const std::vector<vital::simple_camera_rpc >& cameras,
                const std::vector<Eigen::Matrix<T,2,1> >& points)
{
  // Get the pairs of points to define the rays
  std::vector< std::pair< vital::vector_3d, vital::vector_3d > > pts;

  Eigen::Array3d curr_scale = cameras[0].world_scale().array();
  Eigen::Array3d curr_offset = cameras[0].world_offset().array();
  Eigen::Array3d min_pos = curr_offset - curr_scale;
  Eigen::Array3d max_pos = curr_offset + curr_scale;

  for ( unsigned int i = 0; i < points.size(); ++i )
  {
    // Get world offset and scale to set normalization and sample heights
    curr_scale = cameras[i].world_scale().array();
    curr_offset = cameras[i].world_offset().array();

    min_pos = min_pos.min( curr_offset - curr_scale );
    max_pos = max_pos.max( curr_offset + curr_scale );

    double h1 = ( curr_offset - curr_scale )[2];
    double h2 = ( curr_offset + curr_scale )[2];

    vital::vector_3d pt1 =
      cameras[i].back_project( points[i].template cast<double>(), h1 );
    vital::vector_3d pt2 =
      cameras[i].back_project( points[i].template cast<double>(), h2 );
    pts.push_back(
      std::pair< vital::vector_3d, vital::vector_3d >( pt1, pt2 ) );
  }

  // Get normalization factors for full point set
  vital::vector_3d scale = 0.5*( max_pos.matrix() - min_pos.matrix() );
  vital::vector_3d offset = 0.5*( max_pos.matrix() + min_pos.matrix() );

  vital::matrix_3x3d M = vital::matrix_3x3d::Zero();
  vital::vector_3d v(0., 0., 0.);

  for ( auto& pt : pts )
  {
    // Normalize points
    vital::vector_3d p = ( pt.first - offset ).cwiseQuotient( scale );
    vital::vector_3d x = ( pt.second - offset ).cwiseQuotient( scale );

    // Unit vector along ray
    vital::vector_3d unit_vec = ( x - p ).normalized();

    vital::matrix_3x3d tmp_mat =
      vital::matrix_3x3d::Identity() - unit_vec * unit_vec.transpose();
    M += tmp_mat;
    v += tmp_mat * p;
  }

  // Un-normalize before return
  return ( scale.cwiseProduct(
    M.colPivHouseholderQr().solve( v ) ) + offset ).cast<T>();
}

/// \cond DoxygenSuppress
#define INSTANTIATE_TRIANGULATE(T) \
template KWIVER_ALGO_MVG_EXPORT Eigen::Matrix<T,3,1> \
         triangulate_fast_two_view( \
            const vital::simple_camera_perspective &camera0, \
            const vital::simple_camera_perspective &camera1, \
            const Eigen::Matrix<T, 2, 1> &point0, \
            const Eigen::Matrix<T, 2, 1> &point1); \
template KWIVER_ALGO_MVG_EXPORT Eigen::Matrix<T,4,1> \
         triangulate_homog( \
            const std::vector<vital::simple_camera_perspective >& cameras, \
            const std::vector<Eigen::Matrix<T,2,1> >& points); \
template KWIVER_ALGO_MVG_EXPORT Eigen::Matrix<T,3,1> \
         triangulate_inhomog( \
            const std::vector<vital::simple_camera_perspective >& cameras, \
            const std::vector<Eigen::Matrix<T,2,1> >& points); \
template KWIVER_ALGO_MVG_EXPORT Eigen::Matrix<T,3,1> \
         triangulate_rpc( \
            const std::vector<vital::simple_camera_rpc >& cameras, \
            const std::vector<Eigen::Matrix<T,2,1> >& points);

INSTANTIATE_TRIANGULATE(double);
INSTANTIATE_TRIANGULATE(float);

#undef INSTANTIATE_TRIANGULATE
/// \endcond

} // end namespace mvg
} // end namespace arrows
} // end namespace kwiver


/******************************************************************************
**
** Copyright (C) 2009-2012 Kyle Lutz <kyle.r.lutz@gmail.com>
** All rights reserved.
**
** This file is a part of the chemkit project. For more information
** see <http://www.chemkit.org>.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in the
**     documentation and/or other materials provided with the distribution.
**   * Neither the name of the chemkit project nor the names of its
**     contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
******************************************************************************/

#include "vabcdescriptor.h"

#include <boost/bind.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/range/algorithm.hpp>

#include <chemkit/atom.h>
#include <chemkit/bond.h>
#include <chemkit/ring.h>
#include <chemkit/foreach.h>
#include <chemkit/molecule.h>
#include <chemkit/aromaticitymodel.h>

VabcDescriptor::VabcDescriptor()
    : chemkit::MolecularDescriptor("vabc")
{
    setDimensionality(1);

    // values from table 2 and the supplementary spreadsheet
    m_volumes[chemkit::Atom::Hydrogen] = 7.2382293504;
    m_volumes[chemkit::Atom::Carbon] = 20.5795259250667;
    m_volumes[chemkit::Atom::Nitrogen] = 15.5985308577667;
    m_volumes[chemkit::Atom::Oxygen] = 14.7102267005611;
    m_volumes[chemkit::Atom::Fluorine] = 13.3057882007064;
    m_volumes[chemkit::Atom::Chlorine] = 22.4492971208333;
    m_volumes[chemkit::Atom::Bromine] = 26.5218483279667;
    m_volumes[chemkit::Atom::Iodine] = 32.5150310206656;
    m_volumes[chemkit::Atom::Phosphorus] = 24.4290240576;
    m_volumes[chemkit::Atom::Sulfur] = 24.4290240576;
    m_volumes[chemkit::Atom::Arsenic] = 26.5218483279667;
    m_volumes[chemkit::Atom::Boron] = 40.48;
    m_volumes[chemkit::Atom::Silicon] = 38.7923854248;
    m_volumes[chemkit::Atom::Selenium] = 28.7309115245333;
    m_volumes[chemkit::Atom::Tellurium] = 36.62;
}

VabcDescriptor::~VabcDescriptor()
{
}

chemkit::Real VabcDescriptor::volume(const chemkit::Atom *atom) const
{
    std::map<chemkit::Element::AtomicNumberType, chemkit::Real>::const_iterator iter =
        m_volumes.find(atom->atomicNumber());

    if(iter != m_volumes.end()){
        return iter->second;
    }
    else{
        return 0;
    }
}

// Returns the VABC value for the molecule.
chemkit::Variant VabcDescriptor::value(const chemkit::Molecule *molecule) const
{
    chemkit::Real atomContributions = 0;

    foreach(const chemkit::Atom *atom, molecule->atoms()){
        atomContributions += volume(atom);
    }

    size_t bondCount = molecule->bondCount();
    size_t ringCount = molecule->ringCount();
    size_t aromaticRingCount = boost::count_if(molecule->rings(), boost::bind(&chemkit::Ring::isAromatic, _1));
    size_t aliphaticRingCount = ringCount - aromaticRingCount;

    // equation 4
    return atomContributions - (5.92 * bondCount) - (14.7 * aromaticRingCount) - (3.8 * aliphaticRingCount);
}


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

#include <boost/hana/orderable/less_mcd.hpp>

#include <boost/hana/detail/assert.hpp>
#include <boost/hana/detail/minimal/orderable.hpp>
#include <boost/hana/list/instance.hpp>
#include <boost/hana/orderable/laws.hpp>
using namespace boost::hana;

template <typename mcd>
void test() {
    constexpr auto orderable = detail::minimal::orderable<mcd>;
    BOOST_HANA_CONSTEXPR_ASSERT(
        Orderable::laws::check(
            list(orderable(1), orderable(2), orderable(3))
        )
    );
}

int main() {
    test<Orderable::less_mcd>();
}


#include <opencv2\opencv.hpp>
#include <iostream>
#include <fstream>
#include <sstream>
#include <boost\filesystem.hpp>
#include <iomanip>
#include <direct.h>
#include <ctime>
#include "exif.h"

namespace fs = ::boost::filesystem;

using namespace std;
using namespace cv;

/*
structure to map an image to its mean color
*/
struct ImageMean{
	string name;
	Vec3b mean;
};

/*
structure to hold application parameters
*/
struct Params{
	string images_root;
	string out_folder;
	string index_filename = "index.txt";
	Size pixelSize;
	string input_image;
	double resize_x;
	double resize_y;
	char pixelize;
	char mosaicize;
	unsigned skip_interval;
};

bool isInVector(const string& str, const vector<string>& vec){
	for (string s : vec){
		if (s == str)
			return true;
	}
	return false;
}

/*
seeks recursively in root for all files having extension ext,
and builds the list ret
*/
void get_all(const fs::path& root, const vector<string>& extensions, vector<string>& ret)
{
	if (!fs::exists(root) || !fs::is_directory(root)) return;

	fs::recursive_directory_iterator it(root);
	fs::recursive_directory_iterator endit;

	while (it != endit)
	{
		if (fs::is_regular_file(*it) && isInVector(it->path().extension().string(), extensions))
			ret.push_back(it->path().string());
		++it;

	}
}

/*
computes the mean color of an image
*/
Vec3b mean(const Mat3b& m){
	unsigned long b = 0;
	unsigned long g = 0;
	unsigned long r = 0;

	unsigned char *data = (unsigned char*)(m.data);
	for (int r_c = 0; r_c < m.rows; ++r_c){
		for (int c_c = 0; c_c < m.cols * 3; c_c = c_c + 3){
			b += data[m.step * r_c + c_c];
			g += data[m.step * r_c + c_c + 1];
			r += data[m.step * r_c + c_c + 2];
		}
	}
	unsigned nPix = m.rows*m.cols;

	return Vec3b(b / nPix, g / nPix, r / nPix);
}

/*
self explanatory
*/
void printProgress(int percentage, unsigned elapsed, unsigned etl){
	cout << "\rProgress:|";
	char bar_length = 15;
	char number_of_progress_chars = round(percentage * bar_length / 100);

	for (unsigned j = 0; j < number_of_progress_chars; ++j) cout << "=";
	cout << ">";
	for (unsigned j = 0; j < bar_length - number_of_progress_chars; ++j) cout << " ";
	cout << "| " << percentage << "%, Time elapsed: " << elapsed << " seconds, ETL: " << etl << " seconds.";
}

/*
extracts exif orientations from jpeg files.
useful if you have pictures taken with smartphones
*/
char extractEXIFOrientation(const string& img_name){
	FILE *fp = fopen(img_name.c_str(), "rb");
	if (!fp) {
		//printf("Can't open file.\n");
	}

	fseek(fp, 0, SEEK_END);
	unsigned long fsize = ftell(fp);
	rewind(fp);
	unsigned char *buf = new unsigned char[fsize];
	if (fread(buf, 1, fsize, fp) != fsize) {
		//printf("Can't read file.\n");
		delete[] buf;
	}
	fclose(fp);

	// Parse EXIF
	easyexif::EXIFInfo result;
	int code = result.parseFrom(buf, fsize);
	delete[] buf;
	if (code) {
		//printf("Error parsing EXIF: code %d\n", code);
	}

	return result.Orientation;
}

/*
rotates an image clockwise
*/
void rotateClockwise(Mat& img){
	transpose(img, img);
	flip(img, img, 1);
}

/*
fixes image given its exif orientation
*/
void rectifyByOrientation(Mat3b& img, char orientation){
	switch (orientation){
	case 1: break; //fine
	case 6: //rotate clockwise
		rotateClockwise(img);
		break;
	case 3: //flip vertically
		//ignorance is the law
		rotateClockwise(img);
		rotateClockwise(img);
		break;
	case 8: // rotate counterclockwise
		//even more ignorance!!!!
		rotateClockwise(img);
		rotateClockwise(img);
		rotateClockwise(img);
		break;
	default: break;
	}
}

/*
precomputes all the small images ("pixels") that will form the mosaic.
Also stores in an index (index_filename) the mapping with the mean color.
*/
void
computePixelsAndIndex(const string& images_root, const string& out_folder, Size s, const string& index_filename){

	cout << "Pixelizing images from " << images_root << ". This might take a while." << endl;
	vector<string> images;
	vector<string> extensions = { ".jpg", ".jpeg", ".JPG", ".JPEG" };
	get_all(images_root, extensions, images);

	fs::remove_all(out_folder);
	fs::create_directory(out_folder);
	ofstream out_index(out_folder + index_filename);

	clock_t begin = clock();

	for (unsigned i = 0; i < images.size(); ++i)
	{
		Mat3b img = imread(images[i]);
		char orientation = extractEXIFOrientation(images[i]);
		rectifyByOrientation(img, orientation);

		Vec3b mean_color = mean(img);

		Mat3b resized;
		resize(img, resized, s);

		ostringstream out_filename;
		out_filename << out_folder << setfill('0') << setw(5) << i << ".png";
		imwrite(out_filename.str(), resized);

		out_index << out_filename.str() << "\t" << (int)mean_color[0] << " " << (int)mean_color[1] << " " << (int)mean_color[2] << endl;

		//Measuring time for progress...
		clock_t end = clock();
		int percentage = round(double(i) * 100 / images.size());

		unsigned elapsed = double(end - begin) / CLOCKS_PER_SEC;
		unsigned etl = (double(elapsed) / i)*(images.size() - i);

		printProgress(percentage, elapsed, etl);
	}
	out_index.close();

	cout << endl << "Done." << endl;
}

/*
reads the precomputed mean colors from file
*/
void readIndexFile(const string& index_filename, vector<ImageMean>& index){
	ifstream in(index_filename);
	string line;
	while (getline(in, line)){
		stringstream ss(line);
		ImageMean im;
		unsigned r, g, b;
		ss >> im.name >> b >> g >> r;

		im.mean = Vec3b(b, g, r);
		index.push_back(im);
	}
}

/*
utility structure for sorting (by similarity)
*/
struct idxVal{
	int idx;
	double val;

	bool operator<(idxVal conf){
		if (val < conf.val)return true;
		return false;
	}
};

/*
returns the nearest image given the pixel color and the forbidden ones
*/
ImageMean nearestImage(const vector<ImageMean>& index, Vec3b color, vector<unsigned char>& forbidden){
	vector<idxVal> ivals;
	for (unsigned i = 0; i < index.size(); ++i){
		Vec3b conf = index[i].mean;
		idxVal ival;
		ival.idx = i;
		ival.val = norm(color, conf);

		ivals.push_back(ival);
	}
	sort(ivals.begin(), ivals.end());

	for (unsigned i = 0; i < ivals.size(); i++){
		if (!forbidden[ivals[i].idx]){
			forbidden[ivals[i].idx] = 1;
			return index[ivals[i].idx];
		}
	}
	ImageMean err;
	return err;
}

/*
self explanatory
*/
void printUsage(){
	cout << "Usage: Photomosaic <settings_file.ini>" << endl;
}

/*
reads the file holding parameters settings
*/
void readInitFile(const string& init_file, Params& params)
{
	try{
		ifstream in(init_file);
		string line;
		vector<string> strings;
		while (getline(in, line)){
			if (line[0] != '#') strings.push_back(line);
		}
		istringstream ss;
		//dataset
		params.images_root = strings[0];
		//pixel_folder
		params.out_folder = strings[1];
		//pixel_size
		ss = istringstream(strings[2]);
		unsigned sx, sy;
		ss >> sx >> sy;
		params.pixelSize = Size(sx, sy);
		//input image
		params.input_image = strings[3];
		//resize
		ss = istringstream(strings[4]);
		ss >> params.resize_x >> params.resize_y;
		//pixelize
		ss = istringstream(strings[5]);
		params.pixelize = atoi(ss.str().c_str());
		//mosaicize
		ss = istringstream(strings[6]);
		params.mosaicize = atoi(ss.str().c_str());
		//skip_interval
		ss = istringstream(strings[7]);
		ss >> params.skip_interval;
	}
	catch (int e){
		cout << "Something went wrong in the initialization file parsing... please check it." << endl;
		exit(1);
	}
}

/*
main function
*/
int main(int argc, char** argv){

	if (argc != 2){
		printUsage();
		return 1;
	}

	string init_file = argv[1];
	Params p;
	readInitFile(init_file, p);

	if (p.pixelize){
		computePixelsAndIndex(p.images_root, p.out_folder, p.pixelSize, p.index_filename);
	}

	if (p.mosaicize){
		cout << "Rendering mosaic for image " << p.input_image << "..." << endl;
		vector<ImageMean> index;
		readIndexFile(p.out_folder + p.index_filename, index);

		Mat3b src = imread(p.input_image);
		resize(src, src, Size(0, 0), p.resize_x, p.resize_y);

		Mat3b output(src.rows*p.pixelSize.height, src.cols*p.pixelSize.width);
		vector<unsigned char> forbidden(index.size());

		clock_t begin = clock();
		for (int i = 0; i < src.rows; ++i){
			for (int j = 0; j < src.cols; ++j){

				if (((i*src.cols + j) % p.skip_interval) == 0)
					forbidden = vector<unsigned char>(index.size());

				Vec3b color = src(i, j);

				ImageMean best_match = nearestImage(index, color, forbidden);
				Mat3b pixel = imread(best_match.name);

				Rect bound(j*p.pixelSize.width, i*p.pixelSize.height, p.pixelSize.width, p.pixelSize.height);
				pixel.copyTo(output.rowRange(i*p.pixelSize.height, i*p.pixelSize.height + p.pixelSize.height).colRange(j*p.pixelSize.width, j*p.pixelSize.width + p.pixelSize.width));
			}
		}

		imwrite("output.png", output);

		cout << endl << "Done. Mosaic image has been written to output.png" << endl;
	}

}


#ifndef RANDOM_HH
#define RANDOM_HH

#include <boost/random/mersenne_twister.hpp>

#include <random>

typedef boost::random::mt19937 PRNG;

extern PRNG & global_PRNG();

class RandGen {
	std::default_random_engine generator;

 public:
	RandGen();

	double uniform(double a, double b);
	double exponential(double lambda);
};

#endif


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

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

BOOST_CLASS_EXPORT(Convolution2D)

Convolution2D::Convolution2D(LayerModel& model, shared_ptr<NeuralNetworkOptimizer> optimizer)
    : FilterLayer(model, optimizer)
{
}

inline
unique_ptr<BaseLayer> Convolution2D::clone(std::shared_ptr<NeuralNetworkOptimizer> optimizer) const
{
    auto layer = make_unique<Convolution2D>(*this);
    for (auto& neuron : layer->neurons)
        neuron.optimizer = optimizer;
    return layer;
}

std::vector<int> Convolution2D::getShapeOfOutput() const
{
    return {
        this->shapeOfInput[0] - (this->sizeOfFilterMatrix - 1),
        this->shapeOfInput[1] - (this->sizeOfFilterMatrix - 1),
        this->numberOfFilters
    };
}

int Convolution2D::isValid() const
{
    for (auto& neuron : neurons)
    {
        if (neuron.getNumberOfInputs() != this->sizeOfFilterMatrix * this->sizeOfFilterMatrix * this->shapeOfInput[2])
            return 203;
    }
    return this->FilterLayer::isValid();
}

inline
vector<float> Convolution2D::createInputsForNeuron(int neuronNumber, const vector<float>& inputs) const
{
    vector<float> neuronInputs;
    neuronInputs.reserve(this->neurons[neuronNumber].getNumberOfInputs());
    neuronNumber = neuronNumber % this->getNumberOfNeurons()/this->numberOfFilters;
    const int neuronPositionX = neuronNumber % this->shapeOfInput[0];
    const int neuronPositionY = neuronNumber / this->shapeOfInput[0];

    for (int i = 0; i < this->sizeOfFilterMatrix; ++i)
    {
        const int beginIndex = ((neuronPositionY + i) * this->shapeOfInput[0] * this->shapeOfInput[2]) + neuronPositionX * this->shapeOfInput[2];
        const int endIndex = ((neuronPositionY + i) * this->shapeOfInput[0] * this->shapeOfInput[2])
        + (neuronPositionX + this->sizeOfFilterMatrix) * this->shapeOfInput[2];
        for (int j = beginIndex; j < endIndex; ++j)
        {
            neuronInputs.push_back(inputs[j]);
        }
    }
    return neuronInputs;
}

void Convolution2D::insertBackOutputForNeuron(int neuronNumber, const std::vector<float>& error, std::vector<float>& errors) const
{
    neuronNumber = neuronNumber % this->getNumberOfNeurons()/this->numberOfFilters;
    const int neuronPositionX = neuronNumber % (this->shapeOfInput[0] - (this->sizeOfFilterMatrix - 1));
    const int neuronPositionY = neuronNumber / (this->shapeOfInput[0] - (this->sizeOfFilterMatrix - 1));

    for (int i = 0; i < this->sizeOfFilterMatrix; ++i)
    {
        const int beginIndex = ((neuronPositionY + i) * this->shapeOfInput[0] * this->shapeOfInput[2]) + neuronPositionX * this->shapeOfInput[2];
        for(int j = 0; j < this->sizeOfFilterMatrix; ++j)
        {
            const int indexErrors = beginIndex + j;
            const int indexMatrix = i * this->sizeOfFilterMatrix + j;
            errors[indexErrors] += error[indexMatrix];
        }
    }
}

inline
bool Convolution2D::operator==(const BaseLayer& layer) const
{
    return this->FilterLayer::operator==(layer);
}

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


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

#ifndef CRYPTO3_ALGEBRA_CURVES_HASH_TO_CURVE_EXPAND_HPP
#define CRYPTO3_ALGEBRA_CURVES_HASH_TO_CURVE_EXPAND_HPP

#include <nil/crypto3/algebra/curves/detail/h2c/h2c_suites.hpp>
#include <nil/crypto3/algebra/curves/detail/h2c/h2c_sgn0.hpp>

#include <nil/crypto3/algebra/algorithms/strxor.hpp>

#include <nil/crypto3/algebra/curves/bls12.hpp>

#include <nil/crypto3/hash/algorithm/hash.hpp>
#include <nil/crypto3/hash/accumulators/hash.hpp>

#include <boost/assert.hpp>
#include <boost/static_assert.hpp>
#include <boost/concept/assert.hpp>

#include <array>
#include <type_traits>
#include <iterator>

namespace nil {
    namespace crypto3 {
        namespace algebra {
            namespace curves {
                namespace detail {
                    using namespace nil::crypto3::detail;
                    template<std::size_t k, typename HashType,
                             /// HashType::digest_type is required to be uint8_t[]
                             typename = typename std::enable_if<
                                 std::is_same<std::uint8_t, typename HashType::digest_type::value_type>::value>::type>
                    class expand_message_xmd {
                        // https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-10#section-5.4.1
                        BOOST_STATIC_ASSERT_MSG(HashType::block_bits % 8 == 0, "r_in_bytes is not a multiple of 8");
                        BOOST_STATIC_ASSERT_MSG(HashType::digest_bits % 8 == 0, "b_in_bytes is not a multiple of 8");
                        BOOST_STATIC_ASSERT_MSG(HashType::digest_bits >= 2 * k,
                                                "k-bit collision resistance is not fulfilled");

                        constexpr static const std::size_t b_in_bytes = HashType::digest_bits / 8;
                        constexpr static const std::size_t r_in_bytes = HashType::block_bits / 8;

                        constexpr static const std::array<std::uint8_t, r_in_bytes> Z_pad {0};

                    public:
                        template<typename InputMsgType, typename InputDstType, typename OutputType,
                                 typename = typename std::enable_if<
                                     std::is_same<std::uint8_t, typename InputMsgType::value_type>::value &&
                                     std::is_same<std::uint8_t, typename InputDstType::value_type>::value &&
                                     std::is_same<std::uint8_t, typename OutputType::value_type>::value>::type>
                        static inline void process(const std::size_t len_in_bytes, const InputMsgType &msg,
                                                   const InputDstType &dst, OutputType &uniform_bytes) {
                            BOOST_CONCEPT_ASSERT((boost::SinglePassRangeConcept<InputMsgType>));
                            BOOST_CONCEPT_ASSERT((boost::SinglePassRangeConcept<InputDstType>));
                            BOOST_CONCEPT_ASSERT((boost::SinglePassRangeConcept<OutputType>));
                            BOOST_CONCEPT_ASSERT((boost::WriteableRangeConcept<OutputType>));

                            // https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-10#section-5.4.1
                            BOOST_ASSERT(len_in_bytes < 0x10000);
                            BOOST_ASSERT(std::distance(dst.begin(), dst.end()) >= 16 &&
                                         std::distance(dst.begin(), dst.end()) <= 255);
                            BOOST_ASSERT(std::distance(uniform_bytes.begin(), uniform_bytes.end()) >= len_in_bytes);

                            const std::array<std::uint8_t, 2> l_i_b_str = {
                                static_cast<std::uint8_t>(len_in_bytes >> 8u),
                                static_cast<std::uint8_t>(len_in_bytes % 0x100)};
                            const std::size_t ell = static_cast<std::size_t>(len_in_bytes / b_in_bytes) +
                                                    static_cast<std::size_t>(len_in_bytes % b_in_bytes != 0);

                            // https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-10#section-5.4.1
                            BOOST_ASSERT(ell <= 255);

                            // TODO: use accumulators when they will be fixed
                            // accumulator_set<HashType> b0_acc;
                            // hash<HashType>(Z_pad, b0_acc);
                            // hash<HashType>(msg, b0_acc);
                            // hash<HashType>(l_i_b_str, b0_acc);
                            // hash<HashType>(std::array<std::uint8_t, 1> {0}, b0_acc);
                            // hash<HashType>(dst, b0_acc);
                            // hash<HashType>(std::array<std::uint8_t, 1> {static_cast<std::uint8_t>(dst.size())},
                            // b0_acc); typename HashType::digest_type b0 =
                            // accumulators::extract::hash<HashType>(b0_acc);
                            std::vector<std::uint8_t> msg_prime;
                            msg_prime.insert(msg_prime.end(), Z_pad.begin(), Z_pad.end());
                            msg_prime.insert(msg_prime.end(), msg.begin(), msg.end());
                            msg_prime.insert(msg_prime.end(), l_i_b_str.begin(), l_i_b_str.end());
                            msg_prime.insert(msg_prime.end(), static_cast<std::uint8_t>(0));
                            msg_prime.insert(msg_prime.end(), dst.begin(), dst.end());
                            msg_prime.insert(msg_prime.end(),
                                             static_cast<std::uint8_t>(std::distance(dst.begin(), dst.end())));
                            typename HashType::digest_type b0 = hash<HashType>(msg_prime);

                            // TODO: use accumulators when they will be fixed
                            // accumulator_set<HashType> bi_acc;
                            // hash<HashType>(b0, bi_acc);
                            // hash<HashType>(std::array<std::uint8_t, 1> {1}, bi_acc);
                            // hash<HashType>(dst, bi_acc);
                            // hash<HashType>(std::array<std::uint8_t, 1> {static_cast<std::uint8_t>(dst.size())},
                            // bi_acc); typename HashType::digest_type bi =
                            // accumulators::extract::hash<HashType>(bi_acc); std::copy(bi.begin(), bi.end(),
                            // uniform_bytes.begin());
                            std::vector<std::uint8_t> b_i_str;
                            b_i_str.insert(b_i_str.end(), b0.begin(), b0.end());
                            b_i_str.insert(b_i_str.end(), static_cast<std::uint8_t>(1));
                            b_i_str.insert(b_i_str.end(), dst.begin(), dst.end());
                            b_i_str.insert(b_i_str.end(),
                                           static_cast<std::uint8_t>(std::distance(dst.begin(), dst.end())));
                            typename HashType::digest_type bi = hash<HashType>(b_i_str);
                            std::copy(bi.begin(), bi.end(), uniform_bytes.begin());

                            typename HashType::digest_type xored_b;
                            for (std::size_t i = 2; i <= ell; i++) {
                                // TODO: use accumulators when they will be fixed
                                // accumulator_set<HashType> bi_acc;
                                // strxor(b0, bi, xored_b);
                                // hash<HashType>(xored_b, bi_acc);
                                // hash<HashType>(std::array<std::uint8_t, 1> {static_cast<std::uint8_t>(i)}, bi_acc);
                                // hash<HashType>(dst, bi_acc);
                                // hash<HashType>(std::array<std::uint8_t, 1> {static_cast<std::uint8_t>(dst.size())},
                                //                bi_acc);
                                // bi = accumulators::extract::hash<HashType>(bi_acc);
                                // std::copy(bi.begin(), bi.end(), uniform_bytes.begin() + (i - 1) * b_in_bytes);
                                strxor(b0, bi, xored_b);
                                std::vector<std::uint8_t> b_i_str;
                                b_i_str.insert(b_i_str.end(), xored_b.begin(), xored_b.end());
                                b_i_str.insert(b_i_str.end(), static_cast<std::uint8_t>(i));
                                b_i_str.insert(b_i_str.end(), dst.begin(), dst.end());
                                b_i_str.insert(b_i_str.end(),
                                               static_cast<std::uint8_t>(std::distance(dst.begin(), dst.end())));
                                bi = hash<HashType>(b_i_str);
                                std::copy(bi.begin(), bi.end(), uniform_bytes.begin() + (i - 1) * b_in_bytes);
                            }
                        }
                    };
                }    // namespace detail
            }        // namespace curves
        }            // namespace algebra
    }                // namespace crypto3
}    // namespace nil

#endif    // CRYPTO3_ALGEBRA_CURVES_HASH_TO_CURVE_EXPAND_HPP


/*This executable creates test input file from the config file
  mentioned global var 'testConfigFile'. The config should contain every
  message as 1 line in the format of the struct mentioned in messages.h
  example:A,1000001,1,B,100,VOD,1234556,# */

#include <stdio.h>
#include <string.h>
#include <string>
#include <errno.h>
#include <messages.h>
#include <sys/types.h>
#include <stdint.h>
#include <iostream>
#include <boost/tokenizer.hpp>
using namespace messages;

const char *testConfigFile = "inp.asc";
const char *outputFile = "test.in";

int main()
{
	FILE *fp, *wp;
	fp = fopen(testConfigFile, "r");
	wp = fopen(outputFile, "w");
	char buff[256];
	if ( (wp == (FILE*) NULL) )
	{
		printf("Unable to open file to Write!!!!\n");
		exit(1);
	}

	if ( (fp != (FILE*) NULL) )
	{
		printf("File opened\n");
		uint16_t i=0;
		while ( fgets(buff, 256, fp ) )
		{
			i++;
			int arrsize = 0;
			char arr[100][100];
			char *pch= strtok(buff, ",");
			strcpy(arr[arrsize], pch);
			printf("Value is [%s][%d]", arr[arrsize], arrsize);
			while ( pch != NULL )
			{
				arrsize++;
				pch = strtok(NULL,",");
				if ( pch != NULL)
				{
					strcpy(arr[arrsize], pch);
					printf("Valus is [%s][%d]", arr[arrsize], arrsize);
				}
			}
			printf("Done\n");
			char msgType=buff[0];
			UdpHeader udpheader;
			uint32_t sn = ntohl(i);
			udpheader.seqNum = sn;

			switch (msgType)
			{
				case 'A':
						{
							udpheader.packetSize=ntohs(98);
							printf( "mesage is A\n");
							AddOrderIn add;
							add.msgType='A';
							uint64_t ti = std::stoll(arr[1]);
							add.timeStamp= be64toh(ti);
							uint64_t ord = std::stoll(arr[2]);
							add.orderRefNum = be64toh(ord);
							add.side = arr[3][0];
							add.size = ntohl(atol(arr[4]));
							memset(add.stockTicker, ' ', sizeof(add.stockTicker));
							memcpy(add.stockTicker, arr[5], strlen(arr[5]));
							//memcpy(add.stockTicker, arr[5], sizeof(add.stockTicker));
							add.price=ntohl(atol(arr[6]));
							fwrite((void*) & udpheader, sizeof(udpheader), 1,wp);
							fwrite((void*) & add, sizeof(add), 1,wp);
							fwrite((void*) & add, sizeof(add), 1,wp);
							fwrite((void*) & add, sizeof(add)-10, 1,wp);
							udpheader.packetSize=ntohs(16);
							i++;
							uint32_t sn = ntohl(i);
							udpheader.seqNum = sn;
							fwrite((void*) & udpheader, sizeof(udpheader), 1,wp);
							char *tmp = (char*)&add;
							fwrite((void*) tmp+24, 10, 1,wp);
						}
						break;
				case 'E':
						{
							sn = ntohl(i+2);
							udpheader.seqNum = sn;
							udpheader.packetSize=ntohs(27);
							printf( "mesage is E\n");
							OrderExecutedIn exec1;
							exec1.msgType='E';
							uint64_t ti = std::stoll(arr[1]);
							exec1.timeStamp= be64toh(ti);
							uint64_t ord = std::stoll(arr[2]);
							exec1.orderRefNum = be64toh(ord);
							exec1.size = ntohl(atol(arr[4]));
							fwrite((void*) & udpheader, sizeof(udpheader), 1,wp);
							fwrite((void*) & exec1, sizeof(exec1), 1,wp);
							uint32_t sn = ntohl(i+2);

							sn = ntohl(i+1);
							udpheader.seqNum = sn;
							udpheader.packetSize=ntohs(27);
							printf( "mesage is E\n");
							OrderExecutedIn exec2;
							exec2.msgType='E';
							uint64_t ti1 = std::stoll(arr[1]);
							exec2.timeStamp= be64toh(ti1);
							uint64_t ord1 = std::stoll(arr[2]);
							exec2.orderRefNum = be64toh(ord1);
							exec2.size = ntohl(atol(arr[4]));
							fwrite((void*) & udpheader, sizeof(udpheader), 1,wp);
							fwrite((void*) & exec2, sizeof(exec2), 1,wp);

						    sn = ntohl(i);
							udpheader.seqNum = sn;
							udpheader.packetSize=ntohs(27);
							printf( "mesage is E\n");
							OrderExecutedIn exec3;
							exec3.msgType='E';
							uint64_t ti2 = std::stoll(arr[1]);
							exec3.timeStamp= be64toh(ti2);
							uint64_t ord3 = std::stoll(arr[2]);
							exec3.orderRefNum = be64toh(ord3);
							exec3.size = ntohl(atol(arr[4]));
							fwrite((void*) & udpheader, sizeof(udpheader), 1,wp);
							fwrite((void*) & exec3, sizeof(exec3), 1,wp);
							i+=2;
						}
						break;
				case 'X':
						{
							udpheader.packetSize=ntohs(27);
							printf( "mesage is X\n");
							OrderCancelledIn cxl;
							cxl.msgType='X';
							uint64_t ti = std::stoll(arr[1]);
							cxl.timeStamp= be64toh(ti);
							uint64_t ord = std::stoll(arr[2]);
							cxl.orderRefNum = be64toh(ord);
							cxl.size = ntohl(atol(arr[4]));
							fwrite((void*) & udpheader, sizeof(udpheader), 1,wp);
							fwrite((void*) & cxl, sizeof(cxl), 1,wp);
						}
						break;

				case 'R':
						{
							udpheader.packetSize=ntohs(39);
							printf( "mesage is R\n");
							OrderReplacedIn rep;
							rep.msgType='R';
							uint64_t ti = std::stoll(arr[1]);
							rep.timeStamp= be64toh(ti);
							uint64_t ord = std::stoll(arr[2]);
							rep.orginalOrderRefNum = be64toh(ord);
							uint64_t nord = std::stoll(arr[3]);
							rep.newOrderRefNum = be64toh(nord);
							rep.size = ntohl(atol(arr[4]));
							rep.price=ntohl(atol(arr[5]));
							fwrite((void*) & udpheader, sizeof(udpheader), 1,wp);
							fwrite((void*) & rep, sizeof(rep), 1,wp);
						}
						break;
				default:
					printf("Incorrect input line in config file!!\n");
			}
		}
	}
	else
	{
		printf("Unable to open file[%s]--error- [%s]\n", testConfigFile,strerror(errno));
	}

}

Market data processing and testing/createTestInput.cpp


//=================================================================================================
//                    Copyright (C) 2016 Olivier Mallet - All Rights Reserved
//=================================================================================================

#ifndef URT_HPP
#define URT_HPP

//=================================================================================================

#ifdef USE_BLAZE

  // neutralizing OpenMP in Blaze if defined
  #ifdef _OPENMP
    #undef _OPENMP
    #define SET_OPENMP_BACK
  #endif

  #ifndef NDEBUG
    #define NDEBUG
  #endif

  #if defined(USE_MKL) || defined (USE_BLAS)
    #define BLAZE_BLAS_MODE 1
    #define BLAZE_USE_BLAS_MATRIX_VECTOR_MULTIPLICATION 1
    #define BLAZE_USE_BLAS_MATRIX_MATRIX_MULTIPLICATION 1
  #endif

  #ifndef BLAZE_BLAS_INCLUDE_FILE
    #ifdef USE_MKL
      #define BLAZE_BLAS_INCLUDE_FILE <mkl_cblas.h>
    #elif USE_BLAS
      #define BLAZE_BLAS_INCLUDE_FILE <cblas.h>
    #endif
  #endif

  #include <blaze/math/DynamicVector.h>
  #include <blaze/math/DynamicMatrix.h>
  #include <blaze/math/CustomVector.h>
  #include <blaze/math/CustomMatrix.h>

  namespace urt {
     template <typename T>
     using Matrix = blaze::DynamicMatrix<T, blaze::columnMajor>;
     template <typename T>
     using CMatrix = blaze::CustomMatrix<T, blaze::unaligned, blaze::unpadded, blaze::columnMajor>;
     template <typename T>
     using Vector = blaze::DynamicVector<T>;
     template <typename T>
     using CVector = blaze::CustomVector<T, blaze::unaligned, blaze::unpadded>;
  }

  #ifdef SET_OPENMP_BACK
    #define _OPENMP
  #endif

//*************************************************************************************************

#elif USE_EIGEN

  #ifdef USE_MKL
    #define EIGEN_USE_MKL_ALL
  #elif USE_BLAS
    #define EIGEN_USE_BLAS
  #endif

  #define EIGEN_DONT_PARALLELIZE
  #define EIGEN_NO_DEBUG
  #define EIGEN_NO_STATIC_ASSERT

  #include <Eigen/Dense>

  namespace urt {
     template <typename T>
     using Matrix = Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>;
     template <typename T>
     using Vector = Eigen::Matrix<T, Eigen::Dynamic, 1>;
 }

//*************************************************************************************************

#else

  #ifndef ARMA_DONT_USE_WRAPPER
    #if defined(USE_MKL) || defined (USE_BLAS)
      #define ARMA_DONT_USE_WRAPPER
    #endif
  #endif

  #ifndef ARMA_NO_DEBUG
    #define ARMA_NO_DEBUG
  #endif

  #ifndef ARMA_USE_CXX11
    #define ARMA_USE_CXX11
  #endif

  #include <armadillo>

  namespace urt {
     template <typename T>
     using Matrix = arma::Mat<T>;
     template <typename T>
     using Vector = arma::Col<T>;
  }

#endif

//*************************************************************************************************

#include <fstream>
#include <map>
#include <memory>

#include <boost/math/distributions/students_t.hpp>
#include <boost/math/special_functions/gamma.hpp>
#include <boost/random.hpp>
#include <boost/random/normal_distribution.hpp>
#include <boost/generator_iterator.hpp>

#include "./Tools.hpp"

#include "./OLS.hpp"
#include "./UnitRoot.hpp"
#include "./ADF.hpp"
#include "./DFGLS.hpp"
#include "./PP.hpp"
#include "./KPSS.hpp"

// template class specializations
/*
template class urt::OLS<double>;
template class urt::OLS<float>;
template class urt::UnitRoot<double>;
template class urt::UnitRoot<float>;
template class urt::ADF<double>;
template class urt::ADF<float>;
template class urt::DFGLS<double>;
template class urt::DFGLS<float>;
template class urt::PP<double>;
template class urt::PP<float>;
template class urt::KPSS<double>;
template class urt::KPSS<float>;
*/
//=================================================================================================

#endif