/*
==============================================================================
KratosIncompressibleFluidApplication
A library based on:
Kratos
A General Purpose Software for Multi-Physics Finite Element Analysis
Version 1.0 (Released on march 05, 2007).

Copyright 2007
Pooyan Dadvand, Riccardo Rossi
pooyan@cimne.upc.edu
rrossi@cimne.upc.edu
- CIMNE (International Center for Numerical Methods in Engineering),
Gran Capita' s/n, 08034 Barcelona, Spain

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 condition:

Distribution of this code for  any  commercial purpose  is permissible
ONLY BY DIRECT ARRANGEMENT WITH THE COPYRIGHT OWNERS.

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.

==============================================================================
*/

//
//   Project Name:        Kratos
//   Last modified by:    $Author: kazem $
//   Date:                $Date: 2008-12-15 10:10:27 $
//   Revision:            $Revision: 1.8 $
//
//

// System includes

// External includes
#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
#include <boost/timer.hpp>

// Project includes
#include "includes/define.h"
#include "custom_python/add_custom_strategies_to_python.h"

#include "spaces/ublas_space.h"

//strategies
#include "solving_strategies/strategies/solving_strategy.h"
#include "solving_strategies/strategies/residualbased_linear_strategy.h"

//convergence criterias
#include "custom_strategies/strategies/residualbased_fluid_strategy.h"
#include "custom_strategies/strategies/residualbased_ND_fluid_strategy.h"
#include "custom_strategies/strategies/residualbased_fluid_strategy_coupled.h"
#include "custom_strategies/strategies/residualbased_lagrangian_monolithic_scheme.h"
#include "custom_strategies/strategies/newton_raphson_oss_strategy.h"
#include "custom_strategies/convergencecriterias/UP_criteria.h"
//linear solvers
#include "linear_solvers/linear_solver.h"

namespace Kratos
{

	namespace Python
	{
		using namespace boost::python;

		void  AddCustomStrategiesToPython()
		{
			typedef UblasSpace<double, CompressedMatrix, Vector> SparseSpaceType;
			typedef UblasSpace<double, Matrix, Vector> LocalSpaceType;

			typedef LinearSolver<SparseSpaceType, LocalSpaceType > LinearSolverType;
			typedef SolvingStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType > BaseSolvingStrategyType;
			typedef Scheme< SparseSpaceType, LocalSpaceType > BaseSchemeType;
			typedef ConvergenceCriteria< SparseSpaceType, LocalSpaceType > TConvergenceCriteriaType;
			//********************************************************************
			//********************************************************************
			//

			class_< ResidualBasedFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >,
					bases< BaseSolvingStrategyType >,  boost::noncopyable >
				("ResidualBasedFluidStrategy",
				init<ModelPart&, LinearSolverType::Pointer, LinearSolverType::Pointer,
				bool, bool, bool,
				double, double,
				int, int,
				unsigned int, unsigned int, unsigned int,
				bool
				>() )
				  .def("SolveStep1",&ResidualBasedFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::SolveStep1)
				  .def("SolveStep2",&ResidualBasedFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::SolveStep2)
				  .def("SolveStep2_Mp",&ResidualBasedFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::SolveStep2_Mp)
				  .def("SolveStep3",&ResidualBasedFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::SolveStep3)
				  .def("SolveStep4",&ResidualBasedFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::SolveStep4)
				  .def("ActOnLonelyNodes",&ResidualBasedFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::ActOnLonelyNodes)
				  .def("Clear",&ResidualBasedFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::Clear)
				  .def("FractionalVelocityIteration",&ResidualBasedFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::FractionalVelocityIteration)
				  .def("ConvergenceCheck",&ResidualBasedFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::ConvergenceCheck)
				  .def("InitializeFractionalStep",&ResidualBasedFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::InitializeFractionalStep)
				  .def("PredictVelocity",&ResidualBasedFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::PredictVelocity)
				  .def("InitializeProjections",&ResidualBasedFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::InitializeProjections)
				  .def("AssignInitialStepValues",&ResidualBasedFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::AssignInitialStepValues)
				  .def("IterativeSolve",&ResidualBasedFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::IterativeSolve)
				  .def("SavePressureIteration",&ResidualBasedFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::SavePressureIteration)
				  .def("ApplyFractionalVelocityFixity",&ResidualBasedFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::ApplyFractionalVelocityFixity)				;

			class_< ResidualBasedNDFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >,
					bases< BaseSolvingStrategyType >,  boost::noncopyable >
				("ResidualBasedNDFluidStrategy",
				init<ModelPart&, LinearSolverType::Pointer, LinearSolverType::Pointer,
				bool, bool, bool,
				double, double,
				int, int,
				unsigned int, unsigned int, unsigned int,
				bool
				>() )
				  .def("SolveStep1",&ResidualBasedNDFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::SolveStep1)
				  .def("SolveStep2",&ResidualBasedNDFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::SolveStep2)
				  .def("SolveStep3",&ResidualBasedNDFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::SolveStep3)
				  .def("SolveStep4",&ResidualBasedNDFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::SolveStep4)
				  .def("ActOnLonelyNodes",&ResidualBasedNDFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::ActOnLonelyNodes)
				  .def("Clear",&ResidualBasedNDFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::Clear)
				  .def("FractionalVelocityIteration",&ResidualBasedNDFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::FractionalVelocityIteration)
				  .def("ConvergenceCheck",&ResidualBasedNDFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::ConvergenceCheck)
				  .def("InitializeFractionalStep",&ResidualBasedNDFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::InitializeFractionalStep)
				  .def("PredictVelocity",&ResidualBasedNDFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::PredictVelocity)
				  .def("InitializeProjections",&ResidualBasedNDFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::InitializeProjections)
				  .def("AssignInitialStepValues",&ResidualBasedNDFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::AssignInitialStepValues)
				  .def("IterativeSolve",&ResidualBasedNDFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::IterativeSolve)
				  .def("SavePressureIteration",&ResidualBasedNDFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::SavePressureIteration)
				  .def("ApplyFractionalVelocityFixity",&ResidualBasedNDFluidStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >::ApplyFractionalVelocityFixity)				;

			class_< ResidualBasedFluidStrategyCoupled< SparseSpaceType, LocalSpaceType, LinearSolverType >,
					bases< BaseSolvingStrategyType >,  boost::noncopyable >
				("ResidualBasedFluidStrategyCoupled",
				init<ModelPart&, LinearSolverType::Pointer, LinearSolverType::Pointer,
				bool, bool, bool,
				double, double,
				int, int,
				unsigned int, unsigned int, unsigned int,
				bool
				>() )
				  .def("SolveStep1",&ResidualBasedFluidStrategyCoupled< SparseSpaceType, LocalSpaceType, LinearSolverType >::SolveStep1)
				  .def("SolveStep2",&ResidualBasedFluidStrategyCoupled< SparseSpaceType, LocalSpaceType, LinearSolverType >::SolveStep2)
				  .def("SolveStep2_Mp",&ResidualBasedFluidStrategyCoupled< SparseSpaceType, LocalSpaceType, LinearSolverType >::SolveStep2_Mp)
				  .def("SolveStep3",&ResidualBasedFluidStrategyCoupled< SparseSpaceType, LocalSpaceType, LinearSolverType >::SolveStep3)
				  .def("SolveStep4",&ResidualBasedFluidStrategyCoupled< SparseSpaceType, LocalSpaceType, LinearSolverType >::SolveStep4)
				  .def("ActOnLonelyNodes",&ResidualBasedFluidStrategyCoupled< SparseSpaceType, LocalSpaceType, LinearSolverType >::ActOnLonelyNodes)
				  .def("Clear",&ResidualBasedFluidStrategyCoupled< SparseSpaceType, LocalSpaceType, LinearSolverType >::Clear)
				  .def("FractionalVelocityIteration",&ResidualBasedFluidStrategyCoupled< SparseSpaceType, LocalSpaceType, LinearSolverType >::FractionalVelocityIteration)
				  .def("ConvergenceCheck",&ResidualBasedFluidStrategyCoupled< SparseSpaceType, LocalSpaceType, LinearSolverType >::ConvergenceCheck)
				  .def("InitializeFractionalStep",&ResidualBasedFluidStrategyCoupled< SparseSpaceType, LocalSpaceType, LinearSolverType >::InitializeFractionalStep)
				  .def("PredictVelocity",&ResidualBasedFluidStrategyCoupled< SparseSpaceType, LocalSpaceType, LinearSolverType >::PredictVelocity)
				  .def("InitializeProjections",&ResidualBasedFluidStrategyCoupled< SparseSpaceType, LocalSpaceType, LinearSolverType >::InitializeProjections)
				  .def("AssignInitialStepValues",&ResidualBasedFluidStrategyCoupled< SparseSpaceType, LocalSpaceType, LinearSolverType >::AssignInitialStepValues)
				  .def("IterativeSolve",&ResidualBasedFluidStrategyCoupled< SparseSpaceType, LocalSpaceType, LinearSolverType >::IterativeSolve)
				  .def("SavePressureIteration",&ResidualBasedFluidStrategyCoupled< SparseSpaceType, LocalSpaceType, LinearSolverType >::SavePressureIteration)
				  .def("ApplyFractionalVelocityFixity",&ResidualBasedFluidStrategyCoupled< SparseSpaceType, LocalSpaceType, LinearSolverType >::ApplyFractionalVelocityFixity)				;

				class_< ConvergenceCriteria< SparseSpaceType, LocalSpaceType >, boost::noncopyable >("ConvergenceCriteria", init<>() )
				.def("SetActualizeRHSFlag", &ConvergenceCriteria<SparseSpaceType, LocalSpaceType >::SetActualizeRHSFlag )
			 .def("GetActualizeRHSflag", &ConvergenceCriteria<SparseSpaceType, LocalSpaceType >::GetActualizeRHSflag )
			 .def("PreCriteria", &ConvergenceCriteria<SparseSpaceType, LocalSpaceType >::PreCriteria )
			 .def("PostCriteria", &ConvergenceCriteria<SparseSpaceType, LocalSpaceType >::PostCriteria )
			 .def("Initialize", &ConvergenceCriteria<SparseSpaceType, LocalSpaceType >::Initialize )
			 .def("InitializeSolutionStep", &ConvergenceCriteria<SparseSpaceType, LocalSpaceType >::InitializeSolutionStep )
			 .def("FinalizeSolutionStep", &ConvergenceCriteria<SparseSpaceType, LocalSpaceType >::FinalizeSolutionStep )
			 ;

			class_< UPCriteria<SparseSpaceType, LocalSpaceType >,
			         bases<ConvergenceCriteria< SparseSpaceType, LocalSpaceType > >,
			         boost::noncopyable >
			        ("UPCriteria", init< double, double, double, double>() );

        		   class_< ResidualBasedLagrangianMonolithicScheme<SparseSpaceType,LocalSpaceType>,
        			   bases< ResidualBasedIncrementalUpdateStaticScheme<SparseSpaceType,LocalSpaceType> >,  boost::noncopyable >
             			      (
               				     "ResidualBasedLagrangianMonolithicScheme", init< int >()
                		       );

			class_< NewtonRaphsonOssStrategy< SparseSpaceType, LocalSpaceType, LinearSolverType >,bases< BaseSolvingStrategyType >,  boost::noncopyable >
				("NewtonRaphsonOssStrategy",
				init<ModelPart&, BaseSchemeType::Pointer, LinearSolverType::Pointer, TConvergenceCriteriaType::Pointer, int, bool, bool, bool
				>() )
				;

		}

	}  // namespace Python.

} // Namespace Kratos


/* Copyright (C) 2014 InfiniDB, Inc.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; version 2 of
   the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   MA 02110-1301, USA. */

/*****************************************************************************
 * $Id$
 *
 ****************************************************************************/

#define BRMAUTOINCMGR_DLLEXPORT
#include "autoincrementmanager.h"
#undef BRMAUTOINCMGR_DLLEXPORT

#include <math.h>
#include <boost/date_time/posix_time/posix_time.hpp>

using namespace std;
using namespace boost;
using namespace boost::posix_time;

namespace BRM
{

AutoincrementManager::AutoincrementManager()
{
}

AutoincrementManager::~AutoincrementManager()
{
}

void AutoincrementManager::startSequence(uint32_t oid, uint64_t firstNum, uint32_t colWidth,
        execplan::CalpontSystemCatalog::ColDataType colDataType)
{
    boost::mutex::scoped_lock lk(lock);
    map<uint64_t, sequence>::iterator it;
    sequence s;

    it = sequences.find(oid);

    if (it != sequences.end())
        return;

    s.value = firstNum;

    if (isUnsigned(colDataType))
    {
        s.overflow = (0xFFFFFFFFFFFFFFFFULL >> (64 - colWidth * 8)) - 1;
    }
    else
    {
        s.overflow = (1ULL << (colWidth * 8 - 1));
    }

    sequences[oid] = s;
}

bool AutoincrementManager::getAIRange(uint32_t oid, uint64_t count, uint64_t* firstNum)
{
    boost::mutex::scoped_lock lk(lock);
    map<uint64_t, sequence>::iterator it;

    it = sequences.find(oid);

    if (it == sequences.end())
        throw runtime_error("There is no sequence with that lock");

    if ((count >= it->second.overflow ||
            count + it->second.value > it->second.overflow ||
            count + it->second.value <= it->second.value)
            && count != 0)
        return false;

    *firstNum = it->second.value;
    it->second.value += count;
    return true;
}

void AutoincrementManager::resetSequence(uint32_t oid, uint64_t value)
{
    boost::mutex::scoped_lock lk(lock);
    map<uint64_t, sequence>::iterator it;

    it = sequences.find(oid);

    if (it == sequences.end())
        return;

    it->second.value = value;
}

const uint32_t AutoincrementManager::lockTime = 30;

void AutoincrementManager::getLock(uint32_t oid)
{
    boost::mutex::scoped_lock lk(lock);
    map<uint64_t, sequence>::iterator it;
    ptime stealTime = microsec_clock::local_time() + seconds(lockTime);

    bool gotIt = false;

    it = sequences.find(oid);

    if (it == sequences.end())
        throw runtime_error("There is no sequence with that lock");

    lk.unlock();

    while (!gotIt && microsec_clock::local_time() < stealTime)
    {
        gotIt = it->second.lock.try_lock();

        if (!gotIt)
            usleep(100000);
    }

    // If !gotIt, take possession
}

void AutoincrementManager::releaseLock(uint32_t oid)
{
    boost::mutex::scoped_lock lk(lock);
    map<uint64_t, sequence>::iterator it;

    it = sequences.find(oid);

    if (it == sequences.end())
        return;   // it's unlocked if the lock doesn't exist...

    lk.unlock();

    it->second.lock.unlock();
}

void AutoincrementManager::deleteSequence(uint32_t oid)
{
    boost::mutex::scoped_lock lk(lock);
    map<uint64_t, sequence>::iterator it;

    it = sequences.find(oid);

    if (it != sequences.end())
        sequences.erase(it);
}

} /* namespace BRM */


/*

Copyright (c) 2005-2017, 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 CRYPTPROJECTIONFORCE_HPP_
#define CRYPTPROJECTIONFORCE_HPP_

#include "GeneralisedLinearSpringForce.hpp"

#include "ChasteSerialization.hpp"
#include <boost/serialization/base_object.hpp>

/**
 * A force law for use in crypt projection simulations.
 */
class CryptProjectionForce : public GeneralisedLinearSpringForce<2>
{
    friend class TestCryptProjectionForce;

private:

    friend class boost::serialization::access;
    /**
     * Boost Serialization method for archiving/checkpointing.
     * Archives the object and its member variables.
     *
     * @param archive  The boost archive.
     * @param version  The current version of this class.
     */
    template<class Archive>
    void serialize(Archive & archive, const unsigned int version)
    {
        archive & boost::serialization::base_object<GeneralisedLinearSpringForce<2> >(*this);
        archive & mA;
        archive & mB;
        archive & mIncludeWntChemotaxis;
        archive & mWntChemotaxisStrength;
    }

    /**
     * The value of the constant a in the definition of the crypt surface
     *      z = f(r) = a*r^b.
     */
    double mA;

    /**
     * The value of the constant b in the definition of the crypt surface
     *      z = f(r) = a*r^b.
     */
    double mB;

    /**
     * Whether to include Wnt-dependent chemotaxis for stem cells.
     */
    bool mIncludeWntChemotaxis;

    /**
     * Strength of Wnt-based chemotactic force.
     */
    double mWntChemotaxisStrength;

    /**
     * Map node indices to 3D locations on the crypt surface.
     */
    std::map<unsigned, c_vector<double, 3> > mNode3dLocationMap;

    /**
     * Fix up the mappings between node indices and 3D locations.
     *
     * @param rCellPopulation the cell population
     */
    void UpdateNode3dLocationMap(AbstractCellPopulation<2>& rCellPopulation);

    /**
     * Calculates the force between two nodes.
     *
     * Note that this assumes they are connected and is called by rCalculateVelocitiesOfEachNode()
     *
     * @param nodeAGlobalIndex the index of the first node
     * @param nodeBGlobalIndex the index of the second node
     * @param rCellPopulation the cell population
     *
     * @return The force exerted on Node A by Node B.
     */
    c_vector<double,2> CalculateForceBetweenNodes(unsigned nodeAGlobalIndex, unsigned nodeBGlobalIndex, AbstractCellPopulation<2>& rCellPopulation);

public:

    /**
     * Constructor.
     */
    CryptProjectionForce();

    /**
     * Destructor.
     */
    ~CryptProjectionForce();

    /**
     * @return mA.
     */
    double GetA() const;

    /**
     * @return mB.
     */
    double GetB() const;

    /**
     * @return mWntChemotaxisStrength
     */
    double GetWntChemotaxisStrength();

    /**
     * Set mWntChemotaxisStrength.
     *
     * @param wntChemotaxisStrength the new value of mWntChemotaxisStrength
     */
    void SetWntChemotaxisStrength(double wntChemotaxisStrength);

    /**
     * Set mIncludeWntChemotaxis.
     *
     * @param includeWntChemotaxis whether to include Wnt-dependent chemotaxis
     */
    void SetWntChemotaxis(bool includeWntChemotaxis);

    /**
     * Calculates the height of the crypt surface given by
     *     z = f(r) = a*r^b
     * at a point whose 2D position is a distance r from the centre of the cell population.
     * This assumes that the cell population is centred at the origin.
     *
     * @param rNodeLocation
     * @return the z component corresponding to rNodeLocation
     */
    double CalculateCryptSurfaceHeightAtPoint(const c_vector<double,2>& rNodeLocation);

    /**
     * Calculates the derivative df/dr of the crypt surface function z=f(r) at a point
     * whose 2D position is a distance r from the centre of the cell_population, which we assume
     * to be at (0,0).
     *
     * @param rNodeLocation the 2D location of a node
     * @return the gradient
     */
    double CalculateCryptSurfaceDerivativeAtPoint(const c_vector<double,2>& rNodeLocation);

    /**
     * Overridden AddForceContribution method.
     *
     * @param rCellPopulation reference to the cell population
     */
    void AddForceContribution(AbstractCellPopulation<2>& rCellPopulation);

    /**
     * Overridden OutputForceParameters() method.
     *
     * @param rParamsFile the file stream to which the parameters are output
     */
    void OutputForceParameters(out_stream& rParamsFile);
};

// Declare identifier for the serializer
#include "SerializationExportWrapper.hpp"
CHASTE_CLASS_EXPORT(CryptProjectionForce)

#endif /*CRYPTPROJECTIONFORCE_HPP_*/


/**
 * @file
 *
 *   Unless noted otherwise, the portions of Isis written by the USGS are public
 *   domain. See individual third-party library and package descriptions for
 *   intellectual property information,user agreements, and related information.
 *
 *   Although Isis has been used by the USGS, no warranty, expressed or implied,
 *   is made by the USGS as to the accuracy and functioning of such software
 *   and related material nor shall the fact of distribution constitute any such
 *   warranty, and no responsibility is assumed by the USGS in connection
 *   therewith.
 *
 *   For additional information, launch
 *   $ISISROOT/doc//documents/Disclaimers/Disclaimers.html in a browser or see
 *   the Privacy & Disclaimers page on the Isis website,
 *   http://isis.astrogeology.usgs.gov, and the USGS privacy and disclaimers on
 *   http://www.usgs.gov/privacy.html.
 */
#include <cmath>
#include <iostream>

#include <boost/math/special_functions/legendre.hpp>

#include "Camera.h"
#include "Constants.h"
#include "FunctionTools.h"
#include "IString.h"
#include "NewHorizonsMvicFrameCameraDistortionMap.h"

#include "CameraFocalPlaneMap.h"

#include <QDebug>

using namespace boost::math;
using namespace std;
using namespace Isis;

namespace Isis {
  /** Camera distortion map constructor
   *
   * This class maps between distorted and undistorted focal plane x/y's. The default mapping is the
   * identity, that is, the focal plane x/y and undistorted focal plane x/y will be identical.
   *
   * @param parent              the parent camera that will use this distortion map
   * @param zDirection          the direction of the focal plane Z-axis
   *                            (either 1 or -1)
   *
   * @param xDistortionCoeffs   distortion coefficients in x
   * @param yDistortionCoeffs   distortion coefficients in y
   */
  NewHorizonsMvicFrameCameraDistortionMap::NewHorizonsMvicFrameCameraDistortionMap(Camera *parent,
                                                             vector<double> xDistortionCoeffs,
                                                             vector<double> yDistortionCoeffs) :
    CameraDistortionMap(parent, 1.0) {

    m_xDistortionCoeffs = xDistortionCoeffs;
    m_yDistortionCoeffs = yDistortionCoeffs;

    double pixelPitch = p_camera->PixelPitch();

    m_focalPlaneHalf_x = 0.5 * p_camera->Samples() * pixelPitch; // 32.5 mm
    m_focalPlaneHalf_y = 0.5 * p_camera->Lines() * pixelPitch;   // 0.832 mm
  }

  /** Destructor
   */
  NewHorizonsMvicFrameCameraDistortionMap::~NewHorizonsMvicFrameCameraDistortionMap() {
  }

//  /**
//   * Testing method to output corrections in x and y at pixel centers for entire focal plane.
//   * Output in csv format for viewing/plotting in Excel.
//   */
//bool NewHorizonsMvicFrameCameraDistortionMap::outputDeltas() {
//
//  QString ofname("mvic_frame_deltas.csv");
//  std::ofstream fp_out(ofname.toLatin1().data(), std::ios::out);
//  if (!fp_out)
//    return false;
//
//  char buf[1056];
//
//  double deltax, deltay;
//
//  for (double line = 0.5; line <= 128.5; line += 1.0) {    // loop in y direction
//    for (double sample=0.5; sample <= 5000.5; sample += 1.0) {      // loop in x direction
//
//      p_camera->FocalPlaneMap()->SetDetector(sample,line);
//
//      double fplanex = p_camera->FocalPlaneMap()->FocalPlaneX();
//      double fplaney = p_camera->FocalPlaneMap()->FocalPlaneY();
//
//      SetFocalPlane(fplanex,fplaney);
//
//      deltax = fplanex - p_undistortedFocalPlaneX;
//      deltay = fplaney - p_undistortedFocalPlaneY;
//
//      sprintf(buf, "%lf,%lf,%lf,%lf\n", sample, deltax/0.013, line, deltay/0.013);
//
//      fp_out << buf;
//    }
//  }
//
//  fp_out.close();
//
//  return true;
//}

  /** Compute undistorted focal plane x/y
   *
   * Compute undistorted focal plane x/y given a distorted focal plane x/y.
   *
   * @param dx distorted focal plane x in millimeters
   * @param dy distorted focal plane y in millimeters
   *
   * @return if the conversion was successful
   * @see SetDistortion
   */
  bool NewHorizonsMvicFrameCameraDistortionMap::SetFocalPlane(const double dx, const double dy) {

    p_focalPlaneX = dx;
    p_focalPlaneY = dy;

    // in case of failures, initialize undistorted focal plane coordinates to the distorted
    // coordinate values
    p_undistortedFocalPlaneX = dx;
    p_undistortedFocalPlaneY = dy;

    // if x and/or y lie outside of the detector, do NOT apply distortion
    // set undistorted focal plane values to be identical to raw values
    if ((fabs(dx) > m_focalPlaneHalf_x) || (fabs(dy) > m_focalPlaneHalf_y)) {
      return true;
    }

    // shift from ISIS MVIC FT image coordinate system with +x to the left and +y down to
    // the desired system of +x to the right and +y up
    // e.g. negate x and y

    // scale x and y to lie in the range -1.0 to +1.0
    // this is requirement for Legendre Polynomials, man
    double xscaled = -dx/m_focalPlaneHalf_x;
    double yscaled = -dy/m_focalPlaneHalf_y;

    // compute distortion corrections in x and y using Legendre Polynomials
    // these corrections are also in the -1.0 to +1.0 range
    // if Legendre computations fail, we set undistorted focal plane x and y
    // to be identical to distorted x and y and return true
    double deltax, deltay;
    if (!computeDistortionCorrections(xscaled, yscaled, deltax, deltay)) {
      return true;
    }

    // apply the corrections to original x,y
    xscaled += deltax;
    yscaled += deltay;

    // scale back from range of '-1.0 to +1.0' to the detector, '-32.5 to 32.5 mm'
    p_undistortedFocalPlaneX = -xscaled * m_focalPlaneHalf_x;
    p_undistortedFocalPlaneY = -yscaled * m_focalPlaneHalf_y;

    return true;
  }

//  bool NewHorizonsMvicFrameCameraDistortionMap::SetFocalPlane(const double dx, const double dy) {

//    p_focalPlaneX = dx;
//    p_focalPlaneY = dy;

//    // if x and/or y lie outside of the detector, do NOT apply distortion
//    // set undistorted focal plane values to be identical to raw values
//    if ((fabs(dx) > m_focalPlaneHalf_x) || (fabs(dy) > m_focalPlaneHalf_y)) {
//      p_undistortedFocalPlaneX = dx;
//      p_undistortedFocalPlaneY = dy;

//      return true;
//    }

//    // scale x and y to lie in the range -1.0 to +1.0
//    // this is requirement for Legendre Polynomials, man
//    double xscaled = dx/m_focalPlaneHalf_x;
//    double yscaled = dy/m_focalPlaneHalf_y;

//    // compute distortion corrections in x and y using Legendre Polynomials
//    // these corrections are also in the -1.0 to +1.0 range
//    double deltax, deltay;
//    computeDistortionCorrections(xscaled, yscaled, deltax, deltay);

//    // apply the corrections
//    xscaled += deltax;
//    yscaled += deltay;

//    // scale back from range of '-1.0 to +1.0' to the detector, '-32.656 to 32.656 mm'
//    p_undistortedFocalPlaneX = xscaled * m_focalPlaneHalf_x;
//    p_undistortedFocalPlaneY = yscaled * m_focalPlaneHalf_y;

//    return true;
//  }

  /** Compute distorted focal plane x/y
   *
   * Compute distorted focal plane x/y given an undistorted focal plane x/y.
   *
   * This is an iterative procedure.
   *
   * @param ux undistorted focal plane x in millimeters
   * @param uy undistorted focal plane y in millimeters
   *
   * @return if the conversion was successful
   * @see SetDistortion
   */
  bool NewHorizonsMvicFrameCameraDistortionMap::SetUndistortedFocalPlane(const double ux, const double uy) {

    // image coordinates prior to introducing distortion
    p_undistortedFocalPlaneX = ux;
    p_undistortedFocalPlaneY = uy;

    double xScaledDistortion, yScaledDistortion;

    // scale undistorted coordinates to range of -1.0 to +1.0
    double xtScaled = -ux/m_focalPlaneHalf_x;
    double ytScaled = -uy/m_focalPlaneHalf_y;

    double uxScaled = xtScaled;
    double uyScaled = ytScaled;

    double xScaledPrevious = 1000000.0;
    double yScaledPrevious = 1000000.0;

    double tolerance = 0.000001;

    bool bConverged = false;

    // iterating to introduce distortion...
    // we stop when the difference between distorted coordinates
    // in successive iterations is at or below the given tolerance
    for( int i = 0; i < 50; i++ ) {

      // compute distortion in x and y (scaled to -1.0 - +1.0) using Legendre Polynomials
      computeDistortionCorrections(xtScaled, ytScaled, xScaledDistortion, yScaledDistortion);

      // update scaled image coordinates
      xtScaled = uxScaled - xScaledDistortion;
      ytScaled = uyScaled - yScaledDistortion;

      // check for convergence
      if((fabs(xtScaled - xScaledPrevious) <= tolerance) && (fabs(ytScaled - yScaledPrevious) <= tolerance)) {
        bConverged = true;
        break;
      }

      xScaledPrevious = xtScaled;
      yScaledPrevious = ytScaled;
    }

    if (bConverged) {
      // scale coordinates back to detector (-32.5 to +32.5)
      xtScaled *= -m_focalPlaneHalf_x;
      ytScaled *= -m_focalPlaneHalf_y;

      // set distorted coordinates
      p_focalPlaneX = xtScaled;
      p_focalPlaneY = ytScaled;
    }

    return bConverged;
  }
//  bool NewHorizonsMvicFrameCameraDistortionMap::SetUndistortedFocalPlane(const double ux, const double uy) {

//    // image coordinates prior to introducing distortion
//    p_undistortedFocalPlaneX = ux;
//    p_undistortedFocalPlaneY = uy;

//    double xScaledDistortion, yScaledDistortion;

//    // scale undistorted coordinates to range of -1.0 to +1.0
//    double xtScaled = ux/m_focalPlaneHalf_x;
//    double ytScaled = uy/m_focalPlaneHalf_y;

//    double uxScaled = xtScaled;
//    double uyScaled = ytScaled;

//    double xScaledPrevious = 1000000.0;
//    double yScaledPrevious = 1000000.0;

//    double tolerance = 0.000001;

//    bool bConverged = false;

//    // iterating to introduce distortion...
//    // we stop when the difference between distorted coordinates
//    // in successive iterations is at or below the given tolerance
//    for( int i = 0; i < 50; i++ ) {

//      // compute distortion in x and y (scaled to -1.0 - +1.0) using Legendre Polynomials
//      computeDistortionCorrections(xtScaled, ytScaled, xScaledDistortion, yScaledDistortion);

//      // update scaled image coordinates
//      xtScaled = uxScaled - xScaledDistortion;
//      ytScaled = uyScaled - yScaledDistortion;

//      // check for convergence
//      if((fabs(xtScaled - xScaledPrevious) <= tolerance) && (fabs(ytScaled - yScaledPrevious) <= tolerance)) {
//        bConverged = true;
//        break;
//      }

//      xScaledPrevious = xtScaled;
//      yScaledPrevious = ytScaled;
//    }

//    if (bConverged) {
//      // scale coordinates back to detector (-32.656 to +32.656)
//      xtScaled *= m_focalPlaneHalf_x;
//      ytScaled *= m_focalPlaneHalf_y;

//      // set distorted coordinates
//      p_focalPlaneX = xtScaled;
//      p_focalPlaneY = ytScaled;
//    }

//    return bConverged;
//  }

  /** Compute distortion corrections in x and y direction
   *
   * For Legendre Polynomials, see ...
   *
   * http://mathworld.wolfram.com/LegendrePolynomial.html
   * http://www.boost.org/doc/libs/1_36_0/libs/math/doc/sf_and_dist/html/math_toolkit/special/sf_poly/legendre.html
   *
   * @param xscaled focal plane x scaled to range of 1- to 1 for Legendre Polynomials
   * @param yscaled focal plane y scaled to range of 1- to 1 for Legendre Polynomials
   * @param deltax focal plane distortion correction to x in millimeters
   * @param deltay focal plane distortion correction to y in millimeters
   *
   * @return if successful
   */
  bool NewHorizonsMvicFrameCameraDistortionMap::computeDistortionCorrections(const double xscaled,
                                                                  const double yscaled,
                                                                  double &deltax, double &deltay) {

    double lpx0, lpx1, lpx2, lpx3, lpx4, lpx5;
    double lpy0, lpy1, lpy2, lpy3, lpy4, lpy5;

    // Legendre polynomials
    // boost library method legendre_p will generate an exception if xscaled or yscaled do not lie
    // between -1 to 1 (inclusive). In this event we return false.
    try {
      lpx0 = legendre_p(0,xscaled);
      lpx1 = legendre_p(1,xscaled);
      lpx2 = legendre_p(2,xscaled);
      lpx3 = legendre_p(3,xscaled);
      lpx4 = legendre_p(4,xscaled);
      lpx5 = legendre_p(5,xscaled);
      lpy0 = legendre_p(0,yscaled);
      lpy1 = legendre_p(1,yscaled);
      lpy2 = legendre_p(2,yscaled);
      lpy3 = legendre_p(3,yscaled);
      lpy4 = legendre_p(4,yscaled);
      lpy5 = legendre_p(5,yscaled);
    }
    // TESTING NOTE: Could not find a way to cause this error. If one is found a test should be added
    catch (const std::exception& e) {
      return false;
    }

    deltax =
       m_xDistortionCoeffs[0] * lpx0 * lpy1 +
       m_xDistortionCoeffs[1] * lpx1 * lpy0 +
       m_xDistortionCoeffs[2] * lpx0 * lpy2 +
       m_xDistortionCoeffs[3] * lpx1 * lpy1 +
       m_xDistortionCoeffs[4] * lpx2 * lpy0 +
       m_xDistortionCoeffs[5] * lpx0 * lpy3 +
       m_xDistortionCoeffs[6] * lpx1 * lpy2 +
       m_xDistortionCoeffs[7] * lpx2 * lpy1 +
       m_xDistortionCoeffs[8] * lpx3 * lpy0 +
       m_xDistortionCoeffs[9] * lpx0 * lpy4 +
      m_xDistortionCoeffs[10] * lpx1 * lpy3 +
      m_xDistortionCoeffs[11] * lpx2 * lpy2 +
      m_xDistortionCoeffs[12] * lpx3 * lpy1 +
      m_xDistortionCoeffs[13] * lpx4 * lpy0 +
      m_xDistortionCoeffs[14] * lpx0 * lpy5 +
      m_xDistortionCoeffs[15] * lpx1 * lpy4 +
      m_xDistortionCoeffs[16] * lpx2 * lpy3 +
      m_xDistortionCoeffs[17] * lpx3 * lpy2 +
      m_xDistortionCoeffs[18] * lpx4 * lpy1 +
      m_xDistortionCoeffs[19] * lpx5 * lpy0;

    deltay =
      m_yDistortionCoeffs[0] * lpx0 * lpy1 +
      m_yDistortionCoeffs[1] * lpx1 * lpy0 +
      m_yDistortionCoeffs[2] * lpx0 * lpy2 +
      m_yDistortionCoeffs[3] * lpx1 * lpy1 +
      m_yDistortionCoeffs[4] * lpx2 * lpy0 +
      m_yDistortionCoeffs[5] * lpx0 * lpy3 +
      m_yDistortionCoeffs[6] * lpx1 * lpy2 +
      m_yDistortionCoeffs[7] * lpx2 * lpy1 +
      m_yDistortionCoeffs[8] * lpx3 * lpy0 +
      m_yDistortionCoeffs[9] * lpx0 * lpy4 +
     m_yDistortionCoeffs[10] * lpx1 * lpy3 +
     m_yDistortionCoeffs[11] * lpx2 * lpy2 +
     m_yDistortionCoeffs[12] * lpx3 * lpy1 +
     m_yDistortionCoeffs[13] * lpx4 * lpy0 +
     m_yDistortionCoeffs[14] * lpx0 * lpy5 +
     m_yDistortionCoeffs[15] * lpx1 * lpy4 +
     m_yDistortionCoeffs[16] * lpx2 * lpy3 +
     m_yDistortionCoeffs[17] * lpx3 * lpy2 +
     m_yDistortionCoeffs[18] * lpx4 * lpy1 +
     m_yDistortionCoeffs[19] * lpx5 * lpy0;

    return true;
  }
}


#ifndef STAN_MATH_TORSTEN_SOLVE_GROUP_ODE_HPP
#define STAN_MATH_TORSTEN_SOLVE_GROUP_ODE_HPP

#include <Eigen/Dense>
#include <stan/math/torsten/to_array_2d.hpp>
#include <stan/math/torsten/ev_manager.hpp>
#include <stan/math/torsten/pmx_population_check.hpp>
#include <stan/math/torsten/pmx_ode_model.hpp>
#include <stan/math/torsten/pmx_check.hpp>
#include <stan/math/torsten/pmx_solve_ode.hpp>
#include <vector>

namespace torsten {

  template<typename integrator_type>
  struct PMXSolveGroupODE {
    static constexpr double RTOL_DE = 1.e-6;
    static constexpr double ATOL_DE = 1.e-6;
    static constexpr int MAXSTEP_DE = 1e6;
    static constexpr double RTOL_AS = 1.e-6;
    static constexpr double ATOL_AS = 1.e-6;
    static constexpr int MAXSTEP_AS = 1e2;
    /*
     * For population models, more often we use ragged arrays
     * to describe the entire population, so in addition we need the arrays of
     * the length of each individual's data. The size of that
     * vector is the size of the population.
     */
    template <typename T0, typename T1, typename T2, typename T3, typename T4,
              typename T5, typename T6, typename F>
    static Eigen::Matrix<typename  EventsManager<NONMENEventsRecord<T0, T1, T2, T3>, NonEventParameters<T0, T4, std::vector, std::tuple<T5, T6> > >::T_scalar, // NOLINT
                         Eigen::Dynamic, Eigen::Dynamic>
    solve(const F& f,
          const int nCmt,
          const std::vector<int>& len,
          TORSTEN_PMX_FUNC_EVENTS_ARGS,
          const std::vector<std::vector<T4> >& pMatrix,
          const std::vector<std::vector<T5> >& biovar,
          const std::vector<std::vector<T6> >& tlag,
          double rel_tol,
          double abs_tol,
          long int max_num_steps,
          double as_rel_tol,
          double as_abs_tol,
          long int as_max_num_steps,
          std::ostream* msgs) {
      static const char* caller("PMX SOLVE GROUP ODE");
      torsten::pmx_population_check(len, time, amt, rate, ii, evid, cmt, addl, ss, pMatrix, caller);
      torsten::pmx_population_check(len, time, biovar, tlag, caller);

      using ER = NONMENEventsRecord<T0, T1, T2, T3>;
      using EM = EventsManager<ER, NonEventParameters<T0, T4, std::vector, std::tuple<T5, T6> >>;
      ER events_rec(nCmt, len, time, amt, rate, ii, evid, cmt, addl, ss);

      using model_type = torsten::PKODEModel<typename EM::T_par, F>;
      integrator_type integrator(rel_tol, abs_tol, max_num_steps, as_rel_tol, as_abs_tol, as_max_num_steps, msgs);
      EventSolver<model_type, EM> pr;

      Eigen::Matrix<typename EM::T_scalar, -1, -1> pred(nCmt, events_rec.total_num_event_times);

      pr.pred(events_rec, pred, integrator, pMatrix, biovar, tlag, nCmt, f);

      return pred;
    }

    /*
     * overload with default ode & algebra solver controls
     */
    template <typename T0, typename T1, typename T2, typename T3, typename T4,
              typename T5, typename T6, typename F>
    static Eigen::Matrix<typename  EventsManager<NONMENEventsRecord<T0, T1, T2, T3>, NonEventParameters<T0, T4, std::vector, std::tuple<T5, T6> > >::T_scalar, // NOLINT
                         Eigen::Dynamic, Eigen::Dynamic>
    solve(const F& f,
          const int nCmt,
          const std::vector<int>& len,
          TORSTEN_PMX_FUNC_EVENTS_ARGS,
          const std::vector<std::vector<T4> >& pMatrix,
          const std::vector<std::vector<T5> >& biovar,
          const std::vector<std::vector<T6> >& tlag,
          std::ostream* msgs) {
      return solve(f, nCmt, len, time, amt, rate,
                   ii, evid, cmt, addl, ss,
                   pMatrix, biovar, tlag,
                   RTOL_DE, ATOL_DE, MAXSTEP_DE,
                   RTOL_AS, ATOL_AS, MAXSTEP_AS,
                   msgs);
    }

    /*
     * overload with default algebra solver controls
     */
    template <typename T0, typename T1, typename T2, typename T3, typename T4,
              typename T5, typename T6, typename F>
    static Eigen::Matrix<typename  EventsManager<NONMENEventsRecord<T0, T1, T2, T3>, NonEventParameters<T0, T4, std::vector, std::tuple<T5, T6> > >::T_scalar, // NOLINT
                         Eigen::Dynamic, Eigen::Dynamic>
    solve(const F& f,
          const int nCmt,
          const std::vector<int>& len,
          TORSTEN_PMX_FUNC_EVENTS_ARGS,
          const std::vector<std::vector<T4> >& pMatrix,
          const std::vector<std::vector<T5> >& biovar,
          const std::vector<std::vector<T6> >& tlag,
          double rel_tol,
          double abs_tol,
          long int max_num_steps,
          std::ostream* msgs) {
      return solve(f, nCmt, len, time, amt, rate,
                   ii, evid, cmt, addl, ss,
                   pMatrix, biovar, tlag,
                   rel_tol, abs_tol, max_num_steps,
                   RTOL_AS, ATOL_AS, MAXSTEP_AS,
                   msgs);
    }

    /*
     * overload for omitting tlag, with all the solver controls
     */
    template <typename T0, typename T1, typename T2, typename T3, typename T4,
              typename T5, typename F>
    static Eigen::Matrix<typename  EventsManager<NONMENEventsRecord<T0, T1, T2, T3>, NonEventParameters<T0, T4, std::vector, std::tuple<T5> > >::T_scalar, // NOLINT
                         Eigen::Dynamic, Eigen::Dynamic>
    solve(const F& f,
          const int nCmt,
          const std::vector<int>& len,
          TORSTEN_PMX_FUNC_EVENTS_ARGS,
          const std::vector<std::vector<T4> >& pMatrix,
          const std::vector<std::vector<T5> >& biovar,
          double rel_tol,
          double abs_tol,
          long int max_num_steps,
          double as_rel_tol,
          double as_abs_tol,
          long int as_max_num_steps,
          std::ostream* msgs) {
      static const char* caller("PMX SOLVE GROUP ODE");
      torsten::pmx_population_check(len, time, amt, rate, ii, evid, cmt, addl, ss, pMatrix, caller);
      torsten::pmx_population_check(len, time, biovar, caller);

      using ER = NONMENEventsRecord<T0, T1, T2, T3>;
      using EM = EventsManager<ER, NonEventParameters<T0, T4, std::vector, std::tuple<T5> >>;
      ER events_rec(nCmt, len, time, amt, rate, ii, evid, cmt, addl, ss);

      using model_type = torsten::PKODEModel<typename EM::T_par, F>;
      integrator_type integrator(rel_tol, abs_tol, max_num_steps, as_rel_tol, as_abs_tol, as_max_num_steps, msgs);
      EventSolver<model_type, EM> pr;

      Eigen::Matrix<typename EM::T_scalar, -1, -1> pred(nCmt, events_rec.total_num_event_times);

      pr.pred(events_rec, pred, integrator, pMatrix, biovar, nCmt, f);

      return pred;
    }

    /*
     * overload for omitting tlag, with default solver controls
     */
    template <typename T0, typename T1, typename T2, typename T3, typename T4,
              typename T5, typename F>
    static Eigen::Matrix<typename  EventsManager<NONMENEventsRecord<T0, T1, T2, T3>, NonEventParameters<T0, T4, std::vector, std::tuple<T5> > >::T_scalar, // NOLINT
                         Eigen::Dynamic, Eigen::Dynamic>
    solve(const F& f,
          const int nCmt,
          const std::vector<int>& len,
          TORSTEN_PMX_FUNC_EVENTS_ARGS,
          const std::vector<std::vector<T4> >& pMatrix,
          const std::vector<std::vector<T5> >& biovar,
          std::ostream* msgs) {
      return solve(f, nCmt, len, time, amt, rate,
                   ii, evid, cmt, addl, ss,
                   pMatrix, biovar,
                   RTOL_DE, ATOL_DE, MAXSTEP_DE,
                   RTOL_AS, ATOL_AS, MAXSTEP_AS,
                   msgs);
    }

    /*
     * overload for omitting tlag, with default algebra
     * solver controls and user-defined ode solver controls
     */
    template <typename T0, typename T1, typename T2, typename T3, typename T4,
              typename T5, typename F>
    static Eigen::Matrix<typename  EventsManager<NONMENEventsRecord<T0, T1, T2, T3>, NonEventParameters<T0, T4, std::vector, std::tuple<T5> > >::T_scalar, // NOLINT
                         Eigen::Dynamic, Eigen::Dynamic>
    solve(const F& f,
          const int nCmt,
          const std::vector<int>& len,
          TORSTEN_PMX_FUNC_EVENTS_ARGS,
          const std::vector<std::vector<T4> >& pMatrix,
          const std::vector<std::vector<T5> >& biovar,
          double rel_tol,
          double abs_tol,
          long int max_num_steps,
          std::ostream* msgs) {
      return solve(f, nCmt, len, time, amt, rate,
                   ii, evid, cmt, addl, ss,
                   pMatrix, biovar,
                   rel_tol, abs_tol, max_num_steps,
                   RTOL_AS, ATOL_AS, MAXSTEP_AS,
                   msgs);
    }

    /*
     * overload for omitting biuovar/tlag, with all the solver controls
     */
    template <typename T0, typename T1, typename T2, typename T3, typename T4, typename F>
    static Eigen::Matrix<typename  EventsManager<NONMENEventsRecord<T0, T1, T2, T3>, NonEventParameters<T0, T4, std::vector, std::tuple<> > >::T_scalar, // NOLINT
                         Eigen::Dynamic, Eigen::Dynamic>
    solve(const F& f,
          const int nCmt,
          const std::vector<int>& len,
          TORSTEN_PMX_FUNC_EVENTS_ARGS,
          const std::vector<std::vector<T4> >& pMatrix,
          double rel_tol,
          double abs_tol,
          long int max_num_steps,
          double as_rel_tol,
          double as_abs_tol,
          long int as_max_num_steps,
          std::ostream* msgs) {
      static const char* caller("PMX SOLVE GROUP ODE");
      torsten::pmx_population_check(len, time, amt, rate, ii, evid, cmt, addl, ss, pMatrix, caller);

      using ER = NONMENEventsRecord<T0, T1, T2, T3>;
      using EM = EventsManager<ER, NonEventParameters<T0, T4, std::vector, std::tuple<> >>;
      ER events_rec(nCmt, len, time, amt, rate, ii, evid, cmt, addl, ss);

      using model_type = torsten::PKODEModel<typename EM::T_par, F>;
      integrator_type integrator(rel_tol, abs_tol, max_num_steps, as_rel_tol, as_abs_tol, as_max_num_steps, msgs);
      EventSolver<model_type, EM> pr;

      Eigen::Matrix<typename EM::T_scalar, -1, -1> pred(nCmt, events_rec.total_num_event_times);

      pr.pred(events_rec, pred, integrator, pMatrix, nCmt, f);

      return pred;
    }

    /*
     * overload for omitting biovar/tlag, with default solver controls
     */
    template <typename T0, typename T1, typename T2, typename T3, typename T4, typename F>
    static Eigen::Matrix<typename  EventsManager<NONMENEventsRecord<T0, T1, T2, T3>, NonEventParameters<T0, T4, std::vector, std::tuple<> > >::T_scalar, // NOLINT
                         Eigen::Dynamic, Eigen::Dynamic>
    solve(const F& f,
          const int nCmt,
          const std::vector<int>& len,
          TORSTEN_PMX_FUNC_EVENTS_ARGS,
          const std::vector<std::vector<T4> >& pMatrix,
          std::ostream* msgs) {
      return solve(f, nCmt, len, time, amt, rate,
                   ii, evid, cmt, addl, ss,
                   pMatrix,
                   RTOL_DE, ATOL_DE, MAXSTEP_DE,
                   RTOL_AS, ATOL_AS, MAXSTEP_AS,
                   msgs);
    }

    /*
     * overload for omitting tlag, with default algebra
     * solver controls and user-defined ode solver controls
     */
    template <typename T0, typename T1, typename T2, typename T3, typename T4, typename F>
    static Eigen::Matrix<typename  EventsManager<NONMENEventsRecord<T0, T1, T2, T3>, NonEventParameters<T0, T4, std::vector, std::tuple<> > >::T_scalar, // NOLINT
                         Eigen::Dynamic, Eigen::Dynamic>
    solve(const F& f,
          const int nCmt,
          const std::vector<int>& len,
          TORSTEN_PMX_FUNC_EVENTS_ARGS,
          const std::vector<std::vector<T4> >& pMatrix,
          double rel_tol,
          double abs_tol,
          long int max_num_steps,
          std::ostream* msgs) {
      return solve(f, nCmt, len, time, amt, rate,
                   ii, evid, cmt, addl, ss,
                   pMatrix,
                   rel_tol, abs_tol, max_num_steps,
                   RTOL_AS, ATOL_AS, MAXSTEP_AS,
                   msgs);
    }

    /*
     * For population models, more often we use ragged arrays
     * to describe the entire population, so in addition we need the arrays of
     * the length of each individual's data. The size of that
     * vector is the size of the population.
     * Overload with support of real data.
     */
    template <typename T0, typename T1, typename T2, typename T3, typename T4,
              typename T5, typename T6, typename F>
    static Eigen::Matrix<typename  EventsManager<NONMENEventsRecord<T0, T1, T2, T3>, NonEventParameters<T0, T4, std::vector, std::tuple<T5, T6> > >::T_scalar, // NOLINT
                         Eigen::Dynamic, Eigen::Dynamic>
    solve(const F& f,
          const int nCmt,
          const std::vector<int>& len,
          TORSTEN_PMX_FUNC_EVENTS_ARGS,
          const std::vector<std::vector<T4> >& pMatrix,
          const std::vector<std::vector<T5> >& biovar,
          const std::vector<std::vector<T6> >& tlag,
          const std::vector<std::vector<double> >& x_r,
          double rel_tol,
          double abs_tol,
          long int max_num_steps,
          double as_rel_tol,
          double as_abs_tol,
          long int as_max_num_steps,
          std::ostream* msgs) {
      static const char* caller("PMX SOLVE GROUP ODE");
      torsten::pmx_population_check(len, time, amt, rate, ii, evid, cmt, addl, ss, pMatrix, caller);
      torsten::pmx_population_check(len, time, biovar, tlag, caller);

      using ER = NONMENEventsRecord<T0, T1, T2, T3>;
      using EM = EventsManager<ER, NonEventParameters<T0, T4, std::vector, std::tuple<T5, T6>, double>>;
      ER events_rec(nCmt, len, time, amt, rate, ii, evid, cmt, addl, ss);

      using model_type = torsten::PKODEModel<typename EM::T_par, F>;
      integrator_type integrator(rel_tol, abs_tol, max_num_steps, as_rel_tol, as_abs_tol, as_max_num_steps, msgs);
      EventSolver<model_type, EM> pr;

      Eigen::Matrix<typename EM::T_scalar, -1, -1> pred(nCmt, events_rec.total_num_event_times);

      pr.pred(events_rec, pred, integrator, pMatrix, biovar, tlag, x_r, nCmt, f);

      return pred;
    }

    /*
     * overload with default ode & algebra solver controls, with support of real data
     */
    template <typename T0, typename T1, typename T2, typename T3, typename T4,
              typename T5, typename T6, typename F>
    static Eigen::Matrix<typename  EventsManager<NONMENEventsRecord<T0, T1, T2, T3>, NonEventParameters<T0, T4, std::vector, std::tuple<T5, T6> > >::T_scalar, // NOLINT
                         Eigen::Dynamic, Eigen::Dynamic>
    solve(const F& f,
          const int nCmt,
          const std::vector<int>& len,
          TORSTEN_PMX_FUNC_EVENTS_ARGS,
          const std::vector<std::vector<T4> >& pMatrix,
          const std::vector<std::vector<T5> >& biovar,
          const std::vector<std::vector<T6> >& tlag,
          const std::vector<std::vector<double> >& x_r,
          std::ostream* msgs) {
      return solve(f, nCmt, len, time, amt, rate,
                   ii, evid, cmt, addl, ss,
                   pMatrix, biovar, tlag, x_r,
                   RTOL_DE, ATOL_DE, MAXSTEP_DE,
                   RTOL_AS, ATOL_AS, MAXSTEP_AS,
                   msgs);
    }

    /*
     * overload with default algebra solver controls, with support of real data
     */
    template <typename T0, typename T1, typename T2, typename T3, typename T4,
              typename T5, typename T6, typename F>
    static Eigen::Matrix<typename  EventsManager<NONMENEventsRecord<T0, T1, T2, T3>, NonEventParameters<T0, T4, std::vector, std::tuple<T5, T6> > >::T_scalar, // NOLINT
                         Eigen::Dynamic, Eigen::Dynamic>
    solve(const F& f,
          const int nCmt,
          const std::vector<int>& len,
          TORSTEN_PMX_FUNC_EVENTS_ARGS,
          const std::vector<std::vector<T4> >& pMatrix,
          const std::vector<std::vector<T5> >& biovar,
          const std::vector<std::vector<T6> >& tlag,
          const std::vector<std::vector<double> >& x_r,
          double rel_tol,
          double abs_tol,
          long int max_num_steps,
          std::ostream* msgs) {
      return solve(f, nCmt, len, time, amt, rate,
                   ii, evid, cmt, addl, ss,
                   pMatrix, biovar, tlag, x_r,
                   rel_tol, abs_tol, max_num_steps,
                   RTOL_AS, ATOL_AS, MAXSTEP_AS,
                   msgs);
    }

    /*
     * For population models, more often we use ragged arrays
     * to describe the entire population, so in addition we need the arrays of
     * the length of each individual's data. The size of that
     * vector is the size of the population.
     * Overload with support of real & integer data.
     */
    template <typename T0, typename T1, typename T2, typename T3, typename T4,
              typename T5, typename T6, typename F>
    static Eigen::Matrix<typename  EventsManager<NONMENEventsRecord<T0, T1, T2, T3>, NonEventParameters<T0, T4, std::vector, std::tuple<T5, T6> > >::T_scalar, // NOLINT
                         Eigen::Dynamic, Eigen::Dynamic>
    solve(const F& f,
          const int nCmt,
          const std::vector<int>& len,
          TORSTEN_PMX_FUNC_EVENTS_ARGS,
          const std::vector<std::vector<T4> >& pMatrix,
          const std::vector<std::vector<T5> >& biovar,
          const std::vector<std::vector<T6> >& tlag,
          const std::vector<std::vector<double> >& x_r,
          const std::vector<std::vector<int> >& x_i,
          double rel_tol,
          double abs_tol,
          long int max_num_steps,
          double as_rel_tol,
          double as_abs_tol,
          long int as_max_num_steps,
          std::ostream* msgs) {
      static const char* caller("PMX SOLVE GROUP ODE");
      torsten::pmx_population_check(len, time, amt, rate, ii, evid, cmt, addl, ss, pMatrix, caller);
      torsten::pmx_population_check(len, time, biovar, tlag, caller);

      using ER = NONMENEventsRecord<T0, T1, T2, T3>;
      using EM = EventsManager<ER, NonEventParameters<T0, T4, std::vector, std::tuple<T5, T6>, double, int>>;
      ER events_rec(nCmt, len, time, amt, rate, ii, evid, cmt, addl, ss);

      using model_type = torsten::PKODEModel<typename EM::T_par, F>;
      integrator_type integrator(rel_tol, abs_tol, max_num_steps, as_rel_tol, as_abs_tol, as_max_num_steps, msgs);
      EventSolver<model_type, EM> pr;

      Eigen::Matrix<typename EM::T_scalar, -1, -1> pred(nCmt, events_rec.total_num_event_times);

      pr.pred(events_rec, pred, integrator, pMatrix, biovar, tlag, x_r, x_i, nCmt, f);

      return pred;
    }

    /*
     * overload with default ode & algebra solver controls, with support of real & integer data
     */
    template <typename T0, typename T1, typename T2, typename T3, typename T4,
              typename T5, typename T6, typename F>
    static Eigen::Matrix<typename  EventsManager<NONMENEventsRecord<T0, T1, T2, T3>, NonEventParameters<T0, T4, std::vector, std::tuple<T5, T6> > >::T_scalar, // NOLINT
                         Eigen::Dynamic, Eigen::Dynamic>
    solve(const F& f,
          const int nCmt,
          const std::vector<int>& len,
          TORSTEN_PMX_FUNC_EVENTS_ARGS,
          const std::vector<std::vector<T4> >& pMatrix,
          const std::vector<std::vector<T5> >& biovar,
          const std::vector<std::vector<T6> >& tlag,
          const std::vector<std::vector<double> >& x_r,
          const std::vector<std::vector<int> >& x_i,
          std::ostream* msgs) {
      return solve(f, nCmt, len, time, amt, rate,
                   ii, evid, cmt, addl, ss,
                   pMatrix, biovar, tlag, x_r, x_i,
                   RTOL_DE, ATOL_DE, MAXSTEP_DE,
                   RTOL_AS, ATOL_AS, MAXSTEP_AS,
                   msgs);
    }

    /*
     * overload with default algebra solver controls, with support of real & integer data
     */
    template <typename T0, typename T1, typename T2, typename T3, typename T4,
              typename T5, typename T6, typename F>
    static Eigen::Matrix<typename  EventsManager<NONMENEventsRecord<T0, T1, T2, T3>, NonEventParameters<T0, T4, std::vector, std::tuple<T5, T6> > >::T_scalar, // NOLINT
                         Eigen::Dynamic, Eigen::Dynamic>
    solve(const F& f,
          const int nCmt,
          const std::vector<int>& len,
          TORSTEN_PMX_FUNC_EVENTS_ARGS,
          const std::vector<std::vector<T4> >& pMatrix,
          const std::vector<std::vector<T5> >& biovar,
          const std::vector<std::vector<T6> >& tlag,
          const std::vector<std::vector<double> >& x_r,
          const std::vector<std::vector<int> >& x_i,
          double rel_tol,
          double abs_tol,
          long int max_num_steps,
          std::ostream* msgs) {
      return solve(f, nCmt, len, time, amt, rate,
                   ii, evid, cmt, addl, ss,
                   pMatrix, biovar, tlag, x_r, x_i,
                   rel_tol, abs_tol, max_num_steps,
                   RTOL_AS, ATOL_AS, MAXSTEP_AS,
                   msgs);
    }
  };

}
#endif


//---------------------------------------------------------------------------//
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
//
// 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
//
// See http://boostorg.github.com/compute for more information.
//---------------------------------------------------------------------------//

#ifndef BOOST_COMPUTE_RANDOM_MERSENNE_TWISTER_ENGINE_HPP
#define BOOST_COMPUTE_RANDOM_MERSENNE_TWISTER_ENGINE_HPP

#include <algorithm>

#include <boost/compute/types.hpp>
#include <boost/compute/buffer.hpp>
#include <boost/compute/kernel.hpp>
#include <boost/compute/context.hpp>
#include <boost/compute/program.hpp>
#include <boost/compute/command_queue.hpp>
#include <boost/compute/algorithm/transform.hpp>
#include <boost/compute/container/vector.hpp>
#include <boost/compute/detail/iterator_range_size.hpp>
#include <boost/compute/iterator/discard_iterator.hpp>
#include <boost/compute/utility/program_cache.hpp>

namespace boost {
namespace compute {

/// \class mersenne_twister_engine
/// \brief Mersenne twister pseudorandom number generator.
template<class T>
class mersenne_twister_engine
{
public:
    typedef T result_type;
    static const T default_seed = 5489U;
    static const T n = 624;
    static const T m = 397;

    /// Creates a new mersenne_twister_engine and seeds it with \p value.
    explicit mersenne_twister_engine(command_queue &queue,
                                     result_type value = default_seed)
        : m_context(queue.get_context()),
          m_state_buffer(m_context, n * sizeof(result_type))
    {
        // setup program
        load_program();

        // seed state
        seed(value, queue);
    }

    /// Creates a new mersenne_twister_engine object as a copy of \p other.
    mersenne_twister_engine(const mersenne_twister_engine<T> &other)
        : m_context(other.m_context),
          m_state_index(other.m_state_index),
          m_program(other.m_program),
          m_state_buffer(other.m_state_buffer)
    {
    }

    /// Copies \p other to \c *this.
    mersenne_twister_engine<T>& operator=(const mersenne_twister_engine<T> &other)
    {
        if(this != &other){
            m_context = other.m_context;
            m_state_index = other.m_state_index;
            m_program = other.m_program;
            m_state_buffer = other.m_state_buffer;
        }

        return *this;
    }

    /// Destroys the mersenne_twister_engine object.
    ~mersenne_twister_engine()
    {
    }

    /// Seeds the random number generator with \p value.
    ///
    /// \param value seed value for the random-number generator
    /// \param queue command queue to perform the operation
    ///
    /// If no seed value is provided, \c default_seed is used.
    void seed(result_type value, command_queue &queue)
    {
        kernel seed_kernel = m_program.create_kernel("seed");
        seed_kernel.set_arg(0, value);
        seed_kernel.set_arg(1, m_state_buffer);

        queue.enqueue_task(seed_kernel);

        m_state_index = 0;
    }

    /// \overload
    void seed(command_queue &queue)
    {
        seed(default_seed, queue);
    }

    /// Generates random numbers and stores them to the range [\p first, \p last).
    template<class OutputIterator>
    void generate(OutputIterator first, OutputIterator last, command_queue &queue)
    {
        const size_t size = detail::iterator_range_size(first, last);

        kernel fill_kernel(m_program, "fill");
        fill_kernel.set_arg(0, m_state_buffer);
        fill_kernel.set_arg(2, first.get_buffer());

        size_t offset = 0;
        size_t &p = m_state_index;

        for(;;){
            size_t count = 0;
            if(size > n){
                count = (std::min)(static_cast<size_t>(n), size - offset);
            }
            else {
                count = size;
            }
            fill_kernel.set_arg(1, static_cast<const uint_>(p));
            fill_kernel.set_arg(3, static_cast<const uint_>(offset));
            queue.enqueue_1d_range_kernel(fill_kernel, 0, count, 0);

            p += count;
            offset += count;

            if(offset >= size){
                break;
            }

            generate_state(queue);
            p = 0;
        }
    }

    /// \internal_
    void generate(discard_iterator first, discard_iterator last, command_queue &queue)
    {
        (void) queue;

        m_state_index += std::distance(first, last);
    }

    /// Generates random numbers, transforms them with \p op, and then stores
    /// them to the range [\p first, \p last).
    template<class OutputIterator, class Function>
    void generate(OutputIterator first, OutputIterator last, Function op, command_queue &queue)
    {
        vector<T> tmp(std::distance(first, last), queue.get_context());
        generate(tmp.begin(), tmp.end(), queue);
        transform(tmp.begin(), tmp.end(), first, op, queue);
    }

    /// Generates \p z random numbers and discards them.
    void discard(size_t z, command_queue &queue)
    {
        generate(discard_iterator(0), discard_iterator(z), queue);
    }

    /// \internal_ (deprecated)
    template<class OutputIterator>
    void fill(OutputIterator first, OutputIterator last, command_queue &queue)
    {
        generate(first, last, queue);
    }

private:
    /// \internal_
    void generate_state(command_queue &queue)
    {
        kernel generate_state_kernel =
            m_program.create_kernel("generate_state");
        generate_state_kernel.set_arg(0, m_state_buffer);
        queue.enqueue_task(generate_state_kernel);
    }

    /// \internal_
    void load_program()
    {
        boost::shared_ptr<program_cache> cache =
            program_cache::get_global_cache(m_context);

        std::string cache_key =
            std::string("__boost_mersenne_twister_engine_") + type_name<T>();

        const char source[] =
            "static uint twiddle(uint u, uint v)\n"
            "{\n"
            "    return (((u & 0x80000000U) | (v & 0x7FFFFFFFU)) >> 1) ^\n"
            "           ((v & 1U) ? 0x9908B0DFU : 0x0U);\n"
            "}\n"

            "__kernel void generate_state(__global uint *state)\n"
            "{\n"
            "    const uint n = 624;\n"
            "    const uint m = 397;\n"
            "    for(uint i = 0; i < (n - m); i++)\n"
            "        state[i] = state[i+m] ^ twiddle(state[i], state[i+1]);\n"
            "    for(uint i = n - m; i < (n - 1); i++)\n"
            "        state[i] = state[i+m-n] ^ twiddle(state[i], state[i+1]);\n"
            "    state[n-1] = state[m-1] ^ twiddle(state[n-1], state[0]);\n"
            "}\n"

            "__kernel void seed(const uint s, __global uint *state)\n"
            "{\n"
            "    const uint n = 624;\n"
            "    state[0] = s & 0xFFFFFFFFU;\n"
            "    for(uint i = 1; i < n; i++){\n"
            "        state[i] = 1812433253U * (state[i-1] ^ (state[i-1] >> 30)) + i;\n"
            "        state[i] &= 0xFFFFFFFFU;\n"
            "    }\n"
            "    generate_state(state);\n"
            "}\n"

            "static uint random_number(__global uint *state, const uint p)\n"
            "{\n"
            "    uint x = state[p];\n"
            "    x ^= (x >> 11);\n"
            "    x ^= (x << 7) & 0x9D2C5680U;\n"
            "    x ^= (x << 15) & 0xEFC60000U;\n"
            "    return x ^ (x >> 18);\n"
            "}\n"

            "__kernel void fill(__global uint *state,\n"
            "                   const uint state_index,\n"
            "                   __global uint *vector,\n"
            "                   const uint offset)\n"
            "{\n"
            "    const uint i = get_global_id(0);\n"
            "    vector[offset+i] = random_number(state, state_index + i);\n"
            "}\n";

        m_program = cache->get_or_build(cache_key, std::string(), source, m_context);
    }

private:
    context m_context;
    size_t m_state_index;
    program m_program;
    buffer m_state_buffer;
};

typedef mersenne_twister_engine<uint_> mt19937;

} // end compute namespace
} // end boost namespace

#endif // BOOST_COMPUTE_RANDOM_MERSENNE_TWISTER_ENGINE_HPP


#ifndef OSRM_OPENING_HOURS_HPP
#define OSRM_OPENING_HOURS_HPP

#include <boost/date_time/gregorian/gregorian.hpp>

#include <string>
#include <vector>

namespace osrm
{
namespace util
{

// Helper classes for "opening hours" format http://wiki.openstreetmap.org/wiki/Key:opening_hours
// Grammar https://wiki.openstreetmap.org/wiki/Key:opening_hours/specification
// Supported simplified features in CheckOpeningHours:
// - Year/Month/Day ranges
// - Weekday ranges
// - Time ranges
// Not supported:
// - Week numbers
// - Holidays, events, variables dates
// - Day offsets and periodic ranges
struct OpeningHours
{
    enum Modifier
    {
        unknown,
        open,
        closed,
        off,
        is24_7
    };

    struct Time
    {
        enum Event : unsigned char
        {
            invalid,
            none,
            dawn,
            sunrise,
            sunset,
            dusk
        };

        Event event;
        std::int32_t minutes;

        Time() : event(invalid), minutes(0) {}
        Time(Event event) : event(event), minutes(0) {}
        Time(char hour, char min) : event(none), minutes(hour * 60 + min) {}
        Time(Event event, bool positive, const Time &offset)
            : event(event), minutes(positive ? offset.minutes : -offset.minutes)
        {
        }
    };

    struct TimeSpan
    {
        Time from, to;
        TimeSpan() = default;
        TimeSpan(const Time &from_, const Time &to_) : from(from_), to(to_)
        {
            if (to.minutes < from.minutes)
                to.minutes += 24 * 60;
        }

        bool IsInRange(const struct tm &time, bool &use_curr_day, bool &use_next_day) const
        {
            // TODO: events are not handled
            if (from.event != OpeningHours::Time::none || to.event != OpeningHours::Time::none)
                return false;

            const auto minutes = time.tm_hour * 60 + time.tm_min;
            if (to.minutes > 24 * 60)
            {
                use_curr_day = (from.minutes <= minutes); // in range [from, 24:00) current day
                use_next_day = (minutes < to.minutes - 24 * 60); // in range [00:00, to) next day
            }
            else
            {
                use_curr_day =
                    (from.minutes <= minutes && minutes < to.minutes); // in range [from, to)
                use_next_day = false;                                  // do not use the next day
            }

            return use_curr_day || use_next_day;
        }
    };

    struct WeekdayRange
    {
        int weekdays, overnight_weekdays;
        WeekdayRange() = default;
        WeekdayRange(unsigned char from, unsigned char to)
        {
            // weekdays mask for [from, to], e.g [2, 5] -> 0111100, [5, 2] -> 1100111,
            //  [3, 3] -> 0001000, [0,6] -> 1111111, [6,0] -> 1000001, [4, 3] -> 1111111
            weekdays = (from <= to) ? ((1 << (to - from + 1)) - 1) << from
                                    : ~(((1 << (from - to - 1)) - 1) << (to + 1));
            weekdays &= 0x7f;
            overnight_weekdays = (weekdays << 1) | (weekdays & 0x40 ? 1 : 0);
        }

        bool IsInRange(const struct tm &time, bool use_curr_day, bool use_next_day) const
        {
            return (use_curr_day && weekdays & (1 << time.tm_wday)) ||
                   (use_next_day && overnight_weekdays & (1 << time.tm_wday));
        }
    };

    struct Monthday
    {
        int year;
        char month;
        char day;
        Monthday() = default;
        Monthday(int year) : year(year), month(0), day(0) {}
        Monthday(int year, char month, char day) : year(year), month(month), day(day) {}

        bool IsValid() const { return year > 0 || month != 0 || day != 0; }
        bool operator==(const Monthday &rhs) const
        {
            return std::tie(year, month, day) == std::tie(rhs.year, rhs.month, rhs.day);
        }
    };

    struct MonthdayRange
    {
        Monthday from, to;
        MonthdayRange() : from(0, 0, 0), to(0, 0, 0) {}
        MonthdayRange(const Monthday &from, const Monthday &to) : from(from), to(to) {}

        bool IsInRange(const struct tm &time, bool use_curr_day, bool use_next_day) const
        {
            using boost::gregorian::date;
            using boost::gregorian::date_duration;

            const auto year = time.tm_year + 1900;
            const auto month = time.tm_mon + 1;

            date date_current(year, month, time.tm_mday);
            date date_from(boost::gregorian::min_date_time);
            date date_to(boost::gregorian::max_date_time);

            if (from.IsValid())
            {
                date_from = (from.day == 0) ? date(from.year == 0 ? year : from.year,
                                                   from.month == 0 ? month : from.month,
                                                   1)
                                            : date(from.year == 0 ? year : from.year,
                                                   from.month == 0 ? month : from.month,
                                                   from.day);
            }
            if (to.IsValid())
            {
                date_to = date(to.year == 0 ? (from.year == 0 ? year : from.year) : to.year,
                               to.month == 0 ? (from.month == 0 ? month : from.month) : to.month,
                               1);
                date_to = (to.day == 0) ? date_to.end_of_month()
                                        : date(date_to.year(), date_to.month(), to.day);
            }
            else if (to == Monthday())
            {
                date_to = date_from;
            }

            const bool inverse = (from.year == 0) && (to.year == 0) && (date_from > date_to);
            if (inverse)
            {
                std::swap(date_from, date_to);
            }

            if (!use_curr_day)
                date_from += date_duration(1);
            if (use_next_day && date_to != date(boost::gregorian::max_date_time))
                date_to += date_duration(1);

            return (date_from <= date_current && date_current <= date_to) ^ inverse;
        }
    };

    OpeningHours() : modifier(open) {}

    bool IsInRange(const struct tm &time) const
    {
        bool use_curr_day = true;  // the first matching time uses the current day
        bool use_next_day = false; // the first matching time uses the next day
        return (!times.empty() || !weekdays.empty() || !monthdays.empty())
               // the value is in range if time is not specified or is in any time range
               // (also modifies use_curr_day and use_next_day flags to handle overnight day ranges,
               // e.g. for 22:00-03:00 and 2am -> use_curr_day = false and use_next_day = true)
               && (times.empty() ||
                   std::any_of(times.begin(),
                               times.end(),
                               [&time, &use_curr_day, &use_next_day](const auto &x) {
                                   return x.IsInRange(time, use_curr_day, use_next_day);
                               }))
               // .. and if weekdays are not specified or matches weekdays range
               && (weekdays.empty() ||
                   std::any_of(weekdays.begin(),
                               weekdays.end(),
                               [&time, use_curr_day, use_next_day](const auto &x) {
                                   return x.IsInRange(time, use_curr_day, use_next_day);
                               }))
               // .. and if month-day ranges are not specified or is in any month-day range
               && (monthdays.empty() ||
                   std::any_of(monthdays.begin(),
                               monthdays.end(),
                               [&time, use_curr_day, use_next_day](const auto &x) {
                                   return x.IsInRange(time, use_curr_day, use_next_day);
                               }));
    }

    std::vector<TimeSpan> times;
    std::vector<WeekdayRange> weekdays;
    std::vector<MonthdayRange> monthdays;
    Modifier modifier;
};

std::vector<OpeningHours> ParseOpeningHours(const std::string &str);

bool CheckOpeningHours(const std::vector<OpeningHours> &input, const struct tm &time);

} // namespace util
} // namespace osrm

#endif // OSRM_OPENING_HOURS_HPP


// __BEGIN_LICENSE__
// Copyright (C) 2006-2011 United States Government as represented by
// the Administrator of the National Aeronautics and Space Administration.
// All Rights Reserved.
// __END_LICENSE__

/// This application is for use of discovering which cameras contain
/// most of the error after a bundle adjustment. The information
/// produced by this application can help narrow down where better
/// measurements are required.

#include <vw/Core/ProgressCallback.h>
#include <vw/BundleAdjustment/ControlNetwork.h>
#include <asp/Core/Macros.h>
#include <asp/Core/Common.h>
#include <asp/IsisIO/IsisAdjustCameraModel.h>

#include <boost/algorithm/string.hpp>
#include <boost/program_options.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/foreach.hpp>

using namespace vw;
namespace po = boost::program_options;
namespace fs = boost::filesystem;

struct Options : public asp::BaseOptions {
  std::string cnet_file;
  std::vector<std::string> input_names;
  bool find_median;
};

void handle_arguments( int argc, char *argv[], Options& opt ) {
  po::options_description general_options("");
  general_options.add_options()
    ("find-median", po::bool_switch(&opt.find_median)->default_value(false),
     "Find median when reporting high error cameras.");
  general_options.add( asp::BaseOptionsDescription(opt) );

  po::options_description positional("");
  positional.add_options()
    ("cnet-file", po::value(&opt.cnet_file))
    ("input-files", po::value(&opt.input_names));

  po::positional_options_description positional_desc;
  positional_desc.add("cnet-file", 1);
  positional_desc.add("input-files", -1);

  std::string usage("<cnet_file> <isis cube files> ...");
  po::variables_map vm =
    asp::check_command_line( argc, argv, opt, general_options,
                             positional, positional_desc, usage );

  if ( opt.input_names.empty() )
    vw_throw( ArgumentErr() << "Missing input cube files!\n"
              << usage << general_options );
  if ( opt.cnet_file.empty() )
    vw_throw( ArgumentErr() << "Missing input control network!\n"
              << usage << general_options );
}

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

  Options opt;
  try {
    handle_arguments( argc, argv, opt );

    // Loading the image data into the camera models. Also applying
    // blank equations to define the cameras
    std::vector< camera::IsisAdjustCameraModel > camera_models;
    std::map<std::string,size_t> serial_to_camera_model;
    std::map<size_t,std::string> id_to_filename;
    {
      vw_out() << "Loading Camera Models:\n";
      vw_out() << "----------------------\n";
      TerminalProgressCallback progress("asp","Camera Models:");
      progress.report_progress(0);
      double tpc_inc = 1/double(opt.input_names.size());
      BOOST_FOREACH( std::string const& input, opt.input_names ) {
        progress.report_incremental_progress( tpc_inc );
        vw_out(DebugMessage,"asp") << "Loading: " << input << "\n";

        std::string adjust_file =
          fs::path( input ).replace_extension("isis_adjust").string();

        typedef boost::shared_ptr<asp::BaseEquation> shared_eq;
        shared_eq posF, poseF;
        if ( fs::exists( adjust_file ) ) {
          std::ifstream input( adjust_file.c_str() );
          posF = asp::read_equation(input);
          poseF = asp::read_equation(input);
          input.close();
        } else {
          posF = shared_eq( new asp::PolyEquation( 0 ) );
          poseF = shared_eq( new asp::PolyEquation( 0 ) );
        }
        camera::IsisAdjustCameraModel camera( input, posF, poseF );
        camera_models.push_back( camera );

        serial_to_camera_model[ camera_models.back().serial_number() ] =
          camera_models.size() - 1;
        id_to_filename[ camera_models.size() - 1 ] = input;
      }
      progress.report_finished();
    }

    ba::ControlNetwork cnet("");
    std::vector<std::string> tokens;
    boost::split( tokens, opt.cnet_file, boost::is_any_of(".") );
    if ( tokens[tokens.size()-1] == "net" ) {
      cnet.read_isis( opt.cnet_file );
    } else if ( tokens[tokens.size()-1] == "cnet" ) {
      cnet.read_binary( opt.cnet_file );
    } else {
      vw_throw( IOErr() << "Unknown Control Network file extension, \""
                << tokens[tokens.size()-1] << "\"." );
    }

    vw_out() << "Assigning Indexing:\n";
    BOOST_FOREACH( ba::ControlPoint& cp, cnet ) {
      BOOST_FOREACH( ba::ControlMeasure& cm, cp ) {
        std::map<std::string,size_t>::const_iterator id =
          serial_to_camera_model.find( cm.serial() );
        if ( id != serial_to_camera_model.end() )
          cm.set_image_id( id->second );
        else
          vw_throw( IOErr() << "Control Network has serial not associated with input cameras, \"" << cm.serial() << "\"." );
      }
    }

    vw_out() << "Iterating through cameras.\n";
    std::vector<double> average_error( camera_models.size() );
    std::vector<size_t> average_count( camera_models.size() );
    std::fill( average_error.begin(), average_error.end(), 0 );
    std::fill( average_count.begin(), average_count.end(), 0 );
    math::CDFAccumulator<double> cdf_accu;
    BOOST_FOREACH( ba::ControlPoint & cp, cnet ) {
      BOOST_FOREACH( ba::ControlMeasure & cm, cp ) {
        Vector2 reprojection =
          camera_models[cm.image_id()].point_to_pixel( cp.position() );
        average_count[cm.image_id()]++;
        double error = norm_2(reprojection-cm.position());
        average_error[cm.image_id()] += error;
        cdf_accu(error);
      }
    }
    cdf_accu.update();
    vw_out() << "CDF [" << cdf_accu.quantile(0.0) << " "
             << cdf_accu.quantile(0.25) << " " << cdf_accu.quantile(0.5)
             << " " << cdf_accu.quantile(0.75) << " "
             << cdf_accu.quantile(1.0) << "]\n";

    // Calculate average and accumulators
    MeanAccumulator<double> mean_acc;
    StdDevAccumulator<double> stddev_acc;
    double max_error = -1;
    size_t index_max_error = 0;
    std::list<size_t> cameras_not_connected;
    for ( size_t i = 0; i < average_error.size(); i++ ) {
      if ( average_count[i] == 0 ) {
        average_error[i] = 0;
        cameras_not_connected.push_back(i);
        continue;
      }
      average_error[i] /= double(average_count[i]);
      mean_acc( average_error[i] );
      stddev_acc( average_error[i] );
      if ( average_error[i] > max_error ) {
        max_error = average_error[i];
        index_max_error = i;
      }
    }

    double mean = mean_acc.value();
    double stddev = stddev_acc.value();
    std::cout << "Mean error  : " << mean << " px\n";
    std::cout << "StdDev error: " << stddev << " px\n";

    // Printing stats
    vw_out() << "\nCameras not connected:\n";
    vw_out() << "-------------------------------------\n";
    BOOST_FOREACH( size_t i, cameras_not_connected ) {
      vw_out() << id_to_filename[i] << "\n";
    }
    vw_out() << "\nCamera of largest error is:\n";
    vw_out() << "-------------------------------------\n";
    vw_out() << id_to_filename[index_max_error] << " ["
             << max_error << " px]\n";
    vw_out() << "\nCameras that are 1 std dev off:\n";
    vw_out() << "-------------------------------------\n";
    for ( size_t i = 0; i < average_error.size(); i++ ) {
      if ( average_error[i] - mean > stddev ) {
        if ( opt.find_median ) {
          MedianAccumulator<double> cmedian_acc;
          StdDevAccumulator<double> cstddev_acc;
          BOOST_FOREACH( ba::ControlPoint const& cp, cnet ) {
            BOOST_FOREACH( ba::ControlMeasure const& cm, cp ) {
              if ( cm.image_id() == i ) {
                Vector2 reprojection =
                  camera_models[cm.image_id()].point_to_pixel( cp.position() );
                double error = norm_2(reprojection-cm.position());
                cmedian_acc(error);
                cstddev_acc(error);
              }
            }
          }
          vw_out() << id_to_filename[i] << "[ m: " << average_error[i]
                   << " std: " << cstddev_acc.value()
                   << " md: " <<  cmedian_acc.value() << " px ]\n";
        } else {
          vw_out() << id_to_filename[i] << " ["
                   << average_error[i] << " px]\n";
        }
      }
    }

  } ASP_STANDARD_CATCHES;

  return 0;
}