///////////////////////////////////////////////////////////////////////////////
// levels.hpp                                                                //
//                                                                           //
//  Copyright 2010 Erwann Rogard. Distributed under the Boost                //
//  Software License, Version 1.0. (See accompanying file                    //
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)         //
///////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_DETAIL_NON_PARAMETRIC_CONTINGENCY_TABLE_FACTOR_LEVELS_HPP_ER_2010
#define BOOST_STATISTICS_DETAIL_NON_PARAMETRIC_CONTINGENCY_TABLE_FACTOR_LEVELS_HPP_ER_2010
#include <vector>
#include <boost/config.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/type_traits/remove_const.hpp>
#include <boost/range.hpp>
#include <boost/parameter/keyword.hpp>
#include <boost/unordered_set.hpp>
#include <boost/fusion/include/at_key.hpp>
#include <boost/fusion/include/pair.hpp>
#include <boost/parameter/name.hpp>
#include <boost/accumulators/framework/extractor.hpp>
#include <boost/accumulators/framework/accumulator_base.hpp>
#include <boost/accumulators/framework/parameters/sample.hpp>
#include <boost/accumulators/framework/parameters/accumulator.hpp>
#include <boost/accumulators/framework/depends_on.hpp>
#include <boost/accumulators/statistics_fwd.hpp>
#include <boost/statistics/detail/non_parametric/contingency_table/factor/levels.hpp>
#include <boost/statistics/detail/non_parametric/contingency_table/factor/keyword.hpp>

// Source : http://www.ats.ucla.edu/stat/R/modules/factor_variables.htm

namespace boost {
namespace statistics{
namespace detail{
namespace contingency_table{

BOOST_PARAMETER_NAME(map_of_levels);

    template<typename K,typename T>
    struct levels_traits{
        typedef typename boost::fusion::result_of::at_key<T,K>::type ref_;
        typedef typename boost::remove_reference<ref_>::type qual_;
        typedef typename boost::remove_const<qual_>::type val_;
        typedef boost::unordered_set<val_> set_;
    };

namespace impl{

    template<typename Key,typename T>
    class levels : public boost::accumulators::accumulator_base
    {
        typedef boost::accumulators::dont_care dont_care_;
        typedef contingency_table::levels_traits<Key,T> traits_;
        typedef typename traits_::set_ set_;
        typedef contingency_table::kwd_levels<Key> kwd_;

        public:
        typedef Key key_type;
        typedef typename traits_::val_ data_type;

        typedef T sample_type;
        typedef set_& result_type;

        levels(dont_care_){}

        template<typename Args>
        explicit levels(const Args& args)
            :storage(
                make_set(
                    args[ _map_of_levels | set_() ]
                )
            )
            {
               // TODO allow either the above OR kwd_::instance. For now
               // this does not compile
            }

        void operator()(dont_care_)const{}

        result_type result(dont_care_)const{
           return this->storage;
        }

        private:
        static set_ make_set(const set_& s){ return s; }

        template<typename M>
        static set_ make_set(const M& map,typename M::fusion_tag* = 0){
            return make_set_impl( boost::fusion::at_key<Key>(map) );
        }

        template<typename L>
        static set_ make_set_impl(const L& levels){
            return set_(
                boost::begin( levels ),
                boost::end( levels )
            );
        }

        mutable set_ storage;
    };

}// impl

namespace tag
{
    template<typename Key>
    struct levels
      : boost::accumulators::depends_on<>
    {

        //typedef contingency_table::tpl_keyword<Key> keyword_;

        struct impl{
            template<typename T,typename W>
            struct apply{
                typedef contingency_table::impl::levels<Key,T> type;
            };
        };

    };

}// tag

namespace result_of{

    template<typename Key,typename AccSet>
    struct factor
    : boost::accumulators::detail::extractor_result<
        AccSet, contingency_table::tag::levels<Key> >{};

}// result_of

namespace extract
{

  	template<typename Key,typename AccSet>
    typename contingency_table::result_of::template
        factor<Key,AccSet>::type
  	levels(AccSet const& acc)
    {
    	typedef contingency_table::tag::levels<Key> the_tag;
        return boost::accumulators::extract_result<the_tag>(acc);
  	}

}// extract

using extract::levels;

}// contingency_table
}// detail
}// statistics
}// boost

#endif


#pragma once

#include <boost/python.hpp>
using namespace boost::python;

// given, lla, az1 and distance (s), return (lat2, lon2) and modify
// *az2 (starting return heading.)  Lat, lon, and azimuth are in
// degrees, distance in meters.
tuple py_geo_direct_wgs84 ( double lat1, double lon1, double az1, double s );

// given lat1, lon1, lat2, lon2, calculate starting and ending
// az1, az2 and distance (s).  Lat, lon, and azimuth are in degrees.
// distance in meters
tuple py_geo_inverse_wgs84( double lat1, double lon1, double lat2, double lon2 );


/*

Copyright (c) 2005-2020, University of Oxford.
All rights reserved.

University of Oxford means the Chancellor, Masters and Scholars of the
University of Oxford, having an administrative office at Wellington
Square, Oxford OX1 2JD, UK.

This file is part of Chaste.

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 University of Oxford 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 HOLDER 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.

*/

#ifndef TESTELLIPTICGROWINGDOMAINPDEMODIFIER_HPP_
#define TESTELLIPTICGROWINGDOMAINPDEMODIFIER_HPP_

#include <cxxtest/TestSuite.h>

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>

// This macro prevents errors with GCC 4.8 of form "unable to find numeric literal operator 'operator"" Q'"
// when compiling with -std=gnu++11 (see #2929). \todo: remove when GCC 4.8 is no longer supported.
#define BOOST_MATH_DISABLE_FLOAT128
#include <boost/math/special_functions/bessel.hpp>

#include "AbstractCellBasedWithTimingsTestSuite.hpp"
#include "ApoptoticCellProperty.hpp"
#include "AveragedSourceEllipticPde.hpp"
#include "CaBasedCellPopulation.hpp"
#include "CellsGenerator.hpp"
#include "CellwiseSourceEllipticPde.hpp"
#include "CheckpointArchiveTypes.hpp"
#include "DifferentiatedCellProliferativeType.hpp"
#include "EllipticGrowingDomainPdeModifier.hpp"
#include "FixedG1GenerationalCellCycleModel.hpp"
#include "HoneycombMeshGenerator.hpp"
#include "HoneycombVertexMeshGenerator.hpp"
#include "MeshBasedCellPopulation.hpp"
#include "NodeBasedCellPopulation.hpp"
#include "PottsBasedCellPopulation.hpp"
#include "PottsMeshGenerator.hpp"
#include "ReplicatableVector.hpp"
#include "SmartPointers.hpp"
#include "UniformCellCycleModel.hpp"
#include "UniformSourceEllipticPde.hpp"
#include "VertexBasedCellPopulation.hpp"

// This test is always run sequentially (never in parallel)
#include "FakePetscSetup.hpp"

/*
 * In this test suite we check the solution of the CellwisePdes against exact solutions.
 * In each case we are solving Laplacian U = f where f is constant in different regions.
 * We solve unit disc where the solutions are Bessel functions and logs.
 */
class TestEllipticGrowingDomainPdeModifier : public AbstractCellBasedWithTimingsTestSuite
{
public:

    void TestEllipticConstructor()
    {
        // Create PDE and boundary condition objects
        MAKE_PTR_ARGS(UniformSourceEllipticPde<2>, p_pde, (-0.1));
        MAKE_PTR_ARGS(ConstBoundaryCondition<2>, p_bc, (1.0));

        // Create a PDE modifier and set the name of the dependent variable in the PDE
        MAKE_PTR_ARGS(EllipticGrowingDomainPdeModifier<2>, p_pde_modifier, (p_pde, p_bc, false));
        p_pde_modifier->SetDependentVariableName("averaged quantity");

        // Test that member variables are initialised correctly
        TS_ASSERT_EQUALS(p_pde_modifier->rGetDependentVariableName(), "averaged quantity");
    }

    void TestMeshGeneration()
    {
        // Create PDE and boundary condition objects to be used by all cell populations
        MAKE_PTR_ARGS(UniformSourceEllipticPde<2>, p_pde, (-0.1));
        MAKE_PTR_ARGS(ConstBoundaryCondition<2>, p_bc, (1.0));

        // Create a CellsGenerator to be used by all cell populations
        CellsGenerator<FixedG1GenerationalCellCycleModel, 2> cells_generator;

        // Create a PDE modifier and set the name of the dependent variable in the PDE
        MAKE_PTR_ARGS(EllipticGrowingDomainPdeModifier<2>, p_pde_modifier, (p_pde, p_bc, false));
        p_pde_modifier->SetDependentVariableName("averaged quantity");
        {
            // Create a MeshBasedCellPopulation
            HoneycombMeshGenerator generator(10, 10, 0);
            MutableMesh<2,2>* p_mesh = generator.GetMesh();

            std::vector<CellPtr> mesh_cells;
            cells_generator.GenerateBasic(mesh_cells, p_mesh->GetNumNodes());

            MeshBasedCellPopulation<2> mesh_cell_population(*p_mesh, mesh_cells);

            // Now generate the finite element mesh
            p_pde_modifier->GenerateFeMesh(mesh_cell_population);

            // Check that the meshes have the same nodes
            for (unsigned i=0; i<p_mesh->GetNumNodes(); i++)
            {
                TS_ASSERT_DELTA(p_pde_modifier->mpFeMesh->GetNode(i)->rGetLocation()[0], p_mesh->GetNode(i)->rGetLocation()[0], 1e-5);
                TS_ASSERT_DELTA(p_pde_modifier->mpFeMesh->GetNode(i)->rGetLocation()[1], p_mesh->GetNode(i)->rGetLocation()[1], 1e-5);
                TS_ASSERT_EQUALS(p_pde_modifier->mpFeMesh->GetNode(i)->IsBoundaryNode(), p_mesh->GetNode(i)->IsBoundaryNode());
            }
        }

        {
            // Make a NodeBasedCellPopulation
            HoneycombMeshGenerator generator(10, 10, 0);
            MutableMesh<2,2>* p_mesh = generator.GetMesh();
            NodesOnlyMesh<2> node_mesh;
            node_mesh.ConstructNodesWithoutMesh(*p_mesh, 1.5);

            std::vector<CellPtr> node_cells;
            cells_generator.GenerateBasic(node_cells, node_mesh.GetNumNodes());

            NodeBasedCellPopulation<2> node_cell_population(node_mesh, node_cells);

            // Now generate the finite element mesh
            p_pde_modifier->GenerateFeMesh(node_cell_population);

            // Check that the meshes have the same nodes
            for (unsigned i=0; i<node_mesh.GetNumNodes(); i++)
            {
                TS_ASSERT_DELTA(p_pde_modifier->mpFeMesh->GetNode(i)->rGetLocation()[0], node_mesh.GetNode(i)->rGetLocation()[0],1e-5);
                TS_ASSERT_DELTA(p_pde_modifier->mpFeMesh->GetNode(i)->rGetLocation()[1], node_mesh.GetNode(i)->rGetLocation()[1],1e-5);
            }
        }

        {
            // Make a VertexBasedCellPopulation
            HoneycombVertexMeshGenerator generator(10, 10);
            MutableVertexMesh<2,2>* p_mesh = generator.GetMesh();

            std::vector<CellPtr> vertex_cells;
            cells_generator.GenerateBasic(vertex_cells, p_mesh->GetNumElements());

            VertexBasedCellPopulation<2> vertex_cell_population(*p_mesh, vertex_cells);

            // Now generate the finite element mesh
            p_pde_modifier->GenerateFeMesh(vertex_cell_population);

            // Check that the meshes have the same nodes
            for (unsigned i=0; i<p_mesh->GetNumNodes(); i++)
            {
                TS_ASSERT_DELTA(p_pde_modifier->mpFeMesh->GetNode(i)->rGetLocation()[0], p_mesh->GetNode(i)->rGetLocation()[0],1e-5);
                TS_ASSERT_DELTA(p_pde_modifier->mpFeMesh->GetNode(i)->rGetLocation()[1], p_mesh->GetNode(i)->rGetLocation()[1],1e-5);

                TS_ASSERT_EQUALS(p_pde_modifier->mpFeMesh->GetNode(i)->IsBoundaryNode(), p_mesh->GetNode(i)->IsBoundaryNode());
            }
            // New node at every element centre
            for (unsigned i=0; i<p_mesh->GetNumElements(); i++)
            {
                TS_ASSERT_DELTA(p_pde_modifier->mpFeMesh->GetNode(i+p_mesh->GetNumNodes())->rGetLocation()[0], p_mesh->GetCentroidOfElement(i)[0],1e-5);
                TS_ASSERT_DELTA(p_pde_modifier->mpFeMesh->GetNode(i+p_mesh->GetNumNodes())->rGetLocation()[1], p_mesh->GetCentroidOfElement(i)[1],1e-5);

                TS_ASSERT_EQUALS(p_pde_modifier->mpFeMesh->GetNode(i+p_mesh->GetNumNodes())->IsBoundaryNode(), false);
            }
        }

        {
            // Make a PottsBasedCellPopulation
            PottsMeshGenerator<2> generator(50,5,5,50,5,5);
            PottsMesh<2>* p_mesh = generator.GetMesh();

            std::vector<CellPtr> potts_cells;
            cells_generator.GenerateBasic(potts_cells, p_mesh->GetNumElements());

            PottsBasedCellPopulation<2> potts_cell_population(*p_mesh, potts_cells);

            // Now generate the finite element mesh
            p_pde_modifier->GenerateFeMesh(potts_cell_population);

            // Check that the meshes have the same nodes
            for (unsigned i=0; i<p_mesh->GetNumElements(); i++)
            {
                TS_ASSERT_DELTA(p_pde_modifier->mpFeMesh->GetNode(i)->rGetLocation()[0], p_mesh->GetCentroidOfElement(i)[0],1e-5);
                TS_ASSERT_DELTA(p_pde_modifier->mpFeMesh->GetNode(i)->rGetLocation()[1], p_mesh->GetCentroidOfElement(i)[1],1e-5);
            }
        }

        {
            // Make a CaBasedCellPopulation
            PottsMeshGenerator<2> generator(50,0,0,50,0,0);
            PottsMesh<2>* p_mesh = generator.GetMesh();

            // Specify the location of each cell
            std::vector<unsigned> location_indices;
            for (unsigned i=0; i<10; i++)
            {
                for (unsigned j=0; j<10; j++)
                {
                    unsigned offset = (50+1) * (50-10)/2;
                    location_indices.push_back(offset + j + i * 50);
                }
            }

            std::vector<CellPtr> ca_cells;
            cells_generator.GenerateBasic(ca_cells, location_indices.size());

            // Create cell population
            CaBasedCellPopulation<2> ca_cell_population(*p_mesh, ca_cells, location_indices);

            // Now generate the finite element mesh
            p_pde_modifier->GenerateFeMesh(ca_cell_population);

            // Check that the mesh has nodes at the centre of the cells
            unsigned i=0;
            for (AbstractCellPopulation<2>::Iterator cell_iter = ca_cell_population.Begin();
                 cell_iter != ca_cell_population.End();
                 ++cell_iter)
            {
                TS_ASSERT_DELTA(p_pde_modifier->mpFeMesh->GetNode(i)->rGetLocation()[0], ca_cell_population.GetLocationOfCellCentre(*cell_iter)[0],1e-5);
                TS_ASSERT_DELTA(p_pde_modifier->mpFeMesh->GetNode(i)->rGetLocation()[1], ca_cell_population.GetLocationOfCellCentre(*cell_iter)[1],1e-5);
                ++i;
            }
        }
    }

    void TestGrowingDomainPdeModifierExceptions()
    {
        EXIT_IF_PARALLEL;

        // Create a simple mesh
        TrianglesMeshReader<2,2> mesh_reader("mesh/test/data/disk_522_elements");
        TetrahedralMesh<2,2> temp_mesh;
        temp_mesh.ConstructFromMeshReader(mesh_reader);
        temp_mesh.Scale(5.0,1.0);

        NodesOnlyMesh<2> mesh;
        mesh.ConstructNodesWithoutMesh(temp_mesh, 1.5);

        // Set up cells
        std::vector<CellPtr> cells;
        MAKE_PTR(WildTypeCellMutationState, p_state);
        MAKE_PTR(DifferentiatedCellProliferativeType, p_diff_type);
        for (unsigned i=0; i<mesh.GetNumNodes(); i++)
        {
            FixedG1GenerationalCellCycleModel* p_model = new FixedG1GenerationalCellCycleModel();
            p_model->SetDimension(2);

            CellPtr p_cell(new Cell(p_state, p_model));
            p_cell->SetCellProliferativeType(p_diff_type);
            double birth_time = -RandomNumberGenerator::Instance()->ranf()*18.0;
            p_cell->SetBirthTime(birth_time);

            cells.push_back(p_cell);
        }

        // Set up cell population
        NodeBasedCellPopulation<2> cell_population(mesh, cells);

        // Create PDE and boundary condition objects
        MAKE_PTR_ARGS(AveragedSourceEllipticPde<2>, p_pde, (cell_population, -1.0));
        MAKE_PTR_ARGS(ConstBoundaryCondition<2>, p_bc, (1.0));

        // Create a PDE modifier and set the name of the dependent variable in the PDE
        MAKE_PTR_ARGS(EllipticGrowingDomainPdeModifier<2>, p_pde_modifier, (p_pde, p_bc, false));
        p_pde_modifier->SetDependentVariableName("nutrient");

        TS_ASSERT_THROWS_THIS(p_pde_modifier->SetupSolve(cell_population, "output_directory"),
            "EllipticGrowingDomainPdeModifier cannot be used with an AveragedSourceEllipticPde. Use an EllipticBoxDomainPdeModifier instead.");
    }

    void TestArchiveEllipticGrowingDomainPdeModifier()
    {
        // Create a file for archiving
        OutputFileHandler handler("archive", false);
        handler.SetArchiveDirectory();
        std::string archive_filename = handler.GetOutputDirectoryFullPath() + "EllipticGrowingDomainPdeModifier.arch";

        // Separate scope to write the archive
        {
            // Create PDE and boundary condition objects
            MAKE_PTR_ARGS(UniformSourceEllipticPde<2>, p_pde, (-0.1));
            MAKE_PTR_ARGS(ConstBoundaryCondition<2>, p_bc, (1.0));

            // Create a PDE modifier and set the name of the dependent variable in the PDE
            std::vector<double> data(10);
            for (unsigned i=0; i<10; i++)
            {
                data[i] = i + 0.45;
            }
            Vec vector = PetscTools::CreateVec(data);
            EllipticGrowingDomainPdeModifier<2> modifier(p_pde, p_bc, false, vector);
            modifier.SetDependentVariableName("averaged quantity");

            // Create an output archive
            std::ofstream ofs(archive_filename.c_str());
            boost::archive::text_oarchive output_arch(ofs);

            // Serialize via pointer
            AbstractCellBasedSimulationModifier<2,2>* const p_modifier = &modifier;
            output_arch << p_modifier;
        }

        // Separate scope to read the archive
        {
            AbstractCellBasedSimulationModifier<2,2>* p_modifier2;

            // Restore the modifier
            std::ifstream ifs(archive_filename.c_str());
            boost::archive::text_iarchive input_arch(ifs);

            input_arch >> p_modifier2;

            // See whether we read out the correct variable name area
            std::string variable_name = (static_cast<EllipticGrowingDomainPdeModifier<2>*>(p_modifier2))->rGetDependentVariableName();
            TS_ASSERT_EQUALS(variable_name, "averaged quantity");

            Vec solution = (static_cast<EllipticGrowingDomainPdeModifier<2>*>(p_modifier2))->GetSolution();
            ReplicatableVector solution_repl(solution);

            TS_ASSERT_EQUALS(solution_repl.GetSize(), 10u);
            for (unsigned i=0; i<10; i++)
            {
                TS_ASSERT_DELTA(solution_repl[i], i + 0.45, 1e-6);
            }

            delete p_modifier2;
        }
    }

    /*
     * Here the exact solution is
     *
     * u = J0(r)/J0(1)
     *
     * where J0 is the zeroth order Bessel function.
     */
    void TestMeshBasedMonolayerWithEllipticPde()
    {
        TrianglesMeshReader<2,2> mesh_reader("mesh/test/data/disk_984_elements");
        MutableMesh<2,2> mesh;
        mesh.ConstructFromMeshReader(mesh_reader);

        std::vector<CellPtr> cells;
        MAKE_PTR(DifferentiatedCellProliferativeType, p_differentiated_type);
        CellsGenerator<UniformCellCycleModel, 2> cells_generator;
        cells_generator.GenerateBasicRandom(cells, mesh.GetNumNodes(), p_differentiated_type);

        MeshBasedCellPopulation<2> cell_population(mesh, cells);

        // Set up simulation time for file output
        SimulationTime::Instance()->SetEndTimeAndNumberOfTimeSteps(1.0, 1);

        // Create PDE and boundary condition objects
        MAKE_PTR_ARGS(CellwiseSourceEllipticPde<2>, p_pde, (cell_population, 1));
        MAKE_PTR_ARGS(ConstBoundaryCondition<2>, p_bc, (1.0));

        // Create a PDE modifier and set the name of the dependent variable in the PDE
        MAKE_PTR_ARGS(EllipticGrowingDomainPdeModifier<2>, p_pde_modifier, (p_pde, p_bc, false));
        p_pde_modifier->SetDependentVariableName("variable");

        // For coverage output the solution gradient
        p_pde_modifier->SetOutputGradient(true);

        p_pde_modifier->SetupSolve(cell_population,"TestCellwiseEllipticPdeWithMeshOnDisk");

        // Test the solution against the exact solution
        for (AbstractCellPopulation<2>::Iterator cell_iter = cell_population.Begin();
                cell_iter != cell_population.End();
                ++cell_iter)
        {
            c_vector<double,2> cell_location = cell_population.GetLocationOfCellCentre(*cell_iter);
            double r = sqrt(cell_location(0)*cell_location(0) + cell_location(1)*cell_location(1));
            double u_exact = boost::math::cyl_bessel_j(0,r) / boost::math::cyl_bessel_j(0,1);

            TS_ASSERT_DELTA(cell_iter->GetCellData()->GetItem("variable"), u_exact, 1e-3);

            double ux_exact = -cell_location(0) / r * boost::math::cyl_bessel_j(1,r) / boost::math::cyl_bessel_j(0,1);
            double uy_exact = -cell_location(1) / r * boost::math::cyl_bessel_j(1,r) / boost::math::cyl_bessel_j(0,1);

            TS_ASSERT_DELTA(cell_iter->GetCellData()->GetItem("variable_grad_x"), ux_exact, 1e-1);
            TS_ASSERT_DELTA(cell_iter->GetCellData()->GetItem("variable_grad_y"), uy_exact, 1e-1);
        }
    }

    /*
     * Here the outer cells (r>1/2) are apoptotic so the solution is
     *
     * u = C*J0(r)  for r in [0,0.5]
     *     A*ln(r) + 1 for r in [0.5,1]
     *
     *  where J0 is the zeroth order bessel fn and C and A are constants.
     */
    void TestMeshBasedHeterogeneousMonolayerWithEllipticPde()
    {
        TrianglesMeshReader<2,2> mesh_reader("mesh/test/data/disk_984_elements");
        MutableMesh<2,2> mesh;
        mesh.ConstructFromMeshReader(mesh_reader);

        std::vector<CellPtr> cells;
        MAKE_PTR(DifferentiatedCellProliferativeType, p_differentiated_type);
        CellsGenerator<UniformCellCycleModel, 2> cells_generator;
        cells_generator.GenerateBasicRandom(cells, mesh.GetNumNodes(), p_differentiated_type);

        // Make cells with r<1/2 apoptotic (so no source term)
        boost::shared_ptr<AbstractCellProperty> p_apoptotic_property =
                cells[0]->rGetCellPropertyCollection().GetCellPropertyRegistry()->Get<ApoptoticCellProperty>();
        for (unsigned i=0; i<cells.size(); i++)
        {
            c_vector<double,2> cell_location;
            cell_location = mesh.GetNode(i)->rGetLocation();
            double r = sqrt(cell_location(0)*cell_location(0) + cell_location(1)*cell_location(1));
            if (r > 0.5)
            {
                cells[i]->AddCellProperty(p_apoptotic_property);
            }
        }

        MeshBasedCellPopulation<2> cell_population(mesh, cells);

        // Set up simulation time for file output
        SimulationTime::Instance()->SetEndTimeAndNumberOfTimeSteps(1.0, 1);

        // Create PDE and boundary condition objects
        MAKE_PTR_ARGS(CellwiseSourceEllipticPde<2>, p_pde, (cell_population, 1));
        MAKE_PTR_ARGS(ConstBoundaryCondition<2>, p_bc, (1.0));

        // Create a PDE modifier and set the name of the dependent variable in the PDE
        MAKE_PTR_ARGS(EllipticGrowingDomainPdeModifier<2>, p_pde_modifier, (p_pde, p_bc, false));
        p_pde_modifier->SetDependentVariableName("variable");

        p_pde_modifier->SetupSolve(cell_population,"TestCellwiseEllipticPdeWithMeshOnHeterogeneousDisk");

        // Test the solution against the exact solution
        for (AbstractCellPopulation<2>::Iterator cell_iter = cell_population.Begin();
                cell_iter != cell_population.End();
                ++cell_iter)
        {

            c_vector<double,2> cell_location = cell_population.GetLocationOfCellCentre(*cell_iter);
            double r = sqrt(cell_location(0)*cell_location(0) + cell_location(1)*cell_location(1));

            double J005 = boost::math::cyl_bessel_j(0,0.5);
            double J105 = boost::math::cyl_bessel_j(1,0.5);

            double A = -1.0/(2.0*J005/J105 +log(0.5));
            double C = -2*A/J105;

            double u_exact = C*boost::math::cyl_bessel_j(0,r);
            if (r > 0.5)
            {
                u_exact = A*log(r)+1.0;
            }

            TS_ASSERT_DELTA(cell_iter->GetCellData()->GetItem("variable"), u_exact, 1e-2);
        }
    }

    // Now test on a square with half apoptotic cells to compare all the population types
    void TestMeshBasedSquareMonolayer()
    {
        HoneycombMeshGenerator generator(20,20,0);
        MutableMesh<2,2>* p_mesh = generator.GetMesh();

        std::vector<CellPtr> cells;
        MAKE_PTR(DifferentiatedCellProliferativeType, p_differentiated_type);
        CellsGenerator<UniformCellCycleModel, 2> cells_generator;
        cells_generator.GenerateBasicRandom(cells, p_mesh->GetNumNodes(), p_differentiated_type);

        // Make cells with x<10.0 apoptotic (so no source term)
        boost::shared_ptr<AbstractCellProperty> p_apoptotic_property =
                cells[0]->rGetCellPropertyCollection().GetCellPropertyRegistry()->Get<ApoptoticCellProperty>();
        for (unsigned i=0; i<cells.size(); i++)
        {
            c_vector<double,2> cell_location;
            cell_location = p_mesh->GetNode(i)->rGetLocation();
            if (cell_location(0) < 10.0)
            {
                cells[i]->AddCellProperty(p_apoptotic_property);
            }
        }
        TS_ASSERT_EQUALS(p_apoptotic_property->GetCellCount(), 200u);

        MeshBasedCellPopulation<2> cell_population(*p_mesh, cells);

        // Set up simulation time for file output
        SimulationTime::Instance()->SetEndTimeAndNumberOfTimeSteps(1.0, 1);

        // Create PDE and boundary condition objects
        MAKE_PTR_ARGS(CellwiseSourceEllipticPde<2>, p_pde, (cell_population, -0.1));
        MAKE_PTR_ARGS(ConstBoundaryCondition<2>, p_bc, (1.0));

        // Create a PDE modifier and set the name of the dependent variable in the PDE
        MAKE_PTR_ARGS(EllipticGrowingDomainPdeModifier<2>, p_pde_modifier, (p_pde, p_bc, false));
        p_pde_modifier->SetDependentVariableName("variable");

        p_pde_modifier->SetupSolve(cell_population,"TestCellwiseEllipticPdeWithMeshOnSquare");

        // Test the solution at some fixed points to compare with other cell populations
        CellPtr p_cell_210 = cell_population.GetCellUsingLocationIndex(210);
        TS_ASSERT_DELTA(cell_population.GetLocationOfCellCentre(p_cell_210)[0], 10, 1e-4);
        TS_ASSERT_DELTA(cell_population.GetLocationOfCellCentre(p_cell_210)[1], 5.0*sqrt(3.0), 1e-4);
        TS_ASSERT_DELTA(p_cell_210->GetCellData()->GetItem("variable"), 0.4542, 1e-4);
    }

    void TestNodeBasedSquareMonolayer()
    {
        HoneycombMeshGenerator generator(20,20,0);
        MutableMesh<2,2>* p_generating_mesh = generator.GetMesh();
        NodesOnlyMesh<2>* p_mesh = new NodesOnlyMesh<2>;
        p_mesh->ConstructNodesWithoutMesh(*p_generating_mesh, 1.5);

        std::vector<CellPtr> cells;
        MAKE_PTR(DifferentiatedCellProliferativeType, p_differentiated_type);
        CellsGenerator<UniformCellCycleModel, 2> cells_generator;
        cells_generator.GenerateBasicRandom(cells, p_mesh->GetNumNodes(), p_differentiated_type);

        // Make cells with x<10.0 apoptotic (so no source term)
        boost::shared_ptr<AbstractCellProperty> p_apoptotic_property =
                cells[0]->rGetCellPropertyCollection().GetCellPropertyRegistry()->Get<ApoptoticCellProperty>();
        for (unsigned i=0; i<cells.size(); i++)
        {
            c_vector<double,2> cell_location;
            cell_location = p_mesh->GetNode(i)->rGetLocation();
            if (cell_location(0) < 10.0)
            {
                cells[i]->AddCellProperty(p_apoptotic_property);
            }
        }
        TS_ASSERT_EQUALS(p_apoptotic_property->GetCellCount(),200u);

        NodeBasedCellPopulation<2> cell_population(*p_mesh, cells);

        // Set up simulation time for file output
        SimulationTime::Instance()->SetEndTimeAndNumberOfTimeSteps(1.0, 1);

        // Create PDE and boundary condition objects
        MAKE_PTR_ARGS(CellwiseSourceEllipticPde<2>, p_pde, (cell_population, -0.1));
        MAKE_PTR_ARGS(ConstBoundaryCondition<2>, p_bc, (1.0));

        // Create a PDE modifier and set the name of the dependent variable in the PDE
        MAKE_PTR_ARGS(EllipticGrowingDomainPdeModifier<2>, p_pde_modifier, (p_pde, p_bc, false));
        p_pde_modifier->SetDependentVariableName("variable");

        p_pde_modifier->SetupSolve(cell_population,"TestCellwiseEllipticPdeWithNodeOnSquare");

        // Test the solution at some fixed points to compare with other cell populations
        CellPtr p_cell_210 = cell_population.GetCellUsingLocationIndex(210);
        TS_ASSERT_DELTA(cell_population.GetLocationOfCellCentre(p_cell_210)[0], 10, 1e-4);
        TS_ASSERT_DELTA(cell_population.GetLocationOfCellCentre(p_cell_210)[1], 5.0*sqrt(3.0), 1e-4);
        TS_ASSERT_DELTA( p_cell_210->GetCellData()->GetItem("variable"), 0.4542, 1e-2); // Lower tolerance as slightly different meshes

        // Checking it doesn't change for this cell population
        TS_ASSERT_DELTA(p_cell_210->GetCellData()->GetItem("variable"), 0.4476, 1e-4);

        // Clear memory
        delete p_mesh;
    }

    void TestVertexBasedSquareMonolayer()
    {
        HoneycombVertexMeshGenerator generator(20,20);
        MutableVertexMesh<2,2>* p_mesh = generator.GetMesh();

        p_mesh->Translate(-0.5,-sqrt(3.0)/3); // Shift so cells are on top of those in the above centre-based tests

        std::vector<CellPtr> cells;
        MAKE_PTR(DifferentiatedCellProliferativeType, p_differentiated_type);
        CellsGenerator<UniformCellCycleModel, 2> cells_generator;
        cells_generator.GenerateBasicRandom(cells, p_mesh->GetNumElements(), p_differentiated_type);

        // Make cells with x<10.0 apoptotic (so no source term)
        boost::shared_ptr<AbstractCellProperty> p_apoptotic_property =
                cells[0]->rGetCellPropertyCollection().GetCellPropertyRegistry()->Get<ApoptoticCellProperty>();
        for (unsigned i=0; i<cells.size(); i++)
        {
            c_vector<double,2> cell_location;
            cell_location = p_mesh->GetCentroidOfElement(i);
            if (cell_location(0) < 10.0)
            {
                cells[i]->AddCellProperty(p_apoptotic_property);
            }
        }
        TS_ASSERT_EQUALS(p_apoptotic_property->GetCellCount(),200u);

        VertexBasedCellPopulation<2> cell_population(*p_mesh, cells);

        // Set up simulation time for file output
        SimulationTime::Instance()->SetEndTimeAndNumberOfTimeSteps(1.0, 1);

        // Create PDE and boundary condition objects
        MAKE_PTR_ARGS(CellwiseSourceEllipticPde<2>, p_pde, (cell_population, -0.1));
        MAKE_PTR_ARGS(ConstBoundaryCondition<2>, p_bc, (1.0));

        // Create a PDE modifier and set the name of the dependent variable in the PDE
        MAKE_PTR_ARGS(EllipticGrowingDomainPdeModifier<2>, p_pde_modifier, (p_pde, p_bc, false));
        p_pde_modifier->SetDependentVariableName("variable");

        p_pde_modifier->SetupSolve(cell_population,"TestCellwiseEllipticPdeWithVertexOnSquare");

        // Test the solution at some fixed points to compare with other cell populations
        CellPtr p_cell_210 = cell_population.GetCellUsingLocationIndex(210);
        TS_ASSERT_DELTA(cell_population.GetLocationOfCellCentre(p_cell_210)[0], 10, 1e-4);
        TS_ASSERT_DELTA(cell_population.GetLocationOfCellCentre(p_cell_210)[1], 5.0*sqrt(3.0), 1e-4);
        TS_ASSERT_DELTA(p_cell_210->GetCellData()->GetItem("variable"), 0.4542, 1e-1); // Low tolerance as mesh is slightly larger than for centre based models

        // Checking it doesn't change for this cell population
        TS_ASSERT_DELTA(p_cell_210->GetCellData()->GetItem("variable"), 0.4654, 1e-4);
    }

    void TestPottsBasedSquareMonolayer()
    {
        PottsMeshGenerator<2> generator(100, 20, 4, 100, 20, 4);
        PottsMesh<2>* p_mesh = generator.GetMesh();

        // Translate and scale so cells are on top of those in the above centre based tests
        p_mesh->Translate(-11.5,-11.5);
        p_mesh->Scale(0.25,0.25 *sqrt(3.0)*0.5);

        std::vector<CellPtr> cells;
        MAKE_PTR(DifferentiatedCellProliferativeType, p_differentiated_type);
        CellsGenerator<UniformCellCycleModel, 2> cells_generator;
        cells_generator.GenerateBasicRandom(cells, p_mesh->GetNumElements(), p_differentiated_type);

        // Make cells with x<10.0 apoptotic (so no source term)
        boost::shared_ptr<AbstractCellProperty> p_apoptotic_property =
                cells[0]->rGetCellPropertyCollection().GetCellPropertyRegistry()->Get<ApoptoticCellProperty>();
        for (unsigned i=0; i<cells.size(); i++)
        {
            c_vector<double,2> cell_location;
            cell_location = p_mesh->GetCentroidOfElement(i);
            if (cell_location(0) < 10.0)
            {
                cells[i]->AddCellProperty(p_apoptotic_property);
            }
        }
        TS_ASSERT_EQUALS(p_apoptotic_property->GetCellCount(),200u);

        PottsBasedCellPopulation<2> cell_population(*p_mesh, cells);

        // Set up simulation time for file output
        SimulationTime::Instance()->SetEndTimeAndNumberOfTimeSteps(1.0, 1);

        // Create PDE and boundary condition objects
        MAKE_PTR_ARGS(CellwiseSourceEllipticPde<2>, p_pde, (cell_population, -0.1));
        MAKE_PTR_ARGS(ConstBoundaryCondition<2>, p_bc, (1.0));

        // Create a PDE modifier and set the name of the dependent variable in the PDE
        MAKE_PTR_ARGS(EllipticGrowingDomainPdeModifier<2>, p_pde_modifier, (p_pde, p_bc, false));
        p_pde_modifier->SetDependentVariableName("variable");

        p_pde_modifier->SetupSolve(cell_population,"TestCellwiseEllipticPdeWithPottsOnSquare");

        // Test the solution at some fixed points to compare with other cell populations
        CellPtr p_cell_210 = cell_population.GetCellUsingLocationIndex(210);
        TS_ASSERT_DELTA(cell_population.GetLocationOfCellCentre(p_cell_210)[0], 10, 1e-4);
        TS_ASSERT_DELTA(cell_population.GetLocationOfCellCentre(p_cell_210)[1], 5.0*sqrt(3.0), 1e-4);
        TS_ASSERT_DELTA( p_cell_210->GetCellData()->GetItem("variable"), 0.4542, 2e-1); // Low tolerance as mesh is slightly larger than for centre based models

        // Check it doesn't change for this cell population
        TS_ASSERT_DELTA(p_cell_210->GetCellData()->GetItem("variable"), 0.4338, 1e-4);
    }

    void TestCaBasedSquareMonolayer()
    {
        PottsMeshGenerator<2> generator(20, 0, 0, 20, 0, 0);
        PottsMesh<2>* p_mesh = generator.GetMesh();

        // Scale so cells are on top of those in the above centre based tests
        p_mesh->Scale(1.0,sqrt(3.0)*0.5);

        // Specify where cells lie
        std::vector<unsigned> location_indices;
        for (unsigned i=0; i<400; i++)
        {
            location_indices.push_back(i);
        }

        std::vector<CellPtr> cells;
        MAKE_PTR(DifferentiatedCellProliferativeType, p_differentiated_type);
        CellsGenerator<UniformCellCycleModel, 2> cells_generator;
        cells_generator.GenerateBasicRandom(cells, location_indices.size(), p_differentiated_type);

        // Make cells with x<10.0 apoptotic (so no source term)
        boost::shared_ptr<AbstractCellProperty> p_apoptotic_property =
                cells[0]->rGetCellPropertyCollection().GetCellPropertyRegistry()->Get<ApoptoticCellProperty>();
        for (unsigned i=0; i<cells.size(); i++)
        {
            c_vector<double,2> cell_location;
            cell_location = p_mesh->GetNode(i)->rGetLocation();
            if (cell_location(0) < 10.0)
            {
                cells[i]->AddCellProperty(p_apoptotic_property);
            }
        }
        TS_ASSERT_EQUALS(p_apoptotic_property->GetCellCount(),200u);

        CaBasedCellPopulation<2> cell_population(*p_mesh, cells, location_indices);

        // Set up simulation time for file output
        SimulationTime::Instance()->SetEndTimeAndNumberOfTimeSteps(1.0, 1);

        // Create PDE and boundary condition objects
        MAKE_PTR_ARGS(CellwiseSourceEllipticPde<2>, p_pde, (cell_population, -0.1));
        MAKE_PTR_ARGS(ConstBoundaryCondition<2>, p_bc, (1.0));

        // Create a PDE modifier and set the name of the dependent variable in the PDE
        MAKE_PTR_ARGS(EllipticGrowingDomainPdeModifier<2>, p_pde_modifier, (p_pde, p_bc, false));
        p_pde_modifier->SetDependentVariableName("variable");

        p_pde_modifier->SetupSolve(cell_population,"TestCellwiseEllipticPdeWithCaOnSquare");

        // Test the solution at some fixed points to compare with other cell populations
        CellPtr p_cell_210 = cell_population.GetCellUsingLocationIndex(210);
        TS_ASSERT_DELTA(cell_population.GetLocationOfCellCentre(p_cell_210)[0], 10, 1e-4);
        TS_ASSERT_DELTA(cell_population.GetLocationOfCellCentre(p_cell_210)[1], 5.0*sqrt(3.0), 1e-4);
        TS_ASSERT_DELTA(p_cell_210->GetCellData()->GetItem("variable"), 0.4542, 2e-1); // Low tolerance as mesh is slightlty larger than for centre-based models

        // Check it doesn't change for this cell population
        TS_ASSERT_DELTA(p_cell_210->GetCellData()->GetItem("variable"), 0.4338, 1e-3); // Note lower as slightly different answer with intel compiler
    }

    void TestEllipticGrowingDomainPdeModifierIn1d()
    {
        // Create mesh
        std::vector<Node<1>*> nodes;
        nodes.push_back(new Node<1>(0, true,  0.0));
        nodes.push_back(new Node<1>(1, false, 1.0));
        nodes.push_back(new Node<1>(2, false, 2.0));
        nodes.push_back(new Node<1>(3, false, 3.0));
        nodes.push_back(new Node<1>(4, true,  4.0));

        NodesOnlyMesh<1> mesh;
        mesh.ConstructNodesWithoutMesh(nodes, 1.5);

        // Create cells
        std::vector<CellPtr> cells;
        MAKE_PTR(DifferentiatedCellProliferativeType, p_differentiated_type);
        CellsGenerator<UniformCellCycleModel, 1> cells_generator;
        cells_generator.GenerateBasicRandom(cells, mesh.GetNumNodes(), p_differentiated_type);

        // Set up cell population
        NodeBasedCellPopulation<1> cell_population(mesh, cells);

        // Set up simulation time for file output
        SimulationTime::Instance()->SetEndTimeAndNumberOfTimeSteps(1.0, 1);

        // Create PDE and boundary condition objects
        MAKE_PTR_ARGS(CellwiseSourceEllipticPde<1>, p_pde, (cell_population, -0.1));
        MAKE_PTR_ARGS(ConstBoundaryCondition<1>, p_bc, (1.0));;

        // Create a PDE modifier and set the name of the dependent variable in the PDE
        MAKE_PTR_ARGS(EllipticGrowingDomainPdeModifier<1>, p_pde_modifier, (p_pde, p_bc, false));
        p_pde_modifier->SetDependentVariableName("variable");

        p_pde_modifier->SetOutputGradient(true);

        p_pde_modifier->SetupSolve(cell_population,"TestEllipticBoxDomainPdeModifierIn1d");

        // Test the solution at some fixed points to compare with other cell populations
        CellPtr p_cell_2 = cell_population.GetCellUsingLocationIndex(2);
        TS_ASSERT_DELTA(p_cell_2->GetCellData()->GetItem("variable"), 0.8274, 1e-2);
        TS_ASSERT_DELTA(p_cell_2->GetCellData()->GetItem("variable_grad_x"), 0.0, 1e-2);

        // Clear memory
        for (unsigned i=0; i<nodes.size(); i++)
        {
            delete nodes[i];
        }
    }

    void TestEllipticGrowingDomainPdeModifierIn3d()
    {
        // Create a simple mesh
        TetrahedralMesh<3,3> temp_mesh;
        temp_mesh.ConstructCuboid(3,3,3);
        NodesOnlyMesh<3> mesh;
        mesh.ConstructNodesWithoutMesh(temp_mesh, 1.5);

        // Create cells
        std::vector<CellPtr> cells;
        MAKE_PTR(DifferentiatedCellProliferativeType, p_differentiated_type);
        CellsGenerator<UniformCellCycleModel, 3> cells_generator;
        cells_generator.GenerateBasicRandom(cells, mesh.GetNumNodes(), p_differentiated_type);

        // Set up cell population
        NodeBasedCellPopulation<3> cell_population(mesh, cells);

        // Set up simulation time for file output
        SimulationTime::Instance()->SetEndTimeAndNumberOfTimeSteps(1.0, 1);

        // Create PDE and boundary condition objects
        MAKE_PTR_ARGS(CellwiseSourceEllipticPde<3>, p_pde, (cell_population, -0.1));
        MAKE_PTR_ARGS(ConstBoundaryCondition<3>, p_bc, (1.0));

        // Create a PDE modifier and set the name of the dependent variable in the PDE
        MAKE_PTR_ARGS(EllipticGrowingDomainPdeModifier<3>, p_pde_modifier, (p_pde, p_bc, false));
        p_pde_modifier->SetDependentVariableName("variable");

        p_pde_modifier->SetOutputGradient(true);

        p_pde_modifier->SetupSolve(cell_population,"TestEllipticGrowingDomainPdeModifierIn3d");

        // Test the solution at some fixed points to compare with other cell populations
        CellPtr p_cell_62 = cell_population.GetCellUsingLocationIndex(13);
        TS_ASSERT_DELTA(p_cell_62->GetCellData()->GetItem("variable"), 1.0, 1e-2);
        TS_ASSERT_DELTA(p_cell_62->GetCellData()->GetItem("variable_grad_x"), 0.0, 1e-2);
        TS_ASSERT_DELTA(p_cell_62->GetCellData()->GetItem("variable_grad_y"), 0.0, 1e-2);
        TS_ASSERT_DELTA(p_cell_62->GetCellData()->GetItem("variable_grad_z"), 0.0, 1e-2);
    }
};

#endif /*TESTELLIPTICGROWINGDOMAINPDEMODIFIER_HPP_*/


// Copyright (c) 2017 Sony Corporation. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef __NBLA_UTILS_EIGEN_HPP__
#define __NBLA_UTILS_EIGEN_HPP__

#define EIGEN_NO_DEBUG
#define EIGEN_MPL2_ONLY

#include <Eigen/Dense>

namespace nbla {

namespace eigen {
template <typename T>
using Matrix =
    Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;

template <typename T> using RowVector = Eigen::Matrix<T, 1, Eigen::Dynamic>;

template <typename T> using ColVector = Eigen::Matrix<T, Eigen::Dynamic, 1>;

template <typename T> using MatrixMap = Eigen::Map<Matrix<T>>;

template <typename T> using ConstMatrixMap = Eigen::Map<const Matrix<T>>;

template <typename T> using RowVectorMap = Eigen::Map<RowVector<T>>;

template <typename T> using ConstRowVectorMap = Eigen::Map<const RowVector<T>>;

template <typename T> using ColVectorMap = Eigen::Map<ColVector<T>>;

template <typename T> using ConstColVectorMap = Eigen::Map<const ColVector<T>>;
}
}
#endif


// SPDX-FileCopyrightText: 2018-2020 Smart Robotics Lab, Imperial College London
// SPDX-FileCopyrightText: 2018 Nils Funk, ETH Zürich
// SPDX-FileCopyrightText: 2019-2020 Sotiris Papatheodorou, Imperial College London
// SPDX-License-Identifier: BSD-3-Clause

#ifndef __OCTOMAP_IO_IMPL_HPP
#define __OCTOMAP_IO_IMPL_HPP

#if defined(SE_OCTOMAP) && SE_OCTOMAP

#include <cmath>
#include <fstream>
#include <iostream>

#include <Eigen/Dense>
#include <octomap/octomap.h>

template<typename T>
using VoxelBlockType = typename T::VoxelBlockType;

template<typename VoxelT, typename FunctionT>
octomap::OcTree* se::to_octomap(const se::Octree<VoxelT>& octree,
                                const FunctionT           set_node_value) {

  // Test if the octree is initialized.
  if (octree.size() == 0) {
    return nullptr;
  }

  // supereight map parameters.
  const float voxel_dim = octree.dim() / octree.size();
  // The sample point of supereight voxels relative to the voxel
  // corner closest to the origin. Typically the sample point is
  // the voxel centre.
  const Eigen::Vector3f se_sample_offset = se::Octree<VoxelT>::sample_offset_frac_;
  // The sample point of octomap voxels relative to the voxel
  // corner closest to the origin. The sample point is the voxel
  // centre.
  const Eigen::Vector3f om_sample_offset = Eigen::Vector3f::Constant(0.5f);
  // Add this to supereight voxel coordinates to transform them to
  // OctoMap voxel coordinates.
  const Eigen::Vector3f se_to_om_offset = - se_sample_offset + om_sample_offset;

  // Initialize the octomap.
  octomap::OcTree* octomap = new octomap::OcTree(voxel_dim);

  se::Node<VoxelT>* node_stack[se::Octree<VoxelT>::max_voxel_depth * 8 + 1];
  size_t stack_idx = 0;

  se::Node<VoxelT>* node = octree.root();

  if (node != nullptr) {
    se::Node<VoxelT>* current = node;
    node_stack[stack_idx++] = current;

    while (stack_idx != 0) {
      node = current;

      if (node->isBlock()) {
        const VoxelBlockType<VoxelT>* block
            = static_cast<VoxelBlockType <VoxelT>*>(node);
        const Eigen::Vector3i block_coord = block->coordinates();
        const int block_size = static_cast<int>(VoxelBlockType<VoxelT>::size_li);
        const int x_last = block_coord.x() + block_size;
        const int y_last = block_coord.y() + block_size;
        const int z_last = block_coord.z() + block_size;
        for (int z = block_coord.z(); z < z_last; ++z) {
          for (int y = block_coord.y(); y < y_last; ++y) {
            for (int x = block_coord.x(); x < x_last; ++x) {
              const Eigen::Vector3f voxel_coord_eigen
                  = voxel_dim * (Eigen::Vector3f(x, y, z) + se_to_om_offset);
              const octomap::point3d voxel_coord (
                  voxel_coord_eigen.x(),
                  voxel_coord_eigen.y(),
                  voxel_coord_eigen.z());
              const typename VoxelT::VoxelData voxel_data
                  = block->data(Eigen::Vector3i(x, y, z));
              set_node_value(*octomap, voxel_coord, voxel_data);
            }
          }
        }
      }

      if (node->children_mask() == 0) {
        current = node_stack[--stack_idx];
        continue;
      }

      for (int child_idx = 0; child_idx < 8; ++child_idx) {
        se::Node<VoxelT>* child = node->child(child_idx);
        if (child != nullptr) {
          node_stack[stack_idx++] = child;
        }

      }
      current = node_stack[--stack_idx];
    }
  }

  // Make the octree consistent at all levels.
  octomap->updateInnerOccupancy();
  // Combine children with the same values.
  octomap->prune();
  // Return the pointer.
  return octomap;
}

template<typename VoxelT>
octomap::OcTree* se::to_octomap(const se::Octree<VoxelT>& octree) {

  // Create a lambda function to set the state of a single voxel.
  const auto set_node_value = [](octomap::OcTree&                  octomap,
                                 const octomap::point3d&           voxel_coord,
                                 const typename VoxelT::VoxelData& voxel_data) {
    // Just store the log-odds occupancy probability. Do not store log-odds of
    // 0 because OctoMap interprets it as occupied, whereas in supereight it
    // means unknown.
    if (voxel_data.x != 0.f) {
      octomap.setNodeValue(voxel_coord, voxel_data.x, true);
    }
  };

  // Convert to OctoMap and return a pointer.
  return se::to_octomap(octree, set_node_value);
}

template<typename VoxelT>
octomap::OcTree* se::to_binary_octomap(const se::Octree<VoxelT>& octree) {

  // Create a lambda function to set the state of a single voxel.
  const auto set_node_value = [](octomap::OcTree&                  octomap,
                                 const octomap::point3d&           voxel_coord,
                                 const typename VoxelT::VoxelData& voxel_data) {
    if (voxel_data.x > 0) {
      // Occupied
      octomap.updateNode(voxel_coord, true, true);
    } else if (voxel_data.x < 0) {
      // Free
      octomap.updateNode(voxel_coord, false, true);
    }
    // Do not update unknown voxels.
  };

  // Convert to OctoMap and return a pointer.
  return se::to_octomap(octree, set_node_value);
}

#endif

#endif


/*
 * Copyright (c) 2020 Andrew Price
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. 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.
 * 3. Neither the name of the copyright holder 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 "mps_voxels/JaccardMatch.h"
#include "mps_voxels/AABB.h"
#include "mps_voxels/util/hungarian.hpp"
#include <boost/bimap.hpp>
#include <utility>

namespace mps
{

JaccardMatch::JaccardMatch(const cv::Mat& labels1, const cv::Mat& labels2)
{

	if (labels1.channels() > 1 || labels1.type() != CV_16U || labels2.channels() > 1 || labels2.type() != CV_16U)
	{
		throw std::logic_error("jaccard() !!! Only works with CV_16U 1-channel Mats");
	}

	boxes1 = getBBoxes(labels1);
	boxes2 = getBBoxes(labels2);

	int count = 0;
	for (const auto& i : boxes1) { lblIndex1.insert({i.first, count++}); }
	count = 0;
	for (const auto& j : boxes2) { lblIndex2.insert({j.first, count++}); }

	intersection = Eigen::MatrixXi::Zero(boxes1.size(), boxes2.size());
	size_t dSize = std::max(boxes1.size(), boxes2.size());
	IOU = Eigen::MatrixXd::Zero(dSize, dSize);

	for (const auto& i : boxes1)
	{
		cv::Mat mask1 = (labels1 == i.first);
		int count1 = cv::countNonZero(mask1);
		iSizes.insert({i.first, count1});
		for (const auto& j : boxes2)
		{
			int intersectionCount = 0;
			double iou = 0;
			// Prune actual comparisons by starting with bounding boxes
			if (intersect(i.second, j.second))
			{
				cv::Mat mask2 = (labels2 == j.first);

				int count2;
				const auto iter = jSizes.find(j.first);
				if (iter == jSizes.end())
				{
					count2 = cv::countNonZero(mask2);
					jSizes.insert({j.first, count2});
				}
				else
				{
					count2 = iter->second;
				}

				cv::Mat intersectionMask = (mask1 & mask2);

				intersectionCount = cv::countNonZero(intersectionMask);
				int unionCount = count1 + count2 - intersectionCount;
//			    cv::Mat unionMask = (mask1 | mask2); // Optimized to be (|A|+|B|-|AB|)
				iou = (intersectionCount) / static_cast<double>(unionCount);
			}
			IOU(lblIndex1.left.at(i.first), lblIndex2.left.at(j.first)) = iou;
			intersection(lblIndex1.left.at(i.first), lblIndex2.left.at(j.first)) = intersectionCount;
		}
	}

	// If an element in J doesn't intersect anything, it won't have a size computed and cached
	for (const auto& j : boxes2)
	{
		const auto iter = jSizes.find(j.first);
		if (iter == jSizes.end())
		{
			cv::Mat mask2 = (labels2 == j.first);
			int count2 = cv::countNonZero(mask2);
			jSizes.insert({j.first, count2});
		}
	}

	// Compute the optimal assignment of patches
	Hungarian H(-IOU);
	const auto& A = H.getAssignment();
	boost::bimap<LabelT, LabelT> matches;
	for (size_t i = 0; i < A.size(); ++i)
	{
		if (i >= boxes1.size()) { continue; }
		if (A[i] >= (int)boxes2.size()) { continue; }
		matches.insert({lblIndex1.right.at(i), lblIndex2.right.at(A[i])});
	}

	match = {-H.getSolutionCost(), matches};
}

double JaccardMatch::symmetricCover() const
{
	double score = 0.0;
	Eigen::VectorXd iMax = IOU.rowwise().maxCoeff();
	for (const auto& pair : lblIndex1.left)
	{
		// pair is (label, index)
		score += iSizes.at(pair.first) * iMax[pair.second];
	}
	Eigen::RowVectorXd jMax = IOU.colwise().maxCoeff();
	for (const auto& pair : lblIndex2.left)
	{
		// pair is (label, index)
		score += jSizes.at(pair.first) * jMax[pair.second];
	}

	double fullSize = 0;
	for (const auto& i : iSizes) { fullSize += i.second; }
	return score / (2.0 * fullSize);
}

double JaccardMatch::cover() const
{
	double score = 0.0;
	Eigen::VectorXd iMax = IOU.rowwise().maxCoeff();
	for (const auto& pair : lblIndex1.left)
	{
		// pair is (label, index)
		score += iSizes.at(pair.first) * iMax[pair.second];
	}

	double fullSize = 0;
	for (const auto& i : iSizes) { fullSize += i.second; }
	return score / fullSize;
}

JaccardMatch3D::LabelBounds
getBBoxes(const OccupancyData& labels)
{
	JaccardMatch3D::LabelBounds boxes;
	if (!labels.objects.empty())
	{
		for (const auto& kv : labels.objects)
		{
			boxes.emplace(kv.first.id, JaccardMatch3D::AABB(kv.second->minExtent, kv.second->maxExtent));
		}
	}
	else
	{
		for (size_t i = 0; i < labels.voxelRegion->num_vertices(); ++i)
		{
			const auto val = labels.vertexState[i];
			if (val != mps::VoxelRegion::FREE_SPACE)
			{
				const auto coord = labels.voxelRegion->coordinate_of(labels.voxelRegion->vertex_at(i));
				boxes[val].extend(coord);
			}
		}
	}
	return boxes;
}

std::set<size_t>
sparse_matches(const VoxelRegion::VertexLabels& labels,
               const VoxelRegion::VertexLabels::value_type & id)
{
	std::set<size_t> res;
	for (size_t i = 0; i < labels.size(); ++i)
	{
		if (labels[i] == id) { res.insert(i); }
	}
	return res;
}

JaccardMatch3D::JaccardMatch3D(const OccupancyData& labels1, const OccupancyData& labels2)
{
//	if (labels1.channels() > 1 || labels1.type() != CV_16U || labels2.channels() > 1 || labels2.type() != CV_16U)
//	{
//		throw std::logic_error("jaccard() !!! Only works with CV_16U 1-channel Mats");
//	}
	assert(labels1.voxelRegion->num_vertices() == labels1.vertexState.size());
	assert(labels2.voxelRegion->num_vertices() == labels2.vertexState.size());
	assert(labels1.voxelRegion->num_vertices() == labels2.voxelRegion->num_vertices());

	boxes1 = getBBoxes(labels1);
	boxes2 = getBBoxes(labels2);

	int count = 0;
	for (const auto& i : boxes1) { lblIndex1.insert({i.first, count++}); }
	count = 0;
	for (const auto& j : boxes2) { lblIndex2.insert({j.first, count++}); }

	intersection = Eigen::MatrixXi::Zero(boxes1.size(), boxes2.size());
	size_t dSize = std::max(boxes1.size(), boxes2.size());
	IOU = Eigen::MatrixXd::Zero(dSize, dSize);

	for (const auto& i : boxes1)
	{
		const auto mask1 = sparse_matches(labels1.vertexState, i.first);
		int count1 = mask1.size();
		iSizes.insert({i.first, count1});
		for (const auto& j : boxes2)
		{
			int intersectionCount = 0;
			double iou = 0;
			// Prune actual comparisons by starting with bounding boxes
			if (i.second.intersects(j.second))
			{
				const auto mask2 = sparse_matches(labels2.vertexState, j.first);

				int count2;
				const auto iter = jSizes.find(j.first);
				if (iter == jSizes.end())
				{
					count2 = mask2.size();
					jSizes.insert({j.first, count2});
				}
				else
				{
					count2 = iter->second;
				}

				std::vector<size_t> intersectionMask;
				std::vector<int> crossover;
				std::set_intersection(mask1.begin(), mask1.end(), mask2.begin(), mask2.end(), std::back_inserter(intersectionMask));

				intersectionCount = intersectionMask.size();
				int unionCount = count1 + count2 - intersectionCount;
//			    cv::Mat unionMask = (mask1 | mask2); // Optimized to be (|A|+|B|-|AB|)
				iou = (intersectionCount) / static_cast<double>(unionCount);
			}
			IOU(lblIndex1.left.at(i.first), lblIndex2.left.at(j.first)) = iou;
			intersection(lblIndex1.left.at(i.first), lblIndex2.left.at(j.first)) = intersectionCount;
		}
	}

	// If an element in J doesn't intersect anything, it won't have a size computed and cached
	for (const auto& j : boxes2)
	{
		const auto iter = jSizes.find(j.first);
		if (iter == jSizes.end())
		{
			const auto mask2 = sparse_matches(labels2.vertexState, j.first);
			int count2 = mask2.size();
			jSizes.insert({j.first, count2});
		}
	}

	// Compute the optimal assignment of patches
	Hungarian H(-IOU);
	const auto& A = H.getAssignment();
	boost::bimap<LabelT, LabelT> matches;
	for (size_t i = 0; i < A.size(); ++i)
	{
		if (i >= boxes1.size()) { continue; }
		if (A[i] >= (int)boxes2.size()) { continue; }
		matches.insert({lblIndex1.right.at(i), lblIndex2.right.at(A[i])});
	}

	match = {-H.getSolutionCost(), matches};
}

double JaccardMatch3D::symmetricCover() const
{
	double score = 0.0;
	Eigen::VectorXd iMax = IOU.rowwise().maxCoeff();
	for (const auto& pair : lblIndex1.left)
	{
		// pair is (label, index)
		score += iSizes.at(pair.first) * iMax[pair.second];
	}
	Eigen::RowVectorXd jMax = IOU.colwise().maxCoeff();
	for (const auto& pair : lblIndex2.left)
	{
		// pair is (label, index)
		score += jSizes.at(pair.first) * jMax[pair.second];
	}

	double fullSize = 0;
	for (const auto& i : iSizes) { fullSize += i.second; }
	return score / (2.0 * fullSize);
}

}


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

    @copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_FUNCTION_IS_LTZ_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_IS_LTZ_HPP_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{

  /*!
    @ingroup group-predicates

    This function object returns @ref True or @ref False according x is less than @ref Zero or not.

    @par Header <boost/simd/function/is_ltz.hpp>

    @par Note

     Using `is_ltz(x)` is equivalent to: `x < 0`

    @par Example:

      @snippet is_ltz.cpp is_ltz

    @par Possible output:

      @snippet is_ltz.txt is_ltz

  **/
  as_logical_t<Value> is_ltz(Value const& x);
} }
#endif

#include <boost/simd/function/scalar/is_ltz.hpp>
#include <boost/simd/function/simd/is_ltz.hpp>

#endif


#pragma once
/** \file */

#include <Eigen/Geometry>
#include <limits>

namespace rpxdock {
/**
\namespace rpxdock::util
\brief namespace for utils like timers, basic mathy stuff, etc
*/
namespace util {

// RowMajor to match numpy
template <class F>
using V1 = Eigen::Matrix<F, 1, 1>;
template <class F>
using V2 = Eigen::Matrix<F, 2, 1>;
template <class F>
using V3 = Eigen::Matrix<F, 3, 1>;  // todo: should use aligned vector3?
template <class F>
using M3 = Eigen::Matrix<F, 3, 3, Eigen::RowMajor>;
template <class F>
using X3 = Eigen::Transform<F, 3, Eigen::Affine, Eigen::RowMajor>;
template <class F>
using X3C = Eigen::Transform<F, 3, Eigen::AffineCompact, Eigen::RowMajor>;
template <class F>
using V4 = Eigen::Matrix<F, 4, 1>;
template <class F>
using M4 = Eigen::Matrix<F, 4, 4, Eigen::RowMajor>;
template <class F>
using V5 = Eigen::Matrix<F, 5, 1>;
template <class F>
using V6 = Eigen::Matrix<F, 6, 1>;

using V3f = V3<float>;
using V3d = V3<double>;
using X3f = X3<float>;
using X3d = X3<double>;

using M4f = M4<float>;
using M4d = M4<double>;

template <typename F>
using Mx = Eigen::Matrix<F, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
template <typename F>
using RefMx = Eigen::Ref<Mx<F>>;

template <typename F>
using Vx = Eigen::Matrix<F, Eigen::Dynamic, 1>;
template <typename F>
using RefVx = Eigen::Ref<Vx<F>>;

using Mxd = Mx<double>;
using RefMxd = RefMx<double>;
using Vxd = Vx<double>;
using RefVxd = RefVx<double>;
using Vxi = Vx<int>;
using RefVxi = RefVx<int>;

template <typename F>
using MapVxX3 = Eigen::Map<Vx<X3<F>>>;

template <typename T>
using NL = std::numeric_limits<T>;

template <class F>
F epsilon2() {
  return std::sqrt(std::numeric_limits<F>::epsilon());
}

template <typename F>
void set_rot(M3<F>& lhs, M3<F> rhs) {
  lhs = rhs;
}
template <typename F>
void set_rot(X3<F>& lhs, M3<F> rhs) {
  lhs.linear() = rhs;
}
template <typename F>
size_t dim_of() {
  return 0;
}
template <>
size_t dim_of<X3<float>>() {
  return 4;
}
template <>
size_t dim_of<X3<double>>() {
  return 4;
}
template <>
size_t dim_of<M3<float>>() {
  return 3;
}
template <>
size_t dim_of<M3<double>>() {
  return 3;
}

}  // namespace util
}  // namespace rpxdock

namespace Eigen {

template <typename V>
V* begin(rpxdock::util::Vx<V> v) {
  return v.data();
}
template <typename V>
V* end(rpxdock::util::Vx<V> v) {
  return v.data() + v.size();
}

}  // namespace Eigen


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

#include <iostream>

#include <boost/numeric/mtl/recursion/bit_masking.hpp>
#include <boost/numeric/mtl/recursion/predefined_masks.hpp>
#include <boost/numeric/mtl/matrix/dense2D.hpp>
#include <boost/numeric/mtl/matrix/morton_dense.hpp>
#include <boost/numeric/mtl/matrix/compressed2D.hpp>
#include <boost/numeric/mtl/matrix/map_view.hpp>
#include <boost/numeric/mtl/matrix/parameter.hpp>
#include <boost/numeric/mtl/vector/dense_vector.hpp>
#include <boost/numeric/mtl/vector/parameter.hpp>
#include <boost/numeric/mtl/utility/category.hpp>
#include <boost/numeric/mtl/utility/tag.hpp>

using namespace std;

template <typename T>
void dispatch(const T&, mtl::tag::scalar, const char* name)
{
    cout << "dispatch " << name << " to scalar\n";
}

template <typename T>
void dispatch(const T&, mtl::tag::vector, const char* name)
{
    cout << "dispatch " << name << " to vector\n";
}

template <typename T>
void dispatch(const T&, mtl::tag::matrix, const char* name)
{
    cout << "dispatch " << name << " to matrix\n";
}

template <typename T>
void test(const T& x, const char* name)
{
    dispatch(x, typename mtl::traits::algebraic_category<T>::type(), name);
}

struct none {};

int main(int, char**)
{
    using namespace mtl;
    const int size= 5;
    double d;
    int    i;
    none   n;

    dense_vector<float>                                  u(size);
    dense_vector<float, mtl::vec::parameters<row_major> >  ur(size);

    dense2D<double>                                      dr(size, size);
    dense2D<double, mat::parameters<col_major> >      dc(size, size);
    morton_dense<double, recursion::morton_z_mask>       mzd(size, size);
    morton_dense<double, recursion::doppled_2_row_mask>  d2r(size, size);
    compressed2D<double>                                 cr(size, size);
    compressed2D<double, mat::parameters<col_major> > cc(size, size);

    dense2D<complex<double> >                            drc(size, size);
    compressed2D<complex<double> >                       crc(size, size);

    mat::scaled_view<double, dense2D<double> >        scaled_matrix(2.0, dr);

    test(d, "double");
    test(i, "int");
    test(n, "unknown type");

    test(u, "dense (column) vector");
    test(ur, "dense row vector");

    test(dr, "Dense row major");
    test(dc, "Dense column major");
    test(mzd, "Morton Z-order");
    test(d2r, "Hybrid 2 row-major");
    test(cr, "Compressed row major");
    test(cc, "Compressed column major");
    test(drc, "Dense row major complex");
    test(crc, "Compressed row major complex");
    test(scaled_matrix, "Scaled_view of dense row major");

    return 0;
}


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

#include "pme/engine/transition_relation.h"
#include "pme/engine/consecution_checker.h"
#include "pme/util/find_safe_mis.h"

#define BOOST_TEST_MODULE FindSafeMISTest
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>

using namespace PME;

struct MISFixture
{
    aiger * aig;
    ExternalID l0, l1, l2, l3, o0, a0;
    VariableManager vars;
    std::unique_ptr<TransitionRelation> tr;
    std::unique_ptr<ConsecutionChecker> checker;

    MISFixture()
    {
        aig = aiger_init();

        l0 = 2;
        l1 = 4;
        l2 = 6;
        l3 = 8;
        a0 = 10;

        // l0' = l3
        aiger_add_latch(aig, l0, l3, "l0");

        // l1' = l0
        aiger_add_latch(aig, l1, l0, "l1");

        // l2' = l1
        aiger_add_latch(aig, l2, l1, "l2");

        // l3' = l2
        aiger_add_latch(aig, l3, l2, "l3");

        // a0 = l2 & l3
        aiger_add_and(aig, a0, l2, l3);

        // o0 = a0 = l2 & l3
        aiger_add_output(aig, a0, "o0");
        o0 = a0;

        tr.reset(new TransitionRelation(vars, aig));
        checker.reset(new ConsecutionChecker(vars, *tr));

        // Add the property clause as ID 0
        checker->addClause(0, tr->propertyClause());
    }

    ~MISFixture()
    {
        aiger_reset(aig);
        aig = nullptr;
    }
};

BOOST_AUTO_TEST_CASE(trivial_mis)
{
    MISFixture f;
    ID l0 = f.tr->toInternal(f.l0);
    ID l1 = f.tr->toInternal(f.l1);
    ID l2 = f.tr->toInternal(f.l2);
    ID l3 = f.tr->toInternal(f.l3);

    ID nl0 = negate(l0);
    ID nl1 = negate(l1);
    ID nl2 = negate(l2);
    ID nl3 = negate(l3);

    // Safe inductive formula should be its own MIS
    Clause c0 = {nl0, nl1};
    Clause c1 = {nl1, nl2};
    Clause c2 = {nl2, nl3};
    Clause c3 = {nl3, nl0};

    f.checker->addClause(1, c0);
    f.checker->addClause(2, c1);
    f.checker->addClause(3, c2);
    f.checker->addClause(4, c3);

    ClauseIDVec proof = { 0, 1, 2, 3, 4 };
    ClauseIDVec proof_copy = proof;

    BOOST_CHECK(findSafeMIS(*f.checker, proof_copy, 0));
    BOOST_CHECK(proof_copy == proof);
}

BOOST_AUTO_TEST_CASE(no_mis)
{
    MISFixture f;
    ID l0 = f.tr->toInternal(f.l0);
    ID l1 = f.tr->toInternal(f.l1);
    ID l2 = f.tr->toInternal(f.l2);
    ID l3 = f.tr->toInternal(f.l3);

    Clause c0 = { l0, l1, l2 };
    Clause c1 = { l1, l2, l3 };

    f.checker->addClause(1, c0);
    f.checker->addClause(2, c1);

    ClauseIDVec proof = { 0, 1, 2 };
    ClauseIDVec proof_copy = proof;

    BOOST_CHECK(!findSafeMIS(*f.checker, proof_copy, 0));
    BOOST_CHECK(proof_copy == proof);
}

BOOST_AUTO_TEST_CASE(nontrivial_mis)
{
    MISFixture f;
    ID l0 = f.tr->toInternal(f.l0);
    ID l1 = f.tr->toInternal(f.l1);
    ID l2 = f.tr->toInternal(f.l2);
    ID l3 = f.tr->toInternal(f.l3);

    ID nl0 = negate(l0);
    ID nl1 = negate(l1);
    ID nl2 = negate(l2);
    ID nl3 = negate(l3);

    // Safe inductive formula
    Clause c0 = {nl0, nl1};
    Clause c1 = {nl0, nl2};
    Clause c2 = {nl0, nl3};
    Clause c3 = {nl1, nl2};
    Clause c4 = {nl1, nl3};
    Clause c5 = {nl2, nl3};

    // Extra clauses
    Clause c6 = {l0, l1};
    Clause c7 = {l1, l2};

    f.checker->addClause(1, c0);
    f.checker->addClause(2, c1);
    f.checker->addClause(3, c2);
    f.checker->addClause(4, c3);
    f.checker->addClause(5, c4);
    f.checker->addClause(6, c5);
    f.checker->addClause(7, c6);
    f.checker->addClause(8, c7);

    ClauseIDVec proof = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
    ClauseIDVec proof_copy = proof;

    BOOST_CHECK(findSafeMIS(*f.checker, proof_copy, 0));
    std::sort(proof_copy.begin(), proof_copy.end());
    ClauseIDVec expected = { 0, 1, 2, 3, 4, 5, 6 };
    BOOST_CHECK(proof_copy == expected);

    // Check that the result is indeed safe inductive
    proof = proof_copy;
    BOOST_CHECK(findSafeMIS(*f.checker, proof_copy, 0));
    BOOST_CHECK(proof_copy == proof);

    // Deleting 2 or 5 from the below yields a non-inductive formula
    // that contains a MIS of the same clauses as the trivial_mis case
    // proof = { 0, 1, 2, 3, 4, 5, 6 }
    proof = { 0, 1, 3, 4, 5, 6 };
    proof_copy = proof;
    BOOST_CHECK(findSafeMIS(*f.checker, proof_copy, 0));
    std::sort(proof_copy.begin(), proof_copy.end());
    expected = { 0, 1, 3, 4, 6 };
    BOOST_CHECK(proof_copy == expected);
}

BOOST_AUTO_TEST_CASE(multiple_necessary_clauses)
{
    MISFixture f;
    ID l0 = f.tr->toInternal(f.l0);
    ID l1 = f.tr->toInternal(f.l1);
    ID l2 = f.tr->toInternal(f.l2);
    ID l3 = f.tr->toInternal(f.l3);

    ID nl0 = negate(l0);
    ID nl1 = negate(l1);
    ID nl2 = negate(l2);
    ID nl3 = negate(l3);

    // Safe inductive formula
    Clause c0 = {nl0, nl1};
    Clause c1 = {nl0, nl2};
    Clause c2 = {nl0, nl3};
    Clause c3 = {nl1, nl2};
    Clause c4 = {nl1, nl3};
    Clause c5 = {nl2, nl3};
    // Extra clauses
    Clause c6 = {l0, l1};
    Clause c7 = {l1, l2};

    f.checker->addClause(1, c0);
    f.checker->addClause(2, c1);
    f.checker->addClause(3, c2);
    f.checker->addClause(4, c3);
    f.checker->addClause(5, c4);
    f.checker->addClause(6, c5);
    f.checker->addClause(7, c6);
    f.checker->addClause(8, c7);

    ClauseIDVec proof = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
    ClauseIDVec proof_copy = proof;
    ClauseIDVec expected;

    BOOST_CHECK(findSafeMIS(*f.checker, proof_copy, {0, 2}));
    std::sort(proof_copy.begin(), proof_copy.end());
    expected = { 0, 1, 2, 3, 4, 5, 6 };
    BOOST_CHECK(proof_copy == expected);

    proof_copy = proof;
    BOOST_CHECK(findSafeMIS(*f.checker, proof_copy, {0, 5}));
    std::sort(proof_copy.begin(), proof_copy.end());
    expected = { 0, 1, 2, 3, 4, 5, 6 };
    BOOST_CHECK(proof_copy == expected);
}

BOOST_AUTO_TEST_CASE(no_checker_interface)
{
    MISFixture f;
    ID l0 = f.tr->toInternal(f.l0);
    ID l1 = f.tr->toInternal(f.l1);
    ID l2 = f.tr->toInternal(f.l2);
    ID l3 = f.tr->toInternal(f.l3);

    ID nl0 = negate(l0);
    ID nl1 = negate(l1);
    ID nl2 = negate(l2);
    ID nl3 = negate(l3);

    SafetyProof safe_non_inductive = {
        // Safe inductive formula
        { negate(f.tr->bad()) },
        {nl0, nl1},
        {nl0, nl2},
        {nl0, nl3},
        {nl1, nl2},
        {nl1, nl3},
        {nl2, nl3},
        // Extra clauses
        {l0, l1},
        {l1, l2}
    };

    BOOST_CHECK(findSafeMIS(f.vars, *f.tr, safe_non_inductive));

    SafetyProof safe_inductive = {
        { negate(f.tr->bad()) },
        {nl0, nl1},
        {nl0, nl2},
        {nl0, nl3},
        {nl1, nl2},
        {nl1, nl3},
        {nl2, nl3}
    };

    BOOST_CHECK(findSafeMIS(f.vars, *f.tr, safe_inductive));

    SafetyProof minimal_safe_inductive = {
        { negate(f.tr->bad()) },
        {nl0, nl1},
        {nl1, nl2},
        {nl2, nl3},
        {nl3, nl0}
    };

    BOOST_CHECK(findSafeMIS(f.vars, *f.tr, minimal_safe_inductive));

    SafetyProof non_proof = {
        { negate(f.tr->bad()) },
        {nl0, nl1},
        {nl2, nl3},
        {nl3, nl0}
    };

    BOOST_CHECK(!findSafeMIS(f.vars, *f.tr, non_proof));
}


/* ----------------------------------------------------------------------------

 * GTSAM Copyright 2010, Georgia Tech Research Corporation,
 * Atlanta, Georgia 30332-0415
 * All Rights Reserved
 * Authors: Frank Dellaert, et al. (see THANKS for the full author list)

 * See LICENSE for the license information

 * -------------------------------------------------------------------------- */

/**
 * @brief wraps PinholeCamera classes to python
 * @author Ellon Paiva Mendes (LAAS-CNRS)
 **/

#include <boost/python.hpp>

#define NO_IMPORT_ARRAY
#include <numpy_eigen/NumpyEigenConverter.hpp>

#include "gtsam/geometry/PinholeCamera.h"
#include "gtsam/geometry/Cal3_S2.h"

using namespace boost::python;
using namespace gtsam;

typedef PinholeBaseK<Cal3_S2> PinholeBaseKCal3_S2;
typedef PinholeCamera<Cal3_S2> PinholeCameraCal3_S2;

BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(print_overloads, PinholeCameraCal3_S2::print, 0, 1)
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(equals_overloads, PinholeCameraCal3_S2::equals, 1, 2)
BOOST_PYTHON_FUNCTION_OVERLOADS(Lookat_overloads, PinholeCameraCal3_S2::Lookat, 3, 4)

void exportPinholeCamera(){

class_<PinholeCameraCal3_S2, bases<PinholeBaseKCal3_S2> >("PinholeCameraCal3_S2", init<>())
  .def(init<const Pose3 &>())
  .def(init<const Pose3 &, const Cal3_S2 &>())
  .def(init<const Vector &>())
  .def(init<const Vector &, const Vector &>())
  .def("print", &PinholeCameraCal3_S2::print, print_overloads(args("s")))
  .def("equals", &PinholeCameraCal3_S2::equals, equals_overloads(args("q","tol")))
  .def("pose", &PinholeCameraCal3_S2::pose, return_value_policy<copy_const_reference>())
  // We don't need to define calibration() here because it's already defined as virtual in the base class PinholeBaseKCal3_S2
  // .def("calibration", &PinholeCameraCal3_S2::calibration, return_value_policy<copy_const_reference>())
  .def("Lookat", &PinholeCameraCal3_S2::Lookat, Lookat_overloads())
  .staticmethod("Lookat")
;

}


//=======================================================================
// Copyright 2009 Trustees of Indiana University.
// Authors: Michael Hansen, Andrew Lumsdaine
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include <boost/graph/graph_archetypes.hpp>
#include <boost/graph/graph_concepts.hpp>
#include <boost/graph/grid_graph.hpp>
#include <boost/concept/assert.hpp>

#define DIMENSIONS 3
using namespace boost;

int main (int, char*[]) {

  typedef grid_graph<DIMENSIONS> Graph;
  typedef graph_traits<Graph>::vertex_descriptor Vertex;
  typedef graph_traits<Graph>::edge_descriptor Edge;

  BOOST_CONCEPT_ASSERT((BidirectionalGraphConcept<Graph> ));
  BOOST_CONCEPT_ASSERT((VertexListGraphConcept<Graph> ));
  BOOST_CONCEPT_ASSERT((EdgeListGraphConcept<Graph> ));
  BOOST_CONCEPT_ASSERT((IncidenceGraphConcept<Graph> ));
  BOOST_CONCEPT_ASSERT((AdjacencyGraphConcept<Graph> ));
  BOOST_CONCEPT_ASSERT((AdjacencyMatrixConcept<Graph> ));
  BOOST_CONCEPT_ASSERT((ReadablePropertyGraphConcept<Graph, Vertex, vertex_index_t> ));
  BOOST_CONCEPT_ASSERT((ReadablePropertyGraphConcept<Graph, Edge, edge_index_t> ));

  return (0);
}


#define BOOST_TEST_MODULE NUMFMT
#include "numfmt.hpp"
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_CASE(numfmt) {
    BOOST_CHECK_EQUAL(moros::numfmt(1998), "1.95K");
    BOOST_CHECK_EQUAL(moros::numfmt(403236468), "384.56M");
    BOOST_CHECK_EQUAL(moros::numfmt(std::chrono::seconds(3661)), "1h 1m 1s");

}


#ifndef INCLUDED_PDS_PDS_HPP
#define INCLUDED_PDS_PDS_HPP

#include <Eigen/Dense>
#include <vector>

namespace pds
{

	std::vector<Eigen::Vector2f> Random(const Eigen::MatrixXf& density);

	std::vector<Eigen::Vector2f> RectGrid(const Eigen::MatrixXf& density);

	std::vector<Eigen::Vector2f> HexGrid(const Eigen::MatrixXf& density);

	std::vector<Eigen::Vector2f> SimplifiedPDS(const Eigen::MatrixXf& density);

	std::vector<Eigen::Vector2f> SimplifiedPDSOld(const Eigen::MatrixXf& density);

	std::vector<Eigen::Vector2f> FloydSteinberg(const Eigen::MatrixXf& density);

	std::vector<Eigen::Vector2f> FloydSteinbergExpo(const Eigen::MatrixXf& density);

	std::vector<Eigen::Vector2f> FloydSteinbergMultiLayer(const Eigen::MatrixXf& density);

	std::vector<Eigen::Vector2f> Fattal(const Eigen::MatrixXf& density);

	std::vector<Eigen::Vector2f> DeltaDensitySampling(
		const Eigen::MatrixXf& density,
		const std::vector<Eigen::Vector2f>& old_seeds,
		std::vector<int>* seed_origin = 0
	);

	std::vector<Eigen::Vector2f> DeltaDensitySamplingOld(const Eigen::MatrixXf& density, const std::vector<Eigen::Vector2f>& old_seeds);

	inline std::vector<Eigen::Vector2f> PoissonDiscSampling(const std::string& name, const Eigen::MatrixXf& density, const std::vector<Eigen::Vector2f>& old_points = std::vector<Eigen::Vector2f>())
	{
		if(name == "rnd") return Random(density);
		if(name == "rect") return RectGrid(density);
		if(name == "spds") return SimplifiedPDS(density);
		if(name == "fattal") return Fattal(density);
		if(name == "dds") return DeltaDensitySampling(density, old_points);
		throw 0;
	}

}

#endif


#include <list>
#include <vector>

#include <boost/algorithm/cxx11/is_sorted.hpp>
#include <boost/range/algorithm_ext/is_sorted.hpp>

#include <elle/log.hh>
#include <elle/random.hh>
#include <elle/test.hh>

ELLE_LOG_COMPONENT("elle.test.random");

namespace
{
  using Histo = std::vector<int>;

  /*-----------.
  | pick_one.  |
  `-----------*/

  /// Check that `histo` contains only 100 odd numbers.
  void
  check_histo_pick_one(Histo const& histo)
  {
    ELLE_LOG("histo: %s", histo);
    int sum = 0;
    for (auto i = 0u; i < histo.size(); ++i)
    {
      sum += histo[i];
      if (i % 2)
        BOOST_TEST(histo[i] != 0);
      else
        BOOST_TEST(histo[i] == 0);
    }
    BOOST_TEST(sum == 100);
  }

  /// Pick one covers the whole range.
  void
  pick_one()
  {
    auto range = {1, 3, 5, 7, 9};
    auto histo = Histo(10, 0);
    for (int i = 0; i < 100; ++i)
    {
      auto num = *elle::pick_one(range);
      BOOST_TEST(0 <= num);
      BOOST_TEST(num <= 9);
      ++histo[num];
    }
    check_histo_pick_one(histo);
  }

  /// Pick one with filter covers the whole range.
  void
  pick_one_filtered()
  {
    auto range = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    auto histo = Histo(10, 0);
    for (int i = 0; i < 100; ++i)
    {
      auto num = *elle::pick_one(range, [](auto i) { return i % 2; });
      BOOST_TEST(0 <= num);
      BOOST_TEST(num <= 9);
      ++histo[num];
    }
    check_histo_pick_one(histo);
  }

  /*---------.
  | pick_n.  |
  `---------*/

  void
  check_histo_pick_n(Histo const& histo)
  {
    ELLE_LOG("histo: %s", histo);
    int sum = 0;
    for (auto i = 0u; i < histo.size(); ++i)
    {
      // Errors with 350 were happending too frequently.
      BOOST_TEST(240 <= histo[i]);
      BOOST_TEST(histo[i] <= 360);
      sum += histo[i];
    }
    BOOST_TEST(sum == 3000);
  }

  /// Check the selection of integers.
  void
  pick_n_integers()
  {
    auto histo = Histo(10, 0);
    for (int i = 0; i < 1000; ++i)
    {
      auto nums = elle::pick_n(3, 10);
      ELLE_LOG("nums = %s", nums);
      BOOST_TEST(boost::algorithm::is_strictly_increasing(nums));
      for (auto n: nums)
        ++histo[n];
    }
    check_histo_pick_n(histo);
  }

  /// Check the selection of iterators in a container.
  template <typename Range>
  void
  pick_n_iterators_impl(Range const& r)
  {
    for (auto i: r)
    {
      BOOST_TEST_REQUIRE(0 <= i);
      BOOST_TEST_REQUIRE(i < 10);
    }
    auto histo = Histo(10, 0);
    for (int i = 0; i < 1000; ++i)
      for (auto i: elle::pick_n(3, r))
        ++histo[*i];
    check_histo_pick_n(histo);
  }

  void
  pick_n_iterators()
  {
    pick_n_iterators_impl(std::vector<int>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
    // Without random access.
    pick_n_iterators_impl(std::list<int>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
  }
}

ELLE_TEST_SUITE()
{
  auto& suite = boost::unit_test::framework::master_test_suite();
  suite.add(BOOST_TEST_CASE(pick_one));
  suite.add(BOOST_TEST_CASE(pick_one_filtered));
  suite.add(BOOST_TEST_CASE(pick_n_integers));
  suite.add(BOOST_TEST_CASE(pick_n_iterators));
}


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

  Copyright 2016 NumScale SAS

  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/simd/function/insert.hpp>
#include <boost/simd/logical.hpp>
#include <boost/simd/pack.hpp>
#include <simd_test.hpp>
#include <array>

namespace bs = boost::simd;
namespace bd = boost::dispatch;

template <typename T, std::size_t N, typename Env>
void test(Env& $)
{
  bs::pack<T, N>  p;
  std::array<bool,N> ref;

  for(std::size_t i = 0; i < N; ++i)
  {
    bs::insert(p, i, i%2 ? true : false);
    ref[i] = i%2 ? true : false;
  }

  for(std::size_t i = 0;i < N; ++i) STF_EQUAL( p[i], ref[i] );
}

STF_CASE_TPL("Check dynamic insert on pack" , STF_NUMERIC_TYPES)
{
  static const std::size_t N = boost::simd::pack<T>::static_size;

  test<bs::logical<T>, N>($);
  test<bs::logical<T>, N/2>($);
  test<bs::logical<T>, N*2>($);
}


// Copyright Matt Overby 2021.
// Distributed under the MIT License.

#ifndef MCL_SPARSEREPLICATE_HPP
#define MCL_SPARSEREPLICATE_HPP 1

#include <Eigen/Sparse>

namespace mcl
{

// Copies values along the diagonal of a sparse matrix.
// For making n x m matrices n*dim x m*dim
template<typename T>
static inline void sparse_replicate(
		const Eigen::SparseMatrix<T> &A,
		int dim,
		Eigen::SparseMatrix<T> &Adim)
{
	using namespace Eigen;
	typedef typename SparseMatrix<T>::InnerIterator InnerIter;
	dim = std::max(1,dim);

	Adim.resize(A.rows()*dim, A.cols()*dim);
	Adim.setZero();
	Adim.reserve(A.nonZeros()*dim);

	int cols = A.cols();
	for (int c=0; c<cols; ++c)
	{
		for (int d=0; d<dim; ++d)
		{
			int col = c*dim+d;
			Adim.startVec(col);
			for (InnerIter itA(A,c); itA; ++itA)
			{
				int row = itA.row()*dim+d;
				Adim.insertBack(row, col) = itA.value();
			}
		}
	}

	Adim.finalize();
	Adim.makeCompressed();

} // end sparse replicate

} // ns mcl

#endif


#include "mnist.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <random>
#include <algorithm>
#include <Eigen/Dense>

namespace MyDL
{

    using std::cout;
    using std::endl;
    using std::ifstream;
    using std::ios;
    using std::string;
    using std::vector;
    using namespace Eigen;

    // リトルエンディアン → ビッグエンディアンへの変換
    int LittleEndian2BigEndian(int i)
    {
        unsigned char c1, c2, c3, c4;

        c1 = i & 255;         // 0x 00 00 00 xx (xxを抽出, 255 = 0x 00 00 00 FF)
        c2 = (i >> 8) & 255;  // 0x 00 00 xx 00 (xxを抽出 ※下8桁は右シフトで消える)
        c3 = (i >> 16) & 255; // 0x 00 xx 00 00 (xxを抽出 ※下16桁は右シフトで消える)
        c4 = (i >> 24) & 255; // 0x xx 00 00 00 (xxを抽出 ※下24桁は右シフトで消える)

        return ((int)c1 << 24) + ((int)c2 << 16) + ((int)c3 << 8) + ((int)c4);
    }

    // ------------------------------------------------------
    //              Eigen用 MNISTローダ 実装
    // ------------------------------------------------------

    // コンストラクタ
    MnistEigenDataset::MnistEigenDataset(int batch_size, bool random_load)
    {
        _batch_size = batch_size;

        // ファイル読み込み初期化処理
        _init_train_loader();
        _init_test_loader();

        _train_max_batch_num = (_number_of_train_data + _batch_size - 1) / _batch_size; // 切り上げ
        _test_max_batch_num = (_number_of_test_data + _batch_size - 1) / _batch_size;

        // ランダム読み出しの設定をしているときはインデックスをシャッフル
        if (random_load)
        {
            std::mt19937_64 get_rand_mt;
            std::shuffle(_train_indices.begin(), _train_indices.end(), get_rand_mt);
            std::shuffle(_test_indices.begin(), _test_indices.end(), get_rand_mt);
        }
    }

    void MnistEigenDataset::next_train(MatrixXd &train_X, MatrixXd &train_y, bool one_hot_label, bool normalize)
    {
        // 読み出し用一時変数
        int pixels = _rows * _cols;
        vector<double> tmp_image(pixels); // 配列のサイズ初期化が必要(でないとセグフォ)
        vector<double> tmp_labels(_batch_size);

        if (one_hot_label)
        {
            train_y = MatrixXd::Zero(_batch_size, 10); // one_hot_label有効化時の初期化
        }

        // インデックス取得：初期位置計算
        int start_idx = _batch_size * _train_load_count;
        int tmp_idx;
        // ここに必要なだけ訓練データを読み出す処理を作成
        for (int i = 0; i < _batch_size; i++)
        {
            // 59999を超えたインデックスは0から再カウント
            tmp_idx = _train_indices[(start_idx + i) % _number_of_train_data];

            // ファイルシーク：画像データのインターバルは「28×28=784byte」あるので注意
            // 画像データ
            _train_image_ifs.seekg(_train_image_pos);                     // シークを初期位置に
            _train_image_ifs.seekg(tmp_idx * pixels, std::ios_base::cur); // 読み出し位置まで移動
            // ラベル
            _train_label_ifs.seekg(_train_label_pos);                     // シークを初期位置に
            _train_label_ifs.seekg(tmp_idx, std::ios_base::cur);          // 読み出し位置まで移動

            // 画像読み出し
            for (int j = 0; j < pixels; j++)
            {
                unsigned char tmp_value;
                _train_image_ifs.read((char *)&tmp_value, sizeof(tmp_value));
                tmp_image[j] = (double)(tmp_value);
            }

            train_X.row(i) = Map<Matrix<double, 1, 28 * 28>>(&(tmp_image[0]));

            // ラベル読み出し
            unsigned char tmp_label;
            _train_label_ifs.read((char *)&tmp_label, sizeof(tmp_label));
            tmp_labels[i] = (double)(tmp_label);

            // one-hotか否かで場合分け
            if (one_hot_label)
            {
                train_y(i, int(tmp_label)) = 1;
            }
            else
            {
                train_y.row(i) = Map<Matrix<double, 1, 1>>(&(tmp_labels[i]));
            }
        }

        if (normalize)
        {
            train_X /= 255;
        }

        _train_load_count++;
        // カウンタリセット → バッチ数とカウントが同じになったら0にする
        _train_load_count = _train_load_count % _train_max_batch_num;
    }

    void MnistEigenDataset::next_test(MatrixXd &test_X, MatrixXd &test_y, bool one_hot_label, bool normalize)
    {

        // 読み出し用一時変数
        int pixels = _rows * _cols;
        vector<double> tmp_image(pixels);
        vector<double> tmp_labels(_batch_size);

        if (one_hot_label)
        {
            test_y = MatrixXd::Zero(_batch_size, 10);
        }

        // インデックス取得：初期位置計算
        int start_idx = _batch_size * _test_load_count;
        int tmp_idx;
        // ここに必要なだけ訓練データを読み出す処理を作成
        for (int i = 0; i < _batch_size; i++)
        {
            // 59999を超えたインデックスは0から再カウント
            tmp_idx = _test_indices[(start_idx + i) % (_number_of_test_data)];
            // ファイルシーク：画像データのインターバルは「28×28=784byte」あるので注意
            // 画像データ
            _test_image_ifs.seekg(_test_image_pos);                      // シークを初期位置に
            _test_image_ifs.seekg(tmp_idx * pixels, std::ios_base::cur); // 読み出し位置まで移動
            // ラベル
            _test_label_ifs.seekg(_test_label_pos);                      // シークを初期位置に
            _test_label_ifs.seekg(tmp_idx, std::ios_base::cur);          // 読み出し位置まで移動

            // 画像読み出し
            for (int j = 0; j < pixels; j++)
            {
                unsigned char tmp_value;
                _test_image_ifs.read((char *)&tmp_value, sizeof(tmp_value));
                tmp_image[j] = (double)(tmp_value);
            }
            test_X.row(i) = Map<Matrix<double, 1, 28 * 28>>(&(tmp_image[0]));

            // ラベル読み出し
            unsigned char tmp_label;
            _test_label_ifs.read((char *)&tmp_label, sizeof(tmp_label));
            tmp_labels[i] = (double)(tmp_label);

            // one-hotか否かで場合分け
            if (one_hot_label)
            {
                test_y(i, int(tmp_label)) = 1;
            }
            else
            {
                test_y.row(i) = Map<Matrix<double, 1, 1>>(&(tmp_labels[i]));
            }
        }

        if (normalize)
        {
            test_X /= 255;
        }

        _test_load_count++;
        // カウンタリセット → バッチ数とカウントが同じになったら0にする
        _test_load_count = _test_load_count % _test_max_batch_num;
    }

    // ファイルパス setter
    void MnistEigenDataset::set_train_image_filepath(string filepath)
    {
        _train_image_filepath = filepath;
    }

    void MnistEigenDataset::set_train_label_filepath(string filepath)
    {
        _train_label_filepath = filepath;
    }

    void MnistEigenDataset::set_test_image_filepath(string filepath)
    {
        _test_image_filepath = filepath;
    }

    void MnistEigenDataset::set_test_label_filepath(string filepath)
    {
        _test_label_filepath = filepath;
    }

    // パス設定後の初期化処理
    void MnistEigenDataset::initialize_loader(void)
    {
        _init_test_loader();
        _init_train_loader();
    }

    // -------------------------------------------------------------
    //                   内部メソッド
    // -------------------------------------------------------------
    void MnistEigenDataset::_init_train_loader(void)
    {
        // 設定したファイルパスに基づいてファイルオープン
        _train_image_ifs.open(_train_image_filepath, std::ios::in | std::ios::binary);
        _train_label_ifs.open(_train_label_filepath, std::ios::in | std::ios::binary);
        int magic_number = 0;

        // 適切な位置までファイルポインタ移動
        _train_image_ifs.read((char *)&magic_number, sizeof(magic_number)); // sizeof(magic_number) = 4, つまり4byte分読み出し
        magic_number = LittleEndian2BigEndian(magic_number);
        _train_image_ifs.read((char *)&_number_of_train_data, sizeof(_number_of_train_data));
        _number_of_train_data = LittleEndian2BigEndian(_number_of_train_data);
        _train_image_ifs.read((char *)&_rows, sizeof(_rows));
        _rows = LittleEndian2BigEndian(_rows);
        _train_image_ifs.read((char *)&_cols, sizeof(_cols));
        _cols = LittleEndian2BigEndian(_cols);

        cout << "IMAGE magic number: " << magic_number << endl;

        // 適切な位置までファイルポインタ移動
        _train_label_ifs.read((char *)&magic_number, sizeof(magic_number));
        magic_number = LittleEndian2BigEndian(magic_number);
        _train_label_ifs.read((char *)&_number_of_train_data, sizeof(_number_of_train_data));
        _number_of_train_data = LittleEndian2BigEndian(_number_of_train_data);

        cout << "LABEL magic number: " << magic_number << endl;

        // ファイルポインタ：シーク位置の記憶
        _train_image_pos = _train_image_ifs.tellg();
        _train_label_pos = _train_label_ifs.tellg();

        // 読み出しのためのインデックス作成：単なる整数型でOK(int)
        for (int i = 0; i < _number_of_train_data; i++)
        {
            _train_indices.push_back(i);
        }
    }

    void MnistEigenDataset::_init_test_loader(void)
    {
        // 設定したファイルパスに基づいてファイルオープン
        _test_image_ifs.open(_test_image_filepath, std::ios::in | std::ios::binary);
        _test_label_ifs.open(_test_label_filepath, std::ios::in | std::ios::binary);
        int magic_number = 0;

        // 適切な位置までディスクリプタ移動
        _test_image_ifs.read((char *)&magic_number, sizeof(magic_number)); // sizeof(magic_number) = 4, つまり4byte分読み出し
        magic_number = LittleEndian2BigEndian(magic_number);
        _test_image_ifs.read((char *)&_number_of_test_data, sizeof(_number_of_test_data));
        _number_of_test_data = LittleEndian2BigEndian(_number_of_test_data);
        _test_image_ifs.read((char *)&_rows, sizeof(_rows));
        _rows = LittleEndian2BigEndian(_rows);
        _test_image_ifs.read((char *)&_cols, sizeof(_cols));
        _cols = LittleEndian2BigEndian(_cols);

        cout << "IMAGE magic number: " << magic_number << endl;

        // 適切な位置までディスクリプタ移動
        _test_label_ifs.read((char *)&magic_number, sizeof(magic_number));
        magic_number = LittleEndian2BigEndian(magic_number);
        _test_label_ifs.read((char *)&_number_of_test_data, sizeof(_number_of_test_data));
        _number_of_test_data = LittleEndian2BigEndian(_number_of_test_data);

        cout << "LABEL magic number: " << magic_number << endl;

        // ファイルポインタ：シーク位置の記憶
        _test_image_pos = _test_image_ifs.tellg();
        _test_label_pos = _test_label_ifs.tellg();

        // 読み出しのためのインデックス作成：単なる整数型でOK(int)
        for (int i = 0; i < _number_of_test_data; i++)
        {
            _test_indices.push_back(i);
        }
    }

}