#ifndef EXTENDEDCELLPROPERTY_HPP
#define EXTENDEDCELLPROPERTY_HPP

//general includes
#include <vector>
#include <string>
#include <math.h>
#include <boost/shared_ptr.hpp>

// chaste includes
#include "ChemicalCellProperty.hpp"

// base case assume spherical geometry

template<unsigned SPACE_DIM>
class ExtendedCellProperty : public ChemicalCellProperty
{
private:

    // inhertied:
    //StateVariableRegister* mpStateVariableRegister;
    //std::vector<double> mConcentrationVector;

    using ChemicalCellProperty::UpdateCellConcentrationVector;
    using ChemicalCellProperty::InitialiseCell;
protected:

    // base case; one value equal to the radius
    std::vector<double> mCellDimensions = {1.0};

    // vector of minimal dimensions before dimension is assumed to be null.
    std::vector<double> mMinimalDimensions = {1e-5};

    double mCellVolume =0.0;

    double mVolumePerFineMeshElement=0.0;

    bool mIsCellVolumeDefined = false;

    bool mIsMeshElementVolumeDefined = false;

    unsigned mTotalNumberMeshVoxels=0;

    unsigned mNumberMeshVoxelsInCell=0;

    unsigned mNumberMeshVoxelsOnBoundary=0;

    // is the cell permitted to grow and/or change in dimensions
    bool mIsCellDynamic = false;

    std::vector<double> mChangeInCellDimensions ={0.0};

    std::vector<double> mMeshDomainScale = std::vector<double>();

    // option switchs for interpolating into the bulk FeMesh
    bool mIncludeOdeInterpolationInCell=false;

    bool mAverageInternalCellStates=true;

    // member variables for the cell boundary voxels

    std::vector<std::vector<double>> mVectorOfExternalBoundaryStateVariables;

    std::vector<std::vector<double>> mVectorOfInternalBoundaryStateVariables;

    std::vector<ChastePoint<SPACE_DIM>> mVectorOfBoundaryLocations;

    // double in [0.0,1.0] relating the proportion of cell data avaliable for transport processes
    double mTransportConcentrationAvaliability = 1.0;

    // resultant cell boundary concentration vector; total contribution to the cell concentration for all the boundary voxels
    std::vector<double> mNextTimestepConcentrationVector;

public:

    ExtendedCellProperty();

    virtual ~ExtendedCellProperty();

    // virtual methods

    virtual void SetUpExtendedCell(StateVariableRegister*, std::vector<double>, std::vector<double>);

    virtual void SetUpExtendedCell(StateVariableRegister*, std::vector<double>, double radius =0.0);

    virtual void UpdateExtendedCell();

    virtual void InitialiseCell(StateVariableRegister*, std::vector<double>);

    virtual void CellGrowth();

    virtual bool IsPointInCell(const ChastePoint<SPACE_DIM>&, ChastePoint<SPACE_DIM>&);

    virtual bool IsPointOnCellBoundary(const ChastePoint<SPACE_DIM>&, ChastePoint<SPACE_DIM>&);

    virtual bool CheckCellBoundary(const ChastePoint<SPACE_DIM>&, ChastePoint<SPACE_DIM>&);

    virtual void CalculateVolumeOfCell();

    virtual void CalculateNumberOfCellMeshElements(ChastePoint<SPACE_DIM>);

    virtual void UpdateCellConcentrationVector(std::vector<double>&);

    virtual double TransportConcentrationAvaliabilityThisState(std::string state_name ="", unsigned state_index =0);

    // concrete methods

    void StepThroughDirectionalXBoundaryVoxels(ChastePoint<SPACE_DIM>&,ChastePoint<SPACE_DIM>&, unsigned);

    void StepThroughDirectionalXInternalVoxels(ChastePoint<SPACE_DIM>&,ChastePoint<SPACE_DIM>&, unsigned);

    void StepThroughXVoxels(ChastePoint<SPACE_DIM>&,ChastePoint<SPACE_DIM>&);

    void StepThroughDirectionalXYBoundaryVoxels(ChastePoint<SPACE_DIM>&,ChastePoint<SPACE_DIM>&, unsigned);

    void StepThroughDirectionalXYInternalVoxels(ChastePoint<SPACE_DIM>&,ChastePoint<SPACE_DIM>&, unsigned);

    void StepThroughXYVoxels(ChastePoint<SPACE_DIM>&,ChastePoint<SPACE_DIM>&);

    void StepThroughDirectionalXYZBoundaryVoxels(ChastePoint<SPACE_DIM>&,ChastePoint<SPACE_DIM>&, unsigned);

    void StepThroughDirectionalXYZInternalVoxels(ChastePoint<SPACE_DIM>&,ChastePoint<SPACE_DIM>&, unsigned);

    void StepThroughXYZVoxels(ChastePoint<SPACE_DIM>&,ChastePoint<SPACE_DIM>&);

    double RetrieveBoundarySourceByStateName(std::string, ChastePoint<SPACE_DIM>);

    double RetrieveInternalCellSourceByStateName(std::string);

    double RetrieveInternalCellSourceByStateName(std::string, ChastePoint<SPACE_DIM>);

    double RetrieveBoundaryCellSourceByStateNameAndLocation(std::string, ChastePoint<SPACE_DIM>);

    double RetrieveBoundaryInternalCellSourceByStateNameAndLocation(std::string, ChastePoint<SPACE_DIM>);

    std::vector<double>& RetrieveBoundaryCellSourceByLocation(ChastePoint<SPACE_DIM>);

    std::vector<double>& RetrieveInternalBoundaryCellSourceByLocation(ChastePoint<SPACE_DIM>);

    bool CheckChastePointsForEquality(ChastePoint<SPACE_DIM>, ChastePoint<SPACE_DIM>);

    void ResetNextTimestepConcentrationVector();

    void ResetNextTimestepConcentrationVector(unsigned);

    void ResetVectorOfBoundaryStateVariables();

    void ResetVectorOfBoundaryLocations();

    void ResetVectorOfInternalBoundaryStateVariables();

    void AddToVectorOfBoundaryStateVariables(std::vector<double>);

    void AddToVectorOfInternalBoundaryStateVariables(std::vector<double>);

    void AddToVectorOfBoundaryLocations(ChastePoint<SPACE_DIM>);

    void AppendInternalCellBoundaryConcentrations(std::vector<double>&, unsigned);

    void ReplaceBoundaryStateVariables(unsigned, std::vector<double>&);

    void RecordLocationAndStateVariable(ChastePoint<SPACE_DIM>, std::vector<double>);

    // Set methods

    void SetCellRadius(double);

    void SetCellDimensions(std::vector<double>);

    void SetMinimalCellDimensions(std::vector<double>);

    void SetCellVolume(double);

    void SetIsCellDynamic(bool);

    void SetMeshDomainScale(std::vector<double>);

    void SetTotalNumberMeshVoxels(unsigned);

    void SetNumberMeshVoxelsInCell(unsigned);

    void SetNumberMeshVoxelsOnBoundary(unsigned);

    void SetChangeInCellDimensions(std::vector<double>);

    void SetIncludeOdeInterpolationInCell(bool);

    void SetAverageInternalCellStates(bool);

    void SetVectorOfBoundaryStateVariables(std::vector<std::vector<double>>);

    void SetVectorOfBoundaryStateVariablesByIndex(unsigned,std::vector<double>);

    void SetVectorOfInternalBoundaryStateVariables(std::vector<std::vector<double>>);

    void SetVectorOfInternalBoundaryStateVariablesByIndex(unsigned,std::vector<double>);

    void SetVectorOfBoundaryLocations(std::vector<ChastePoint<SPACE_DIM>>);

    void SetVectorOfBoundaryLocationsByIndex(unsigned,ChastePoint<SPACE_DIM>);

    void SetNextTimestepConcentrationVector(std::vector<double>);

    // Get methods

    double GetCellRadius();

    std::vector<double> GetCellDimensions();

    std::vector<double> GetMinimalCellDimensions();

    double GetCellVolume();

    bool GetIsCellVolumeDefined();

    bool GetIsMeshElementVolumeDefined();

    bool GetIsCellDynamic();

    std::vector<double> GetMeshDomainScale();

    unsigned GetTotalNumberMeshVoxels();

    unsigned GetNumberMeshVoxelsInCell();

    unsigned GetNumberMeshVoxelsOnBoundary();

    std::vector<double> GetChangeInCellDimensions();

    bool GetIncludeOdeInterpolationInCell();

    bool GetAverageInternalCellStates();

    std::vector<std::vector<double>> GetVectorOfBoundaryStateVariables();

    std::vector<double> GetVectorOfBoundaryStateVariablesByLocationIndex(unsigned);

    std::vector<std::vector<double>> GetVectorOfInternalBoundaryStateVariables();

    std::vector<double> GetVectorOfInternalBoundaryStateVariablesByLocationIndex(unsigned);

    std::vector<ChastePoint<SPACE_DIM>> GetVectorOfBoundaryLocations();

    ChastePoint<SPACE_DIM> GetVectorOfBoundaryLocationsByIndex(unsigned);

    std::vector<double> GetNextTimestepConcentrationVector();

};

template<unsigned SPACE_DIM>
ExtendedCellProperty<SPACE_DIM>::ExtendedCellProperty()
: ChemicalCellProperty()
{

}

template<unsigned SPACE_DIM>
ExtendedCellProperty<SPACE_DIM>::~ExtendedCellProperty()
{
}

// virtual methods

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::SetUpExtendedCell(StateVariableRegister* p_StateVariableRegister, std::vector<double> concentrationVector, std::vector<double> cellDimensions)
{
    InitialiseCell(p_StateVariableRegister, concentrationVector);
    SetCellDimensions(cellDimensions);

    std::vector<double> minimalDimensions(SPACE_DIM,mMinimalDimensions[0]);
    SetMinimalCellDimensions(minimalDimensions);

    // instantansiate the cell space
    ResetNextTimestepConcentrationVector(concentrationVector.size());

    std::vector<ChastePoint<SPACE_DIM>> dummyPoint;
    std::vector<std::vector<double>> vectorDummy;
    SetVectorOfBoundaryStateVariables(vectorDummy);
    SetVectorOfBoundaryLocations(dummyPoint);
    CalculateVolumeOfCell();
    CalculateNumberOfCellMeshElements(ChastePoint<SPACE_DIM>());
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::SetUpExtendedCell(StateVariableRegister* p_StateVariableRegister, std::vector<double> concentrationVector, double radius)
{
    InitialiseCell(p_StateVariableRegister, concentrationVector);
    std::vector<double> cellDimensions(1,radius);
    SetCellDimensions(cellDimensions);

    // instantansiate the cell space
    ResetNextTimestepConcentrationVector(concentrationVector.size());

    std::vector<ChastePoint<SPACE_DIM>> dummyPoint;
    std::vector<std::vector<double>> vectorDummy;
    SetVectorOfBoundaryStateVariables(vectorDummy);
    SetVectorOfBoundaryLocations(dummyPoint);
    CalculateVolumeOfCell();
    CalculateNumberOfCellMeshElements(ChastePoint<SPACE_DIM>());

}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::UpdateExtendedCell()
{
    if(mIsCellDynamic)
    {
        CellGrowth();
    }
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::InitialiseCell(StateVariableRegister* p_StateVariableRegister, std::vector<double> concentrationVector)
{
    ChemicalCellProperty::InitialiseCell(p_StateVariableRegister, concentrationVector);
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::CellGrowth()
{
    // apply the change in cell dimensions to the cell dimension vector
    for(unsigned i=0; i<mCellDimensions.size();i++)
    {
        mCellDimensions[i] += mChangeInCellDimensions[i];
    }

    CalculateVolumeOfCell();
    CalculateNumberOfCellMeshElements(ChastePoint<SPACE_DIM>());
}

template<unsigned SPACE_DIM>
bool ExtendedCellProperty<SPACE_DIM>::IsPointInCell(const ChastePoint<SPACE_DIM>& cellCentrePoint, ChastePoint<SPACE_DIM>& test_point)
{
    // assume the cell is SPACE_DIM spherical with radius mCellDimensions[0]

    std::vector<double> difference;
    double square_difference_distance=0.0;
    for(unsigned dim=0; dim<SPACE_DIM; dim++)
    {
        square_difference_distance += pow((cellCentrePoint.rGetLocation()[dim] - test_point.rGetLocation()[dim]),2.0);
    }

    if(sqrt(square_difference_distance)<=mCellDimensions[0])
    {
        return true;
    }

    return false;
}

template<unsigned SPACE_DIM>
bool ExtendedCellProperty<SPACE_DIM>::IsPointOnCellBoundary(const ChastePoint<SPACE_DIM>& cellCentrePoint, ChastePoint<SPACE_DIM>& test_point)
{
    // assume the cell is SPACE_DIM spherical with radius mCellDimensions[0]
    // if the test_point is within the cell but a secondary test point either side is not
    // within the cell we can say the test_point is a boundary point. Use the mMeshDomainScale
    // to determine the locations of the points either side. Stepsize in x mMeshDomainScale[0]
    // stepsize in y mMeshDomainScale[1], stepsize in z mMeshDomainScale[2] .

    if(IsPointInCell(cellCentrePoint,test_point))
    {
        return CheckCellBoundary(cellCentrePoint,test_point);
    }
    // test_point is not within the cell so cannot possibly be a boundary point
    return false;
}

template<unsigned SPACE_DIM>
bool ExtendedCellProperty<SPACE_DIM>::CheckCellBoundary(const ChastePoint<SPACE_DIM>& cellCentrePoint, ChastePoint<SPACE_DIM>& test_point)
{
    // the number of test cases depends on SPACE_DIM
    // SPACE_DIM=1 the single x dimension; 2 cases positive and negative x
    // SPACE_DIM=2 4 cases; positive and negative x takes precedence but also positive and negative y
    // SPACE_DIM=3 6 cases; positive and negative x, positive and negative y, positive and negative z

    ChastePoint<SPACE_DIM> secondary_test_point = test_point;
    for(unsigned dim=0; dim<SPACE_DIM; dim++)
    {
        secondary_test_point.SetCoordinate(dim, test_point[dim]+ mCellDimensions[dim]);
        if(!IsPointInCell(cellCentrePoint, secondary_test_point))
        {
            return true;
            break;
        }
        secondary_test_point.SetCoordinate(dim, test_point[dim] - mCellDimensions[dim]);
        if(!IsPointInCell(cellCentrePoint, secondary_test_point))
        {
            return true;
            break;
        }
        // reset the secondary test point
        secondary_test_point = test_point;
    }

    return false;
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::CalculateVolumeOfCell()
{
    if(mCellDimensions[0]<mMinimalDimensions[0])
    {
        // i.e the cell radius is below the minimal dimension threshold suggesting
        // radius is true zero or at least negligible. Set the volume ot be zero.
        mCellVolume = 0.0;
        mIsCellVolumeDefined =false;
    }
    else
    {
        // have a finite radius so calculate the volume in the spatial dimensions
        switch(SPACE_DIM)
        {
            case 2:
                mCellVolume = M_PI*pow(mCellDimensions[0],2.0);
                mIsCellVolumeDefined =true;
                break;

            case 3:
                mCellVolume = (4/3)*M_PI*pow(mCellDimensions[0],3.0);
                mIsCellVolumeDefined =true;
                break;
            default:
                mCellVolume = 0.0;
                mIsCellVolumeDefined =false;
        }
    }
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::CalculateNumberOfCellMeshElements(ChastePoint<SPACE_DIM> cellCentrePoint)
{

    // over ride previous calculations
    mIsMeshElementVolumeDefined = false;

    mTotalNumberMeshVoxels=0;

    mNumberMeshVoxelsInCell=0;

    mNumberMeshVoxelsOnBoundary=0;

    if(mCellDimensions[0]<mMinimalDimensions[0])
    {
        // i.e the cell radius is below the minimal dimension threshold suggesting
        // radius is true zero or at least negligible.

        // cell must exist at a point which is considered a boundary for transport phenomena
        mTotalNumberMeshVoxels =1;
        mNumberMeshVoxelsOnBoundary =1;
        mIsMeshElementVolumeDefined =true;
    }
    else
    {
        // have a finite radius so calculate the volume in terms of mesh voxels in the spatial dimensions
        if(CheckCellBoundary(cellCentrePoint,cellCentrePoint))
        {
            // if the cell centre is a boundary point then the central voxel is the only voxel within the cell
            mTotalNumberMeshVoxels =1;
            mNumberMeshVoxelsOnBoundary =1;
            mIsMeshElementVolumeDefined =true;
        }
        else
        {

            switch(SPACE_DIM)
            {
                case 1:
                    // line segement; some outer boundary voxels enclose some cell voxels
                    // step along the line from the point start_point. Update the cell and boundary voxels

                    StepThroughXVoxels(cellCentrePoint,cellCentrePoint);

                    break;

                case 2:
                    // 2D plane segment; a 2D shell of boundary voxels surrounding some internal cell voxels
                    // step along the y direction and run through the whole x line for a given y

                    StepThroughXYVoxels(cellCentrePoint,cellCentrePoint);

                    break;

                case 3:
                    // 3D volume segment; a 3D shell of boundary voxels surrounding some internal cell voxels
                    // step along the z direction and run through the whole xy plane for a given z

                    StepThroughXYZVoxels(cellCentrePoint,cellCentrePoint);

                    break;
                default:
                    // cell must exist at a point which is considered a boundary for transport phenomena
                    mTotalNumberMeshVoxels =1;
                    mNumberMeshVoxelsOnBoundary =1;
            }
            mIsMeshElementVolumeDefined =true;
            mTotalNumberMeshVoxels = mNumberMeshVoxelsOnBoundary + mNumberMeshVoxelsInCell;
        }

    }

}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::UpdateCellConcentrationVector(std::vector<double>& cell_concentration_vector_from_cell_data)
{
    // input a concentration vector of the size and ordering of the internal cell concentration vector
    std::vector<double> concentrationVector(cell_concentration_vector_from_cell_data.size(),0.0);

    double constant_state_avaliability = TransportConcentrationAvaliabilityThisState();

    for(unsigned i=0; i<cell_concentration_vector_from_cell_data.size(); i++)
    {
        concentrationVector[i] =  constant_state_avaliability*cell_concentration_vector_from_cell_data[i];
        cell_concentration_vector_from_cell_data[i] -= concentrationVector[i];
    }

    mConcentrationVector = concentrationVector;
}

template<unsigned SPACE_DIM>
double ExtendedCellProperty<SPACE_DIM>::TransportConcentrationAvaliabilityThisState(std::string, unsigned)
{
    // return double in [0.0,1.0] representing the proportion of the cell data that may be avaliable for cell boundary
    // transport.  May be dependent of state properties such as intra cell diffusivities, charges etc.
    // default return mTransportConcentrationAvaliability;
    return mTransportConcentrationAvaliability;
}

// concrete methods

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::StepThroughDirectionalXBoundaryVoxels(ChastePoint<SPACE_DIM>& cellCentrePoint,ChastePoint<SPACE_DIM>& start_point, unsigned polarity)
{
    // start point is on the boundary, step along the boundary until the voxel no longer belongs in the cell

    // check for contigual boundary points in both positive nad negative x directions
    // positive x direction
    bool test_point_is_on_boundary = true;
    ChastePoint<SPACE_DIM> test_point = start_point;
    test_point.SetCoordinate(0, start_point[0]);
    unsigned x_step_counter=1;
    double deltaX = mMeshDomainScale[0]; // step size of the FeMesh
    if(polarity>0.0)
    {
        while(test_point_is_on_boundary)
        {
            // while we are on the boundary need to check for contigual voxels also being on the boundary
            // will occur when x plane aligns with boundary tangent. Check that next voxel in x is in cell
            test_point.SetCoordinate(0, start_point[0] + x_step_counter*deltaX);
            test_point_is_on_boundary = IsPointInCell(cellCentrePoint,test_point);
            mNumberMeshVoxelsOnBoundary += (unsigned)test_point_is_on_boundary;
            x_step_counter +=1;
        }
    }
    else
    {
        while(test_point_is_on_boundary)
        {
            // while we are on the boundary need to check for contigual voxels also being on the boundary
            // will occur when x plane aligns with boundary tangent. Check that next voxel in x is in cell
            test_point.SetCoordinate(0, start_point[0] - x_step_counter*deltaX);
            test_point_is_on_boundary = IsPointInCell(cellCentrePoint,test_point);
            mNumberMeshVoxelsOnBoundary += (unsigned)test_point_is_on_boundary;
            x_step_counter +=1;
        }
    }
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::StepThroughDirectionalXInternalVoxels(ChastePoint<SPACE_DIM>& cellCentrePoint,ChastePoint<SPACE_DIM>& start_point, unsigned polarity)
{
    // starting point is in the cell
    bool test_point_is_on_boundary = false;
    ChastePoint<SPACE_DIM> test_point = start_point;
    test_point.SetCoordinate(0, start_point[0]);
    unsigned x_step_counter=1;
    double deltaX = mMeshDomainScale[0]; // step size in x of FeMesh
    if(polarity>0.0)
    {
        // positive direction
        while(!test_point_is_on_boundary)
        {
            test_point.SetCoordinate(0, start_point[0] + x_step_counter*deltaX);
            test_point_is_on_boundary = CheckCellBoundary(cellCentrePoint,test_point);
            mNumberMeshVoxelsInCell += (unsigned)!test_point_is_on_boundary;
            x_step_counter +=1;
        }

    }
    else
    {
        // negative direction
        while(!test_point_is_on_boundary)
        {
            test_point.SetCoordinate(0, start_point[0] - x_step_counter*deltaX);
            test_point_is_on_boundary = CheckCellBoundary(cellCentrePoint,test_point);
            mNumberMeshVoxelsInCell += (unsigned)!test_point_is_on_boundary;
            x_step_counter +=1;
        }
    }

}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::StepThroughXVoxels(ChastePoint<SPACE_DIM>& cellCentrePoint,ChastePoint<SPACE_DIM>& start_point)
{
    // perturb the centre point across x dimension until hit the boundary

    // assume the start point is a voxel of the cell

    // initialise procedure variables
    ChastePoint<SPACE_DIM> test_point = start_point;
    bool test_point_is_on_boundary = false;
    unsigned x_step_counter=1; // x_step_counter = 0 refers to the original start point

    // check if the start point is on the boundary
    if(CheckCellBoundary(cellCentrePoint,start_point))
    {
        mNumberMeshVoxelsOnBoundary +=1;
        test_point.SetCoordinate(0, start_point[0]);
        StepThroughDirectionalXBoundaryVoxels(cellCentrePoint,test_point,1);

        // negative x direction
         // re-center
        test_point.SetCoordinate(0, start_point[0]);
        StepThroughDirectionalXBoundaryVoxels(cellCentrePoint,test_point,-1);
    }
    else
    {   // starting within the cell body, begin stepping procedure
        mNumberMeshVoxelsInCell +=1; // for the start point

        // positive x direction
        test_point.SetCoordinate(0, start_point[0]);
        StepThroughDirectionalXInternalVoxels(cellCentrePoint,test_point,1);
        // ends when we hit a boundary cell
        mNumberMeshVoxelsOnBoundary +=1;
        // continue the stepping but along the boundary, don't recenter the test_point
        StepThroughDirectionalXBoundaryVoxels(cellCentrePoint,test_point,1);

        // negative x direction
        test_point.SetCoordinate(0, start_point[0]); // re set starting test_point
        StepThroughDirectionalXInternalVoxels(cellCentrePoint,test_point,-1);
        // ends when we hit a boundary cell
        mNumberMeshVoxelsOnBoundary +=1;
        // continue the stepping but along the boundary, don't recenter the test_point
        StepThroughDirectionalXBoundaryVoxels(cellCentrePoint,test_point,-1);

    }
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::StepThroughDirectionalXYBoundaryVoxels(ChastePoint<SPACE_DIM>& cellCentrePoint,ChastePoint<SPACE_DIM>& start_point, unsigned polarity)
{
    // start point is on the boundary
    // step along the boundary in the y direction and step through the x direction
    // until the voxel no longer belongs in the cell

    // check for contigual boundary points in both positive nad negative x directions
    // positive x direction
    bool test_point_is_on_boundary = true;
    ChastePoint<SPACE_DIM> test_point = start_point;
    test_point.SetCoordinate(1, start_point[1]);
    unsigned y_step_counter=1;
    double deltaY = mMeshDomainScale[1]; // step size in y of FeMesh
    if(polarity>0.0)
    {
        while(test_point_is_on_boundary)
        {
            // while we are on the boundary need to check for contigual voxels also being on the boundary
            // will occur when XY plane aligns with boundary tangent. Check that next voxel in Y is in cell
            test_point.SetCoordinate(1, start_point[1] + y_step_counter*deltaY);
            test_point_is_on_boundary = IsPointInCell(cellCentrePoint,test_point);

            if(test_point_is_on_boundary)
            {
                // next voxel step in Y is in boundary, run through the X slice from this test_point
                StepThroughXVoxels(cellCentrePoint,start_point);
            }
            // if next point is not on the boundary then it isn't in the cell and subsequent voxels will also not
            // be in the cell with respect to x

            y_step_counter +=1;
        }
    }
    else
    {
        while(test_point_is_on_boundary)
        {
            // while we are on the boundary need to check for contigual voxels also being on the boundary
            // will occur when XY plane aligns with boundary tangent. Check that next voxel in Y is in cell
            test_point.SetCoordinate(1, start_point[1] - y_step_counter*deltaY);
            test_point_is_on_boundary = IsPointInCell(cellCentrePoint,test_point);

            if(test_point_is_on_boundary)
            {
                // next voxel step in Y is in boundary, run through the X slice from this test_point
                StepThroughXVoxels(cellCentrePoint,start_point);
            }
            // if next point is not on the boundary then it isn't in the cell and subsequent voxels will also not
            // be in the cell with respect to x

            y_step_counter +=1;
        }
    }
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::StepThroughDirectionalXYInternalVoxels(ChastePoint<SPACE_DIM>& cellCentrePoint,ChastePoint<SPACE_DIM>& start_point, unsigned polarity)
{
    // starting point is in the cell
    bool test_point_is_on_boundary = false;
    ChastePoint<SPACE_DIM> test_point = start_point;
    test_point.SetCoordinate(1, start_point[1]);
    unsigned y_step_counter=1;
    double deltaY = mMeshDomainScale[1]; // step size in y of FeMesh
    if(polarity>0.0)
    {
        // positive direction
        while(!test_point_is_on_boundary)
        {
            test_point.SetCoordinate(1, start_point[1] + y_step_counter*deltaY);
            test_point_is_on_boundary = CheckCellBoundary(cellCentrePoint,test_point);

            // if the next y step voxel is not on the boundary run through the x slice
            if(!test_point_is_on_boundary)
            {
                // next voxel step in Y is in boundary, run through the X slice from this test_point
                StepThroughXVoxels(cellCentrePoint,test_point);
            }
            // if the next y step voxel is on the boundary end
            y_step_counter +=1;

        }
    }
    else
    {
        // negative direction
        while(!test_point_is_on_boundary)
        {
            test_point.SetCoordinate(1, start_point[1] - y_step_counter*deltaY);
            test_point_is_on_boundary = CheckCellBoundary(cellCentrePoint,test_point);

            // if the next y step voxel is not on the boundary run through the x slice
            if(!test_point_is_on_boundary)
            {
                // next voxel step in Y is in boundary, run through the X slice from this test_point
                StepThroughXVoxels(cellCentrePoint,test_point);
            }
            // if the next y step voxel is on the boundary end
            y_step_counter +=1;
        }
    }

}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::StepThroughXYVoxels(ChastePoint<SPACE_DIM>& cellCentrePoint,ChastePoint<SPACE_DIM>& start_point)
{
    // consider the 2D case, (X,Y). For each step in Y we need to step through the corresponding x

    // assume the start point is a voxel of the cell

    // initialise procedure variables
    ChastePoint<SPACE_DIM> test_point = start_point;

    // check if the start point is on the boundary
    if(CheckCellBoundary(cellCentrePoint,start_point))
    {
        mNumberMeshVoxelsOnBoundary +=1;
        // positive y direction
        test_point.SetCoordinate(1, start_point[1]);
        StepThroughDirectionalXYBoundaryVoxels(cellCentrePoint,test_point,1);

        // negative y direction
        // re-center
        test_point.SetCoordinate(1, start_point[1]);
        StepThroughDirectionalXYBoundaryVoxels(cellCentrePoint,test_point,-1);
    }
    else
    {   // starting within the cell body, begin stepping procedure
        mNumberMeshVoxelsInCell +=1; // for the start point

        // positive x direction
        test_point.SetCoordinate(1, start_point[1]);
        StepThroughDirectionalXYInternalVoxels(cellCentrePoint,test_point,1);
        // ends when we hit a boundary cell
        mNumberMeshVoxelsOnBoundary +=1;
        // continue the stepping but along the boundary, don't recenter the test_point
        StepThroughDirectionalXYBoundaryVoxels(cellCentrePoint,test_point,1);

        // negative x direction
        test_point.SetCoordinate(1, start_point[1]); // re set starting test_point
        StepThroughDirectionalXYInternalVoxels(cellCentrePoint,test_point,-1);
        // ends when we hit a boundary cell
        mNumberMeshVoxelsOnBoundary +=1;
        // continue the stepping but along the boundary, don't recenter the test_point
        StepThroughDirectionalXYBoundaryVoxels(cellCentrePoint,test_point,-1);

    }

}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::StepThroughDirectionalXYZBoundaryVoxels(ChastePoint<SPACE_DIM>& cellCentrePoint,ChastePoint<SPACE_DIM>& start_point, unsigned polarity)
{
    // start point is on the boundary
    // step along the boundary in the z direction and step through the x and Y directions recursively
    // until the voxel no longer belongs in the cell

    // check for contigual boundary points in both positive and negative y directions
    // positive y direction
    bool test_point_is_on_boundary = true;
    ChastePoint<SPACE_DIM> test_point = start_point;
    test_point.SetCoordinate(2, start_point[2]);
    unsigned z_step_counter=1;
    double deltaZ = mMeshDomainScale[2]; // step size in z of FeMesh
    if(polarity>0.0)
    {
        while(test_point_is_on_boundary)
        {
            // while we are on the boundary need to check for contigual voxels also being on the boundary
            // will occur when XY plane aligns with boundary tangent. Check that next voxel in Y is in cell
            test_point.SetCoordinate(2, start_point[2] + z_step_counter*deltaZ);
            test_point_is_on_boundary = IsPointInCell(cellCentrePoint,test_point);

            if(test_point_is_on_boundary)
            {
                // next voxel step in Z is in boundary, run through the XY slice from this test_point
                StepThroughXYVoxels(cellCentrePoint,start_point);
            }
            // if next point is not on the boundary then it isn't in the cell and subsequent voxels will also not
            // be in the cell with respect to Y

            z_step_counter +=1;
        }
    }
    else
    {
        while(test_point_is_on_boundary)
        {
            // while we are on the boundary need to check for contigual voxels also being on the boundary
            // will occur when XYZ shell aligns with boundary tangent. Check that next voxel in Z is in cell
            test_point.SetCoordinate(2, start_point[2] - z_step_counter*deltaZ);
            test_point_is_on_boundary = IsPointInCell(cellCentrePoint,test_point);

            if(test_point_is_on_boundary)
            {
                // next voxel step in Z is in boundary, run through the Y slice from this test_point
                StepThroughXYVoxels(cellCentrePoint,start_point);
            }
            // if next point is not on the boundary then it isn't in the cell and subsequent voxels will also not
            // be in the cell with respect to y

            z_step_counter +=1;
        }
    }
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::StepThroughDirectionalXYZInternalVoxels(ChastePoint<SPACE_DIM>& cellCentrePoint,ChastePoint<SPACE_DIM>& start_point, unsigned polarity)
{
    // starting point is in the cell
    bool test_point_is_on_boundary = false;
    ChastePoint<SPACE_DIM> test_point = start_point;
    test_point.SetCoordinate(2, start_point[2]);
    unsigned z_step_counter=1;
    double deltaZ = mMeshDomainScale[1]; // step size in z of FeMesh
    if(polarity>0.0)
    {
        // positive direction
        while(!test_point_is_on_boundary)
        {
            test_point.SetCoordinate(2, start_point[2] + z_step_counter*deltaZ);
            test_point_is_on_boundary = CheckCellBoundary(cellCentrePoint,test_point);

            // if the next y step voxel is not on the boundary run through the x slice
            if(!test_point_is_on_boundary)
            {
                // next voxel step in Z is in boundary, run through the Y slice from this test_point
                StepThroughXYVoxels(cellCentrePoint,test_point);
            }
            // if the next z step voxel is on the boundary end
            z_step_counter +=1;

        }
    }
    else
    {
        // negative direction
        while(!test_point_is_on_boundary)
        {
            test_point.SetCoordinate(2, start_point[2] - z_step_counter*deltaZ);
            test_point_is_on_boundary = CheckCellBoundary(cellCentrePoint,test_point);

            // if the next z step voxel is not on the boundary run through the y slice
            if(!test_point_is_on_boundary)
            {
                // next voxel step in Z is in boundary, run through the Y slice from this test_point
                StepThroughXYVoxels(cellCentrePoint,test_point);
            }
            // if the next z step voxel is on the boundary end
            z_step_counter +=1;
        }
    }

}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::StepThroughXYZVoxels(ChastePoint<SPACE_DIM>& cellCentrePoint,ChastePoint<SPACE_DIM>& start_point)
{
    // consider the 3D case, (X,Y,Z). For each step in Z we need to step through the corresponding Y and X slices

    // assume the start point is a voxel of the cell

    // initialise procedure variables
    ChastePoint<SPACE_DIM> test_point = start_point;

    // check if the start point is on the boundary
    if(CheckCellBoundary(cellCentrePoint,start_point))
    {
        mNumberMeshVoxelsOnBoundary +=1;
        // positive y direction
        test_point.SetCoordinate(2, start_point[2]);
        StepThroughDirectionalXYZBoundaryVoxels(cellCentrePoint,test_point,1);

        // negative y direction
        // re-center
        test_point.SetCoordinate(2, start_point[2]);
        StepThroughDirectionalXYZBoundaryVoxels(cellCentrePoint,test_point,-1);
    }
    else
    {   // starting within the cell body, begin stepping procedure
        mNumberMeshVoxelsInCell +=1; // for the start point

        // positive x direction
        test_point.SetCoordinate(2, start_point[2]);
        StepThroughDirectionalXYZInternalVoxels(cellCentrePoint,test_point,1);
        // ends when we hit a boundary cell
        mNumberMeshVoxelsOnBoundary +=1;
        // continue the stepping but along the boundary, don't recenter the test_point
        StepThroughDirectionalXYZBoundaryVoxels(cellCentrePoint,test_point,1);

        // negative x direction
        test_point.SetCoordinate(2, start_point[2]); // re set starting test_point
        StepThroughDirectionalXYZInternalVoxels(cellCentrePoint,test_point,-1);
        // ends when we hit a boundary cell
        mNumberMeshVoxelsOnBoundary +=1;
        // continue the stepping but along the boundary, don't recenter the test_point
        StepThroughDirectionalXYZBoundaryVoxels(cellCentrePoint,test_point,-1);

    }
}

template<unsigned SPACE_DIM>
double ExtendedCellProperty<SPACE_DIM>::RetrieveBoundarySourceByStateName(std::string stateName, ChastePoint<SPACE_DIM> rX)
{
    // if cell is on boundary add the result of the transport Ode system
    unsigned index = mpStateVariableRegister -> RetrieveStateVariableIndex(stateName);

    // retrieve the extended cell property
    //boost::shared_ptr<ExtendedCellProperty<SPACE_DIM>> extended_cell_property = boost::static_pointer_cast<ExtendedCellProperty<SPACE_DIM>>(mThis_cellPtr->rGetCellPropertyCollection().GetPropertiesType<ExtendedCellProperty<SPACE_DIM>>().GetProperty());

    return RetrieveBoundaryCellSourceByStateNameAndLocation(stateName, rX);
}

template<unsigned SPACE_DIM>
double ExtendedCellProperty<SPACE_DIM>::RetrieveInternalCellSourceByStateName(std::string stateName)
{
    // the value to give the variable stateName when the point is in the cell

    unsigned index = mpStateVariableRegister -> RetrieveStateVariableIndex(stateName);

    return mConcentrationVector[index];
}

template<unsigned SPACE_DIM>
double ExtendedCellProperty<SPACE_DIM>::RetrieveBoundaryCellSourceByStateNameAndLocation(std::string stateName, ChastePoint<SPACE_DIM> rX_test)
{
    // run through the registered boundary locations to find the one that is congruent with the test point

    for(unsigned index=0; index<mVectorOfBoundaryLocations.size();index++)
    {
        if(CheckChastePointsForEquality(rX_test,mVectorOfBoundaryLocations[index]))
        {
            // return the corresponding state variable
            return mVectorOfExternalBoundaryStateVariables[index][mpStateVariableRegister -> RetrieveStateVariableIndex(stateName)];
        }
    }
    return 0.0;
}

template<unsigned SPACE_DIM>
double ExtendedCellProperty<SPACE_DIM>::RetrieveBoundaryInternalCellSourceByStateNameAndLocation(std::string stateName, ChastePoint<SPACE_DIM> rX_test)
{
    // run through the registered boundary locations to find the one that is congruent with the test point

    for(unsigned index=0; index<mVectorOfBoundaryLocations.size;index++)
    {
        if(CheckChastePointsForEquality(rX_test,mVectorOfBoundaryLocations[index]))
        {
            // return the corresponding state variable
            return mVectorOfInternalBoundaryStateVariables[index][mpStateVariableRegister -> RetrieveStateVariableIndex(stateName)];
        }
    }
    return 0.0;
}

template<unsigned SPACE_DIM>
std::vector<double>& ExtendedCellProperty<SPACE_DIM>::RetrieveBoundaryCellSourceByLocation(ChastePoint<SPACE_DIM> rX_test)
{
    // run through the registered boundary locations to find the one that is congruent with the test point

    for(unsigned index=0; index<mVectorOfBoundaryLocations.size;index++)
    {
        if(CheckChastePointsForEquality(rX_test,mVectorOfBoundaryLocations[index]))
        {
            // return the corresponding state variable vector
            return mVectorOfExternalBoundaryStateVariables[index];
        }
    }
    std::vector<double> empty_return(mVectorOfExternalBoundaryStateVariables[0].size(),0.0);

    return empty_return;
}

template<unsigned SPACE_DIM>
std::vector<double>& ExtendedCellProperty<SPACE_DIM>::RetrieveInternalBoundaryCellSourceByLocation(ChastePoint<SPACE_DIM> rX_test)
{
    // run through the registered boundary locations to find the one that is congruent with the test point

    for(unsigned index=0; index<mVectorOfBoundaryLocations.size;index++)
    {
        if(CheckChastePointsForEquality(rX_test,mVectorOfBoundaryLocations[index]))
        {
            // return the corresponding state variable vector
            return mVectorOfInternalBoundaryStateVariables[index];
        }
    }
    std::vector<double> empty_return(mVectorOfInternalBoundaryStateVariables[0].size(),0.0);

    return empty_return;
}

template<unsigned SPACE_DIM>
bool ExtendedCellProperty<SPACE_DIM>::CheckChastePointsForEquality(ChastePoint<SPACE_DIM> rX1,ChastePoint<SPACE_DIM> rX2)
{
    // function to check whether two chaste points are congruent
    // use half the scale dimension as an error

    bool equality=true;

    for(unsigned i=0; i<SPACE_DIM; i++)
    {
        if(rX1[i] < rX2[i] + 0.5*mMeshDomainScale[i] && rX1[i] >= rX2[i] - 0.5*mMeshDomainScale[i])
        {
            // both points are congruent within 0.5 of mesh scale in the ith dimension
            equality=true;
        }
        else
        {
            // as soon as a dimension where the points are disaparate is found end the function return false
            equality=false;
            break;
        }

    }
    return equality;
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::ResetNextTimestepConcentrationVector()
{
    std::vector<double> resetVector(mNextTimestepConcentrationVector.size(),0.0);
    mNextTimestepConcentrationVector = resetVector;
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::ResetNextTimestepConcentrationVector(unsigned sizeOfConcentrationVector)
{
    std::vector<double> resetVector(sizeOfConcentrationVector,0.0);
    mNextTimestepConcentrationVector = resetVector;
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::ResetVectorOfBoundaryStateVariables()
{
    mVectorOfExternalBoundaryStateVariables.clear();
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::ResetVectorOfBoundaryLocations()
{
    mVectorOfBoundaryLocations.clear();
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::ResetVectorOfInternalBoundaryStateVariables()
{
    mVectorOfInternalBoundaryStateVariables.clear();
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::AddToVectorOfBoundaryStateVariables(std::vector<double> boundaryStateVariables)
{
    mVectorOfExternalBoundaryStateVariables.push_back(boundaryStateVariables);
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::AddToVectorOfInternalBoundaryStateVariables(std::vector<double> boundaryStateVariables)
{
    mVectorOfInternalBoundaryStateVariables.push_back(boundaryStateVariables);
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::AddToVectorOfBoundaryLocations(ChastePoint<SPACE_DIM> point)
{
    mVectorOfBoundaryLocations.push_back(point);
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::AppendInternalCellBoundaryConcentrations(std::vector<double>& rY, unsigned boundary_index)
{

    if(mVectorOfBoundaryLocations.empty())
    {
        if(mNumberMeshVoxelsOnBoundary>0)
        {
            // scale the cell concentration by the number of boundary voxels shared
            for(unsigned i=0; i<mConcentrationVector.size(); i++)
            {
                rY.push_back(mConcentrationVector[i]/mNumberMeshVoxelsOnBoundary);
            }
        }
        else
        {
            // insert without scaling
            rY.insert(rY.end(), mConcentrationVector.begin(), mConcentrationVector.end());
        }
    }
    else
    {
        // use the location index to determine the internal cell concentration
        std::vector<double> internalStates = GetVectorOfInternalBoundaryStateVariablesByLocationIndex(boundary_index);

        rY.insert(rY.end(), internalStates.begin(), internalStates.end());

    }

}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::ReplaceBoundaryStateVariables(unsigned boundary_index, std::vector<double>& rY)
{
    unsigned numberOfStates = mVectorOfExternalBoundaryStateVariables[boundary_index].size();
    unsigned numberOfCellStates = rY.size() - numberOfStates;
    std::vector<double> boundaryExternalStateVariables(numberOfStates,0.0);

    for(unsigned i=0; i<numberOfStates;i++)
    {
        boundaryExternalStateVariables[i] = rY[i];
    }

    SetVectorOfBoundaryStateVariablesByIndex(boundary_index, boundaryExternalStateVariables);

    std::vector<double> boundaryInternalStateVariables(numberOfCellStates,0.0);

    for(unsigned i=0; i<numberOfCellStates;i++)
    {
        boundaryInternalStateVariables[i] = rY[i + numberOfStates];
        mNextTimestepConcentrationVector[i] += rY[i + numberOfStates];
    }

    SetVectorOfInternalBoundaryStateVariablesByIndex(boundary_index, boundaryInternalStateVariables);
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::RecordLocationAndStateVariable(ChastePoint<SPACE_DIM> rX, std::vector<double> rU)
{
    AddToVectorOfBoundaryStateVariables(rU);
    AddToVectorOfBoundaryLocations(rX);
}

// set methods

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::SetCellRadius(double radius)
{
    mCellDimensions[0] = radius;
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::SetCellDimensions(std::vector<double> cellDimensions)
{
    mCellDimensions = cellDimensions;
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::SetMinimalCellDimensions(std::vector<double> minimalDimensions)
{
    mMinimalDimensions = minimalDimensions;
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::SetCellVolume(double volume)
{
    mCellVolume = volume;
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::SetIsCellDynamic(bool isCellDynamic)
{
    mIsCellDynamic = isCellDynamic;
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::SetMeshDomainScale(std::vector<double> meshDomainScale)
{
    mMeshDomainScale = meshDomainScale;

    switch(SPACE_DIM)
    {
        case 2:
            mVolumePerFineMeshElement = meshDomainScale[0]*meshDomainScale[1];
            mIsMeshElementVolumeDefined = true;
            break;
        case 3:
            mVolumePerFineMeshElement = meshDomainScale[0]*meshDomainScale[1]*meshDomainScale[2];
            mIsMeshElementVolumeDefined = true;
            break;
        default:
            mVolumePerFineMeshElement = meshDomainScale[0];
            mIsMeshElementVolumeDefined = true;
    }
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::SetTotalNumberMeshVoxels(unsigned totalNumberMeshVoxels)
{
    mTotalNumberMeshVoxels = totalNumberMeshVoxels;
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::SetNumberMeshVoxelsInCell(unsigned numberInCell)
{
    mNumberMeshVoxelsInCell = numberInCell;
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::SetNumberMeshVoxelsOnBoundary(unsigned numberOnBoundary)
{
    mNumberMeshVoxelsOnBoundary = numberOnBoundary;
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::SetChangeInCellDimensions(std::vector<double> changeCellDimensions)
{
    mChangeInCellDimensions = changeCellDimensions;
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::SetIncludeOdeInterpolationInCell(bool includeOdeInterpolationInCell)
{
    mIncludeOdeInterpolationInCell = includeOdeInterpolationInCell;
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::SetAverageInternalCellStates(bool averageInternalCellStates)
{
    mAverageInternalCellStates = averageInternalCellStates;
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::SetVectorOfBoundaryStateVariables(std::vector<std::vector<double>> boundaryStateVariables)
{
    mVectorOfExternalBoundaryStateVariables = boundaryStateVariables;
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::SetVectorOfBoundaryStateVariablesByIndex(unsigned index, std::vector<double> boundaryStateVariables)
{
    if(index<mVectorOfExternalBoundaryStateVariables.size())
    {
        mVectorOfExternalBoundaryStateVariables[index] = boundaryStateVariables;
    }
    else
    {
        std::cout<<"Error: ExtendedCellProperty<SPACE_DIM>::SetVectorOfBoundaryStateVariablesByIndex index out of bounds"<<std::endl;
    }

}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::SetVectorOfInternalBoundaryStateVariables(std::vector<std::vector<double>> boundaryStateVariables)
{
    mVectorOfInternalBoundaryStateVariables = boundaryStateVariables;
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::SetVectorOfInternalBoundaryStateVariablesByIndex(unsigned index, std::vector<double> boundaryStateVariables)
{
    if(index<mVectorOfInternalBoundaryStateVariables.size())
    {
        mVectorOfInternalBoundaryStateVariables[index] = boundaryStateVariables;
    }
    else
    {
        std::cout<<"Error: ExtendedCellProperty<SPACE_DIM>::SetVectorOfInternalBoundaryStateVariablesByIndex index out of bounds"<<std::endl;
    }

}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::SetVectorOfBoundaryLocations(std::vector<ChastePoint<SPACE_DIM>> vecPoint)
{
    mVectorOfBoundaryLocations = vecPoint;
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::SetVectorOfBoundaryLocationsByIndex(unsigned index, ChastePoint<SPACE_DIM> point)
{
    if(index<mVectorOfBoundaryLocations.size())
    {
        mVectorOfBoundaryLocations[index] = point;
    }
    else
    {
        std::cout<<"Error: ExtendedCellProperty<SPACE_DIM>::SetVectorOfBoundaryLocationsByIndex index out of bounds"<<std::endl;
    }
}

template<unsigned SPACE_DIM>
void ExtendedCellProperty<SPACE_DIM>::SetNextTimestepConcentrationVector(std::vector<double> concentrations)
{
    mNextTimestepConcentrationVector = concentrations;
}

// Get methods

template<unsigned SPACE_DIM>
double ExtendedCellProperty<SPACE_DIM>::GetCellRadius()
{
    return mCellDimensions[0];
}

template<unsigned SPACE_DIM>
std::vector<double> ExtendedCellProperty<SPACE_DIM>::GetCellDimensions()
{
    return mCellDimensions;
}

template<unsigned SPACE_DIM>
std::vector<double> ExtendedCellProperty<SPACE_DIM>::GetMinimalCellDimensions()
{
    return mMinimalDimensions;
}

template<unsigned SPACE_DIM>
double ExtendedCellProperty<SPACE_DIM>::GetCellVolume()
{
    return mCellVolume;
}

template<unsigned SPACE_DIM>
bool ExtendedCellProperty<SPACE_DIM>::GetIsCellVolumeDefined()
{
    return mIsCellVolumeDefined;
}

template<unsigned SPACE_DIM>
bool ExtendedCellProperty<SPACE_DIM>::GetIsMeshElementVolumeDefined()
{
    return mIsMeshElementVolumeDefined;
}

template<unsigned SPACE_DIM>
bool ExtendedCellProperty<SPACE_DIM>::GetIsCellDynamic()
{
    return mIsCellDynamic;
}

template<unsigned SPACE_DIM>
std::vector<double> ExtendedCellProperty<SPACE_DIM>::GetMeshDomainScale()
{
    return mMeshDomainScale;
}

template<unsigned SPACE_DIM>
unsigned ExtendedCellProperty<SPACE_DIM>::GetTotalNumberMeshVoxels()
{
    return mTotalNumberMeshVoxels;
}

template<unsigned SPACE_DIM>
unsigned ExtendedCellProperty<SPACE_DIM>::GetNumberMeshVoxelsInCell()
{
    return mNumberMeshVoxelsInCell;
}

template<unsigned SPACE_DIM>
unsigned ExtendedCellProperty<SPACE_DIM>::GetNumberMeshVoxelsOnBoundary()
{
    return mNumberMeshVoxelsOnBoundary;
}

template<unsigned SPACE_DIM>
std::vector<double> ExtendedCellProperty<SPACE_DIM>::GetChangeInCellDimensions()
{
    return mChangeInCellDimensions;
}

template<unsigned SPACE_DIM>
bool ExtendedCellProperty<SPACE_DIM>::GetIncludeOdeInterpolationInCell()
{
    return mIncludeOdeInterpolationInCell;
}

template<unsigned SPACE_DIM>
bool ExtendedCellProperty<SPACE_DIM>::GetAverageInternalCellStates()
{
    return mAverageInternalCellStates;
}

template<unsigned SPACE_DIM>
std::vector<std::vector<double>> ExtendedCellProperty<SPACE_DIM>::GetVectorOfBoundaryStateVariables()
{
    return mVectorOfExternalBoundaryStateVariables;
}

template<unsigned SPACE_DIM>
std::vector<double> ExtendedCellProperty<SPACE_DIM>::GetVectorOfBoundaryStateVariablesByLocationIndex(unsigned index)
{
    if(index < mVectorOfExternalBoundaryStateVariables.size())
    {
        return mVectorOfExternalBoundaryStateVariables[index];
    }

    return std::vector<double>();
}

template<unsigned SPACE_DIM>
std::vector<std::vector<double>> ExtendedCellProperty<SPACE_DIM>::GetVectorOfInternalBoundaryStateVariables()
{
    return mVectorOfInternalBoundaryStateVariables;
}

template<unsigned SPACE_DIM>
std::vector<double> ExtendedCellProperty<SPACE_DIM>::GetVectorOfInternalBoundaryStateVariablesByLocationIndex(unsigned index)
{
    if(index < mVectorOfInternalBoundaryStateVariables.size())
    {
        return mVectorOfInternalBoundaryStateVariables[index];
    }

    return std::vector<double>();
}

template<unsigned SPACE_DIM>
std::vector<ChastePoint<SPACE_DIM>> ExtendedCellProperty<SPACE_DIM>::GetVectorOfBoundaryLocations()
{
    return mVectorOfBoundaryLocations;
}

template<unsigned SPACE_DIM>
ChastePoint<SPACE_DIM> ExtendedCellProperty<SPACE_DIM>::GetVectorOfBoundaryLocationsByIndex(unsigned index)
{
    if(index < mVectorOfBoundaryLocations.size())
    {
        return mVectorOfBoundaryLocations[index];
    }

    return ChastePoint<SPACE_DIM>();
}

template<unsigned SPACE_DIM>
std::vector<double> ExtendedCellProperty<SPACE_DIM>::GetNextTimestepConcentrationVector()
{
    return mNextTimestepConcentrationVector;
}

#endif


// Distributed under the MIT License.
// See LICENSE.txt for details.

#include "Framework/TestingFramework.hpp"

#include <array>
#include <boost/optional.hpp>
#include <boost/optional/optional_io.hpp>
#include <memory>
#include <string>
#include <unordered_map>

#include "DataStructures/DataBox/DataBox.hpp"
#include "DataStructures/DataBox/DataBoxTag.hpp"
#include "DataStructures/DataVector.hpp"
#include "DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp"
#include "DataStructures/Tensor/Tensor.hpp"
#include "Domain/CoordinateMaps/Affine.hpp"
#include "Domain/CoordinateMaps/CoordinateMap.hpp"
#include "Domain/CoordinateMaps/CoordinateMap.tpp"
#include "Domain/CoordinateMaps/Identity.hpp"
#include "Domain/CoordinateMaps/ProductMaps.hpp"
#include "Domain/CoordinateMaps/ProductMaps.tpp"
#include "Domain/CoordinateMaps/Tags.hpp"
#include "Domain/CoordinateMaps/TimeDependent/ProductMaps.hpp"
#include "Domain/CoordinateMaps/TimeDependent/ProductMaps.tpp"
#include "Domain/CoordinateMaps/TimeDependent/Translation.hpp"
#include "Domain/FunctionsOfTime/FunctionOfTime.hpp"
#include "Domain/FunctionsOfTime/PiecewisePolynomial.hpp"
#include "Domain/Tags.hpp"
#include "Domain/TagsTimeDependent.hpp"
#include "Framework/TestHelpers.hpp"
#include "Helpers/DataStructures/DataBox/TestHelpers.hpp"
#include "Helpers/DataStructures/MakeWithRandomValues.hpp"
#include "NumericalAlgorithms/LinearOperators/Divergence.hpp"
#include "NumericalAlgorithms/Spectral/Spectral.hpp"
#include "Utilities/TMPL.hpp"

namespace {
template <size_t Dim>
void test_tags() noexcept {
  TestHelpers::db::test_simple_tag<domain::Tags::InitialFunctionsOfTime<Dim>>(
      "InitialFunctionsOfTime");
  TestHelpers::db::test_simple_tag<domain::Tags::FunctionsOfTime>(
      "FunctionsOfTime");
  TestHelpers::db::test_simple_tag<
      domain::Tags::CoordinatesMeshVelocityAndJacobians<Dim>>(
      "CoordinatesMeshVelocityAndJacobians");
  TestHelpers::db::test_compute_tag<
      domain::Tags::CoordinatesMeshVelocityAndJacobiansCompute<
          domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,
                                                      Frame::Inertial>>>(
      "CoordinatesMeshVelocityAndJacobians");
  TestHelpers::db::test_compute_tag<
      domain::Tags::InertialFromGridCoordinatesCompute<Dim>>(
      "InertialCoordinates");
  TestHelpers::db::test_simple_tag<domain::Tags::MeshVelocity<Dim>>(
      "MeshVelocity");
  TestHelpers::db::test_compute_tag<
      domain::Tags::InertialMeshVelocityCompute<Dim>>("MeshVelocity");
  TestHelpers::db::test_simple_tag<domain::Tags::DivMeshVelocity>(
      "div(MeshVelocity)");
}

using TranslationMap = domain::CoordinateMaps::TimeDependent::Translation;
using TranslationMap2d =
    domain::CoordinateMaps::TimeDependent::ProductOf2Maps<TranslationMap,
                                                          TranslationMap>;
using TranslationMap3d = domain::CoordinateMaps::TimeDependent::ProductOf3Maps<
    TranslationMap, TranslationMap, TranslationMap>;

using AffineMap = domain::CoordinateMaps::Affine;
using AffineMap2d =
    domain::CoordinateMaps::ProductOf2Maps<AffineMap, AffineMap>;
using AffineMap3d =
    domain::CoordinateMaps::ProductOf3Maps<AffineMap, AffineMap, AffineMap>;

template <size_t MeshDim>
using ConcreteMap = tmpl::conditional_t<
    MeshDim == 1,
    domain::CoordinateMap<Frame::Grid, Frame::Inertial, TranslationMap,
                          AffineMap>,
    tmpl::conditional_t<MeshDim == 2,
                        domain::CoordinateMap<Frame::Grid, Frame::Inertial,
                                              TranslationMap2d, AffineMap2d>,
                        domain::CoordinateMap<Frame::Grid, Frame::Inertial,
                                              TranslationMap3d, AffineMap3d>>>;

template <size_t MeshDim>
ConcreteMap<MeshDim> create_coord_map(
    const std::array<std::string, 3>& f_of_t_names);

template <>
ConcreteMap<1> create_coord_map<1>(
    const std::array<std::string, 3>& f_of_t_names) {
  return ConcreteMap<1>{TranslationMap{f_of_t_names[0]},
                        AffineMap{-1.0, 1.0, 2.0, 7.2}};
}

template <>
ConcreteMap<2> create_coord_map<2>(
    const std::array<std::string, 3>& f_of_t_names) {
  return ConcreteMap<2>{
      {TranslationMap{f_of_t_names[0]}, TranslationMap{f_of_t_names[1]}},
      {AffineMap{-1.0, 1.0, -2.0, 2.2}, AffineMap{-1.0, 1.0, 2.0, 7.2}}};
}

template <>
ConcreteMap<3> create_coord_map<3>(
    const std::array<std::string, 3>& f_of_t_names) {
  return ConcreteMap<3>{
      {TranslationMap{f_of_t_names[0]}, TranslationMap{f_of_t_names[1]},
       TranslationMap{f_of_t_names[2]}},
      {AffineMap{-1.0, 1.0, -2.0, 2.2}, AffineMap{-1.0, 1.0, 2.0, 7.2},
       AffineMap{-1.0, 1.0, 1.0, 3.5}}};
}

template <size_t Dim, bool IsTimeDependent>
void test() noexcept {
  using simple_tags = db::AddSimpleTags<
      Tags::Time, domain::Tags::Coordinates<Dim, Frame::Grid>,
      domain::Tags::InverseJacobian<Dim, Frame::Logical, Frame::Grid>,
      domain::Tags::FunctionsOfTime,
      domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,
                                                  Frame::Inertial>>;
  using compute_tags = db::AddComputeTags<
      domain::Tags::CoordinatesMeshVelocityAndJacobiansCompute<
          domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,
                                                      Frame::Inertial>>,
      domain::Tags::InertialFromGridCoordinatesCompute<Dim>,
      domain::Tags::ElementToInertialInverseJacobian<Dim>,
      domain::Tags::InertialMeshVelocityCompute<Dim>>;

  MAKE_GENERATOR(gen);
  const std::array<double, 3> velocity{{1.2, 0.2, -8.9}};
  const double initial_time = 0.0;
  const std::array<std::string, 3> functions_of_time_names{
      {"TranslationX", "TranslationY", "TranslationZ"}};

  UniformCustomDistribution<double> dist(-10.0, 10.0);

  const size_t num_pts = Dim * 5;

  tnsr::I<DataVector, Dim, Frame::Grid> grid_coords{num_pts};
  fill_with_random_values(make_not_null(&grid_coords), make_not_null(&gen),
                          make_not_null(&dist));
  InverseJacobian<DataVector, Dim, Frame::Logical, Frame::Grid>
      element_to_grid_inverse_jacobian{num_pts};
  fill_with_random_values(make_not_null(&element_to_grid_inverse_jacobian),
                          make_not_null(&gen), make_not_null(&dist));

  std::unordered_map<std::string,
                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>
      functions_of_time{};
  functions_of_time[functions_of_time_names[0]] =
      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(
          initial_time,
          std::array<DataVector, 3>{{{0.0}, {velocity[0]}, {0.0}}});
  functions_of_time[functions_of_time_names[1]] =
      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(
          initial_time,
          std::array<DataVector, 3>{{{0.0}, {velocity[1]}, {0.0}}});
  functions_of_time[functions_of_time_names[2]] =
      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(
          initial_time,
          std::array<DataVector, 3>{{{0.0}, {velocity[2]}, {0.0}}});

  using MapPtr = std::unique_ptr<
      domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, Dim>>;
  const MapPtr grid_to_inertial_map =
      IsTimeDependent ? MapPtr{std::make_unique<ConcreteMap<Dim>>(
                            create_coord_map<Dim>(functions_of_time_names))}
                      : MapPtr{std::make_unique<domain::CoordinateMap<
                            Frame::Grid, Frame::Inertial,
                            domain::CoordinateMaps::Identity<Dim>>>()};

  const double time = 3.0;
  auto box = db::create<simple_tags, compute_tags>(
      time, grid_coords, element_to_grid_inverse_jacobian,
      std::move(functions_of_time), grid_to_inertial_map->get_clone());

  const auto check_helper = [&box, &element_to_grid_inverse_jacobian,
                             &grid_coords, &grid_to_inertial_map,
                             num_pts](const double expected_time) noexcept {
    if (IsTimeDependent) {
      const tnsr::I<DataVector, Dim, Frame::Inertial> expected_coords =
          (*grid_to_inertial_map)(grid_coords, expected_time,
                                  db::get<domain::Tags::FunctionsOfTime>(box));

      const InverseJacobian<DataVector, Dim, Frame::Grid, Frame::Inertial>
          expected_inv_jacobian_grid_to_inertial =
              grid_to_inertial_map->inv_jacobian(
                  grid_coords, expected_time,
                  db::get<domain::Tags::FunctionsOfTime>(box));

      InverseJacobian<DataVector, Dim, Frame::Logical, Frame::Inertial>
          expected_inv_jacobian{num_pts};

      for (size_t logical_i = 0; logical_i < Dim; ++logical_i) {
        for (size_t inertial_i = 0; inertial_i < Dim; ++inertial_i) {
          expected_inv_jacobian.get(logical_i, inertial_i) =
              element_to_grid_inverse_jacobian.get(logical_i, 0) *
              expected_inv_jacobian_grid_to_inertial.get(0, inertial_i);
          for (size_t grid_i = 1; grid_i < Dim; ++grid_i) {
            expected_inv_jacobian.get(logical_i, inertial_i) +=
                element_to_grid_inverse_jacobian.get(logical_i, grid_i) *
                expected_inv_jacobian_grid_to_inertial.get(grid_i, inertial_i);
          }
        }
      }

      REQUIRE(static_cast<bool>(
          db::get<domain::Tags::CoordinatesMeshVelocityAndJacobians<Dim>>(
              box)));

      for (size_t i = 0; i < Dim; ++i) {
        // Check that the `const_cast`s and set_data_ref inside the compute tag
        // functions worked correctly
        CHECK(db::get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(box)
                  .get(i)
                  .data() ==
              std::get<0>(
                  *db::get<
                      domain::Tags::CoordinatesMeshVelocityAndJacobians<Dim>>(
                      box))
                  .get(i)
                  .data());
      }
      CHECK_ITERABLE_APPROX(
          (db::get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(box)),
          expected_coords);

      for (size_t i = 0;
           i < db::get<domain::Tags::InverseJacobian<Dim, Frame::Logical,
                                                     Frame::Inertial>>(box)
                   .size();
           ++i) {
        CHECK(
            db::get<domain::Tags::InverseJacobian<Dim, Frame::Logical,
                                                  Frame::Inertial>>(box)[i]
                .data() !=
            std::get<1>(*db::get<
                        domain::Tags::CoordinatesMeshVelocityAndJacobians<Dim>>(
                box))[i]
                .data());
      }
      CHECK_ITERABLE_APPROX(
          (db::get<domain::Tags::InverseJacobian<Dim, Frame::Logical,
                                                 Frame::Inertial>>(box)),
          expected_inv_jacobian);

      const auto expected_coords_mesh_velocity_jacobians =
          grid_to_inertial_map->coords_frame_velocity_jacobians(
              db::get<domain::Tags::Coordinates<Dim, Frame::Grid>>(box),
              db::get<::Tags::Time>(box),
              db::get<domain::Tags::FunctionsOfTime>(box));

      for (size_t i = 0; i < Dim; ++i) {
        // Check that the `const_cast`s and set_data_ref inside the compute tag
        // functions worked correctly
        CHECK(db::get<domain::Tags::MeshVelocity<Dim>>(box)->get(i).data() ==
              std::get<3>(
                  *db::get<
                      domain::Tags::CoordinatesMeshVelocityAndJacobians<Dim>>(
                      box))
                  .get(i)
                  .data());
      }
      CHECK_ITERABLE_APPROX(
          db::get<domain::Tags::MeshVelocity<Dim>>(box).get(),
          std::get<3>(expected_coords_mesh_velocity_jacobians));
    } else {
      tnsr::I<DataVector, Dim, Frame::Inertial> expected_coords{num_pts};
      for (size_t i = 0; i < Dim; ++i) {
        expected_coords[i] = grid_coords[i];
      }

      InverseJacobian<DataVector, Dim, Frame::Logical, Frame::Inertial>
          expected_inv_jacobian{num_pts};
      // The Grid->Inertial Jacobian is currently just the identity
      for (size_t i = 0; i < expected_inv_jacobian.size(); ++i) {
        expected_inv_jacobian[i] = element_to_grid_inverse_jacobian[i];
      }

      for (size_t i = 0; i < Dim; ++i) {
        // Check that the `const_cast`s and set_data_ref inside the compute tag
        // functions worked correctly
        CHECK(db::get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(box)
                  .get(i)
                  .data() ==
              db::get<domain::Tags::Coordinates<Dim, Frame::Grid>>(box)
                  .get(i)
                  .data());
      }
      CHECK_ITERABLE_APPROX(
          (db::get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(box)),
          expected_coords);

      for (size_t i = 0;
           i < db::get<domain::Tags::InverseJacobian<Dim, Frame::Logical,
                                                     Frame::Inertial>>(box)
                   .size();
           ++i) {
        // Check that the `const_cast`s and set_data_ref inside the compute tag
        // functions worked correctly
        CHECK(db::get<domain::Tags::InverseJacobian<Dim, Frame::Logical,
                                                    Frame::Inertial>>(box)[i]
                  .data() ==
              db::get<domain::Tags::InverseJacobian<Dim, Frame::Logical,
                                                    Frame::Grid>>(box)[i]
                  .data());
      }
      CHECK_ITERABLE_APPROX(
          (db::get<domain::Tags::InverseJacobian<Dim, Frame::Logical,
                                                 Frame::Inertial>>(box)),
          expected_inv_jacobian);

      CHECK_FALSE(db::get<domain::Tags::MeshVelocity<Dim>>(box));
    }
  };
  check_helper(3.0);

  db::mutate<Tags::Time>(make_not_null(&box),
                         [](const gsl::not_null<double*> local_time) noexcept {
                           *local_time = 4.5;
                         });
  check_helper(4.5);
}

SPECTRE_TEST_CASE("Unit.Domain.TagsTimeDependent", "[Unit][Actions]") {
  test_tags<1>();
  test_tags<2>();
  test_tags<3>();

  test<1, true>();
  test<2, true>();
  test<3, true>();

  test<1, false>();
  test<2, false>();
  test<3, false>();
}
}  // namespace


/**
* DID YOU GET WHAT YOU WANTED FROM THIS LIFE?
* I DID....
* AND WHAT DID YOU WANT?
* TO CALL MYSELF BELOVED.
* TO FEEL MYSELF BELOVED ON THE EARTH....
*/
#include <bits/stdc++.h>
#define sz(x) (x.size())
#define all(x) (x).begin(), (x).end()
#define pb(x) push_back(x)
#define mp(x, y) make_pair(x, y)
#define ___WTHIT___ main
//#include <boost/accumulators/accumulators.hpp>
//#include <dirent.h>
//#include <stdio.h>
//#pragma comment(linker, "/STACK:16777216")
//#define M 300002
//#define pi 3.14159265358979323846
#define MINF -2000000000
//#define M  100005
#define K 28
using namespace std;
typedef long long ll;
//typedef long long ull;
const ll INF = 1.e18 + 2;
const ll mod = 1000000007;
const int MAXN = 100005;
using namespace std;
vector <string> v;
string s;
const int p = 203;
vector<ll> p_pow (MAXN);
vector <ll> h (MAXN);
int i1, j1, i2, j2;
void init() {
    p_pow[0] = 1;
    for (int i = 1; i < p_pow.size(); ++i) p_pow[i] = (p_pow[i-1] * p) % mod;
    for (int i = 0; i < s.length(); ++i) {
        h[i] = ( (s[i] - 'a' + 1) * p_pow[i] ) % mod;
        if (i) {
            h[i] += h[i-1];
            h[i] %= mod;
        }
    }
}
ll bin_pow(ll a, ll c) {
    if(c == 0) return 1LL;
    if(c % 2) return bin_pow(a, c - 1) * a % mod;
    else {
        ll res = bin_pow(a, c / 2);
        return res * res % mod;
    }
}
ll inverse(ll a) {
    return bin_pow(a, mod - 2);
}
int main() {
    //freopen("substrcmp.in", "r", stdin);
    //freopen("substrcmp.out", "w", stdout);
    cin >> s;
    int m; cin >> m;
    init();
    for(int i = 0; i < m; ++i) {
        scanf("%d%d%d%d", &i1, &j1, &i2, &j2);
        ll h1 = h[j1 - 1];
        if(i1 - 1) h1 -= h[i1 - 2];
        h1 += mod; h1 %= mod;
        h1 *= inverse(p_pow[i1 - 1] % mod) % mod; h1 %= mod;
        ll h2 = h[j2 - 1];
        if(i2 - 1) h2 -= h[i2 - 2];
        h2 += mod; h2 %= mod;
        h2 *= inverse(p_pow[i2 - 1] % mod) % mod; h2 %= mod;
       if(h1 == h2) printf("Yes\n");
       else printf("No\n");
    }
    return 0;
}


//Link to Boost
#define BOOST_TEST_DYN_LINK

//VERY IMPORTANT - include this last
//#include <boost/test/included/unit_test.hpp>
#include <boost/test/unit_test.hpp>

#include "test.h"
#include "../HomotopyContinuation.h"
#include "../BetheHomotopy.h"

// test suite
BOOST_FIXTURE_TEST_SUITE(Demo_suite, SimpleTestFixture, * utf::label("UnityStartSystem"))

/*
BOOST_AUTO_TEST_CASE(Demo2)
{
    SolvedBetheEquation start(6, 2);
    BetaHomotopy hom(0.5235, &start);
    //BetaHomotopy hom(0.2578, &start);
    //BetaHomotopy hom(0.25502, &start);
    HomotopyContinuation hc(1000);

    std::cout << "begin demo2" << std::endl;
    hc.solve(hom);
    for (int i = 0; i < hom.numberOfRoots(); i++) {
        Vector root = hom.getRoot(i);
        std::cout << "demo2 root=" << root << ", eval=" << hom.evalTarget(root) << std::endl;
    }
    std::cout << "end demo2" << std::endl;
}*/

var_t normalPhase(var_t phase) {
    if (phase.real() < .0) {
        phase += 2 * Pi;
    } else {
        phase -= floor(phase.real() / (2.0 * Pi)) * 2 * Pi;
    }
    return phase;
}

BOOST_AUTO_TEST_CASE(Demo3, * utf::disabled())
{
//    ExpandedBetaHomotopy hom(0.523599 * 2, &numerator, &denominator);
    elem_t beta = Pi;
    elem_t expectedMomentumDiff = .0;
    ExpandedBetaHomotopy hom(6, 2, beta);
    hom.setSteps(1000);
    BetheRootCache cache(6, 2);
    BetheHomotopyContinuation hc;

    std::cout << "begin demo3" << std::endl;
    for (int i = 0; i < cache.numberOfRoots(); i++) {
        std::string file = "BS_L6M2r" + ToString(i) + ".txt";
        hc.setTraceFile(file);
        Vector startRoot = cache.getRoot(i);
        InvertableSolution sol(startRoot, 6);
        hc.solve(hom, sol);
        elem_t m1 = momentum(startRoot);
        elem_t m2 = momentum(sol.normalRoot());
        BOOST_TEST_INFO("m1/Pi=" << m1/(elem_t)Pi << ", m2/Pi=" << m2/(elem_t)Pi);
        BOOST_TEST(std::abs(floatMod(m2 - m1, 2.0 * Pi) - expectedMomentumDiff) < 1e-4);
        std::cout << "from " << startRoot << std::endl;
        std::cout << "to " << sol.normalRoot() << ", error=" << hom.evalTarget(sol) << std::endl;
    }
    std::cout << "end demo3" << std::endl;
}

BOOST_AUTO_TEST_CASE(Demo4, * utf::disabled())
{
    ExpandedBetaHomotopy hom(6, 3, 0.5235);
    hom.setSteps(1000);
    BetheRootCache cache(6, 3);
    BetheHomotopyContinuation hc;

    std::cout << "begin demo4" << std::endl;
    for (int i = 0; i < cache.numberOfRoots(); i++) {
        std::string file = "BS_L6M3r" + ToString(i) + ".txt";
        hc.setTraceFile(file);
        Vector startRoot = cache.getRoot(i);
        InvertableSolution sol(startRoot, 6);
        hc.solve(hom, sol);
        std::cout << "startRoot=" << startRoot << std::endl;
//        BOOST_TEST_INFO("root=" << sol.normalRoot() << ", startRoot=" << startRoot);
//        BOOST_TEST((sol.normalRoot()-startRoot).norm() < 1e-3);
        std::cout << "root=" << sol.normalRoot() << ", eval=" << hom.evalTarget(sol).norm() << std::endl;
    }
    std::cout << "end demo4" << std::endl;
}

BOOST_AUTO_TEST_CASE(Demo5, * utf::disabled())
{
    ExpandedBetaHomotopy hom(7, 2, 0.4488);
    hom.setSteps(1000);
    BetheRootCache cache(7, 2);
    BetheHomotopyContinuation hc;

    std::cout << "begin demo5" << std::endl;
    for (int i = 0; i < cache.numberOfRoots(); i++) {
        std::string file = "BS_L7M2r" + ToString(i) + ".txt";
        hc.setTraceFile(file);
        Vector startRoot = cache.getRoot(i);
        InvertableSolution sol(startRoot, 7);
        hc.solve(hom, sol);
//        BOOST_TEST_INFO("root=" << sol.normalRoot() << ", startRoot=" << startRoot);
//        BOOST_TEST((sol.normalRoot() - startRoot).norm() < 1e-3);
        std::cout << "demo5 root=" << sol.normalRoot() << ", eval=" << hom.evalTarget(sol);
        std::cout << ", momentum="<< momentum(sol.normalRoot()) <<  std::endl;
        std::cout << "start root=" << startRoot;
        std::cout << ", momentum="<< momentum(startRoot) << std::endl << std::endl;
    }
    std::cout << "end demo5" << std::endl;
}

BOOST_AUTO_TEST_CASE(Demo6, * utf::disabled())
{
    int L = 6;
    int M = 2;
    int steps = 1000;
    elem_t totalPhase = Pi;
    elem_t expectedMomentumDiff = .0;
    PoleFreeBetheHomotopy hom(L, M, totalPhase);
    hom.setSteps(steps);
    BetheRootCache cache(L, M);
    BetheHomotopyContinuation hc;

    std::cout << "begin demo6" << std::endl;
    for (int i = 0; i < cache.numberOfRoots(); i++) {
        std::string file = "Demo6_BS_L" + ToString(L)+ "M" + ToString(M)+ "r" + ToString(i) + ".txt";
        hc.setTraceFile(file);
        Vector startRoot = cache.getRoot(i);
        BetheSolution sol(startRoot, L, 0.0, totalPhase / steps);
        hc.solve(hom, sol);
        elem_t m1 = momentum(startRoot);
        elem_t m2 = momentum(sol.normalRoot());
        BOOST_TEST_INFO("m1/Pi=" << m1/(elem_t)Pi << ", m2/Pi=" << m2/(elem_t)Pi);
        BOOST_TEST(std::abs(floatMod(m2 - m1, 2.0 * Pi) - expectedMomentumDiff) < 1e-4);
        std::cout << "from " << startRoot << std::endl;
        std::cout << "to " << sol.normalRoot() << ", error=" << hom.evalTarget(sol) << std::endl;
    }
    std::cout << "end demo6" << std::endl;
}

BOOST_AUTO_TEST_CASE(Demo7, * utf::disabled())
{
    int L = 6;
    int M = 3;
    int steps = 1000;
    elem_t totalPhase = Pi;
    elem_t expectedMomentumDiff = .0;
    PoleFreeBetheHomotopy hom(L, M, totalPhase);
    hom.setSteps(steps);
    BetheRootCache cache(L, M);
    BetheHomotopyContinuation hc;

    std::cout << "begin demo7" << std::endl;
    for (int i = 0; i < cache.numberOfRoots(); i++) {
        std::string file = "Demo7_BS_L" + ToString(L)+ "M" + ToString(M)+ "r" + ToString(i) + ".txt";
        hc.setTraceFile(file);
        Vector startRoot = cache.getRoot(i);
        BetheSolution sol(startRoot, L, 0.0, totalPhase / steps);
        hc.solve(hom, sol);
        elem_t m1 = momentum(startRoot);
        elem_t m2 = momentum(sol.normalRoot());
        BOOST_TEST_INFO("m1/Pi=" << m1/(elem_t)Pi << ", m2/Pi=" << m2/(elem_t)Pi);
        BOOST_TEST(std::abs(floatMod(m2 - m1, 2.0 * Pi) - expectedMomentumDiff) < 1e-4);
        std::cout << "from " << startRoot << std::endl;
        std::cout << "to " << sol.normalRoot() << ", error=" << hom.evalTarget(sol) << std::endl;
    }
    std::cout << "end demo7" << std::endl;
}

BOOST_AUTO_TEST_CASE(Demo8, * utf::disabled())
{
    int L = 7;
    int M = 2;
    int steps = 3000;
    elem_t totalPhase = Pi;
    elem_t expectedMomentumDiff = .0;
    PoleFreeBetheHomotopy hom(L, M, totalPhase);
    hom.setSteps(steps);
    BetheRootCache cache(L, M);
    BetheHomotopyContinuation hc;
    SET_LOG_LEVEL(Info);

    // i = 1, 3, 8 failed.
    // i = 1 fail at two identical infinity roots
    // i = 3 and 8 fail at two conjugate finite roots.
    std::cout << "begin demo8" << std::endl;
    for (int i = 1; i < cache.numberOfRoots() && i < 2; i++) {
        std::string file = "Demo8_BS_L" + ToString(L)+ "M" + ToString(M)+ "r" + ToString(i) + ".txt";
        hc.setTraceFile(file);
        Vector startRoot = cache.getRoot(i);
        BetheSolution sol(startRoot, L, 0.0, totalPhase / hom.getSteps());
        hc.solve(hom, sol);
        elem_t m1 = momentum(startRoot);
        elem_t m2 = momentum(sol.normalRoot());
        BOOST_TEST_INFO("i=" << i << ", m1/Pi=" << m1/(elem_t)Pi << ", m2/Pi=" << m2/(elem_t)Pi);
        BOOST_TEST(std::abs(floatMod(m2 - m1, 2.0 * Pi) - expectedMomentumDiff) < 1e-4);
        std::cout << "from " << startRoot << std::endl;
        std::cout << "to " << sol.normalRoot() << ", error=" << hom.evalTarget(sol) << std::endl;
    }
    std::cout << "end demo8" << std::endl;
    SET_LOG_LEVEL(Error);
}

BOOST_AUTO_TEST_CASE(Demo9, * utf::disabled())
{
    int L = 7;
    int M = 3;
    int steps = 6000;
    elem_t totalPhase = Pi;
    elem_t expectedMomentumDiff = .0;
    PoleFreeBetheHomotopy hom(L, M, totalPhase);
    hom.setSteps(steps);
    BetheRootCache cache(L, M);
    BetheHomotopyContinuation hc;

    // i = 0 fail at 2 identical infinite roots
    // i = 1 fail at 2 identical infinite roots
    // i = 4 fail at 2 conjugate finite roots
    // i=10 fail at 2 identical infinities
    // i = 11 fail at 2 identical infinities
    std::cout << "begin demo9" << std::endl;
    std::vector<bool> hasInfinity;
    for (int i = 6; i < cache.numberOfRoots() && i < 7; i++) {
        std::string file = "BS_L" + ToString(L)+ "M" + ToString(M)+ "r" + ToString(i) + ".txt";
        hc.setTraceFile(file);
        hc.hasInfinity = false;
        Vector startRoot = cache.getRoot(i);
        BetheSolution sol(startRoot, L, 0.0, totalPhase / steps);
        hc.solve(hom, sol);
        hasInfinity.push_back(hc.hasInfinity);
        elem_t m1 = momentum(startRoot);
        elem_t m2 = momentum(sol.normalRoot());
        BOOST_TEST_INFO("i=" << i << ", m1/Pi=" << m1/(elem_t)Pi << ", m2/Pi=" << m2/(elem_t)Pi);
        BOOST_TEST(std::abs(floatMod(m2 - m1, 2.0 * Pi) - expectedMomentumDiff) < 1e-4);
        std::cout << "from " << startRoot << std::endl;
        std::cout << "to " << sol.normalRoot() << ", error=" << hom.evalTarget(sol) << std::endl;
    }
    std::cout << hasInfinity << std::endl;
    std::cout << "end demo9" << std::endl;
}

BOOST_AUTO_TEST_CASE(Demo10a, * utf::disabled())
{
    int L = 8;
    int M = 3;
    elem_t totalPhase = random(1.0, 2.0) * Pi;
    elem_t expectedMomentumDiff = floatMod(2 * M * totalPhase, 2 * Pi);
    PoleFreeBetheHomotopy hom(L, M, totalPhase);
    BetheRootCache cache(L, M);
    BetheHomotopyContinuation hc;

    SET_LOG_LEVEL(Info);
    std::cout << "begin demo10a" << std::endl;
    std::vector<bool> hasInfinity;
    for (int i = 17; i < cache.numberOfRoots() && i < 18; i++) {
        std::string file = "BS_L" + ToString(L)+ "M" + ToString(M)+ "r" + ToString(i) + ".txt";
        hc.setTraceFile(file);
        hc.hasInfinity = false;
        Vector startRoot = cache.getRoot(i);
        BetheSolution sol(startRoot, L, 0.0, totalPhase / hom.getSteps());
        hc.solve(hom, sol);
        hasInfinity.push_back(hc.hasInfinity);
        elem_t m1 = momentum(startRoot);
        elem_t m2 = momentum(sol.normalRoot());
        BOOST_TEST_INFO("i=" << i << ", m1/Pi=" << m1/(elem_t)Pi << ", m2/Pi=" << m2/(elem_t)Pi);
        BOOST_TEST(std::abs(floatMod(m2 - m1, 2.0 * Pi) - expectedMomentumDiff) < 1e-4);
        std::cout << "from " << startRoot << std::endl;
        std::cout << "to " << sol.normalRoot() << ", error=" << hom.evalTarget(sol) << std::endl;
    }
    std::cout << hasInfinity << std::endl;
    std::cout << "end demo10" << std::endl;
}

BOOST_AUTO_TEST_CASE(Demo10, * utf::disabled())
{
    int L = 8;
    int M = 4;
    int steps = 3000;
    elem_t totalPhase = Pi;
    elem_t expectedMomentumDiff = .0;
    PoleFreeBetheHomotopy hom(L, M, totalPhase);
    hom.setSteps(steps);
    BetheRootCache cache(L, M);
    BetheHomotopyContinuation hc;

    // i = 5 four infinities, two pairs of conjugates
    // i = 6 three infinities, of which two are conjugates
    // i = 10 two identical infinities, two conjugates
    // i = 13 three infinities, of which two are conjugates
    std::cout << "begin demo10" << std::endl;
    std::vector<bool> hasInfinity;
    for (int i = 0; i < cache.numberOfRoots(); i++) {
        std::string file = "BS_L" + ToString(L)+ "M" + ToString(M)+ "r" + ToString(i) + ".txt";
        hc.setTraceFile(file);
        hc.hasInfinity = false;
        Vector startRoot = cache.getRoot(i);
        BetheSolution sol(startRoot, L, 0.0, totalPhase / steps);
        hc.solve(hom, sol);
        hasInfinity.push_back(hc.hasInfinity);
        elem_t m1 = momentum(startRoot);
        elem_t m2 = momentum(sol.normalRoot());
        BOOST_TEST_INFO("i=" << i << ", m1/Pi=" << m1/(elem_t)Pi << ", m2/Pi=" << m2/(elem_t)Pi);
        BOOST_TEST(std::abs(floatMod(m2 - m1, 2.0 * Pi) - expectedMomentumDiff) < 1e-4);
        std::cout << "from " << startRoot << std::endl;
        std::cout << "to " << sol.normalRoot() << ", error=" << hom.evalTarget(sol) << std::endl;
    }
    std::cout << hasInfinity << std::endl;
    std::cout << "end demo10" << std::endl;
}

BOOST_AUTO_TEST_CASE(Demo11, * utf::disabled())
{
    int L = 12;
    int M = 6;
    int steps = 2000;
    elem_t totalPhase = Pi;
    elem_t expectedMomentumDiff = .0;
    PoleFreeBetheHomotopy hom(L, M, totalPhase);
    hom.setSteps(steps);
    BetheRootCache cache(L, M);
    BetheHomotopyContinuation hc;

    // i = 7 two finite conjugates
    // i = 8 two finite conjugates, two identical finite roots
    // i = 9 two identical finite, two singulars
    // i = 10 two pairs of finite conjugates
    std::vector<int> failed;
    std::cout << "begin demo11" << std::endl;
    for (int i = 0; i < cache.numberOfRoots(); i++) {
        std::string file = "BS_L" + ToString(L)+ "M" + ToString(M)+ "r" + ToString(i) + ".txt";
        hc.setTraceFile(file);
        Vector startRoot = cache.getRoot(i);
        BetheSolution sol(startRoot, L, 0.0, totalPhase / steps);
        hc.solve(hom, sol);
        elem_t m1 = momentum(startRoot);
        elem_t m2 = momentum(sol.normalRoot());
        BOOST_TEST_INFO("i=" << i << ", m1/Pi=" << m1/(elem_t)Pi << ", m2/Pi=" << m2/(elem_t)Pi);
        BOOST_TEST(std::abs(floatMod(m2 - m1, 2.0 * Pi) - expectedMomentumDiff) < 1e-4);
        if (!(std::abs(floatMod(m2 - m1, 2.0 * Pi) - expectedMomentumDiff) < 1e-4)) {
            failed.push_back(i);
        }
        std::cout << "from " << startRoot << std::endl;
        std::cout << "to " << sol.normalRoot() << ", error=" << hom.evalTarget(sol) << std::endl;
    }
    std::cout << "Failed cases: " << failed << std::endl;
    std::cout << "end demo11" << std::endl;
}

BOOST_AUTO_TEST_SUITE_END()


//          Copyright Gavin Band 2008 - 2012.
// 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 RELATEDNESS_COMPONENT_PCA_PROJECTOR_HPP
#define RELATEDNESS_COMPONENT_PCA_PROJECTOR_HPP

#include <map>
#include <memory>
#include <boost/signals2/signal.hpp>
#include <Eigen/Core>
#include "genfile/CohortIndividualSource.hpp"
#include "genfile/SNPDataSourceProcessor.hpp"
#include "appcontext/UIContext.hpp"

namespace pca {
	struct PCAProjector: public genfile::SNPDataSourceProcessor::Callback
	{
	public:
		typedef Eigen::MatrixXd Matrix ;
		typedef Eigen::VectorXd Vector ;
		typedef std::auto_ptr< PCAProjector > UniquePtr ;

		static genfile::VariantEntry get_projection_name( std::size_t i ) ;

	public:
		static UniquePtr create(
			genfile::CohortIndividualSource const& samples,
			appcontext::UIContext& ui_context,
			genfile::VariantIdentifyingData::CompareFields const&
		) ;
		PCAProjector(
			genfile::CohortIndividualSource const& samples,
			appcontext::UIContext& ui_context,
			genfile::VariantIdentifyingData::CompareFields const&
		) ;

		void set_loadings(
			std::vector< genfile::VariantIdentifyingData > const& snps,
			Vector const& counts,
			Vector const& frequencies,
			Matrix const& loadings,
			std::vector< std::string > const& names
		) ;

		void begin_processing_snps( std::size_t number_of_samples, genfile::SNPDataSource::Metadata const& ) ;
		void processed_snp( genfile::VariantIdentifyingData const&, genfile::VariantDataReader& ) ;
		void end_processing_snps() ;

		typedef boost::function< genfile::VariantEntry ( std::size_t ) > GetNames ;
		typedef boost::signals2::signal< void( std::string, Eigen::MatrixXd const&, GetNames, GetNames ) > ResultSignal ;
		void send_results_to( ResultSignal::slot_type callback ) ;

		std::string get_metadata() const ;
	private:
		genfile::CohortIndividualSource const& m_samples ;
		appcontext::UIContext& m_ui_context ;
		typedef std::map< genfile::VariantIdentifyingData, int, genfile::VariantIdentifyingData::CompareFields > SnpMap ;
		SnpMap m_snps ;
		Eigen::VectorXd m_counts ;
		Eigen::VectorXd m_frequencies ;
		Eigen::MatrixXd m_loadings ;
		std::vector< std::string > m_names ;
		Eigen::VectorXd m_genotype_calls ;
		Eigen::VectorXd m_non_missingness ;
		Eigen::MatrixXd m_projections ;
		Eigen::VectorXd m_snps_visited_per_sample ;

		// m_visited keeps track of which SNPs have been used in the projection.
		typedef std::map< genfile::GenomePosition, int > VisitedSnpMap ;
		VisitedSnpMap m_visited ;
		std::size_t m_total_snps_visited ;
		ResultSignal m_result_signal ;

		void diagnose_projection() const ;
	} ;
}

#endif


//  (C) Copyright Nick Thompson 2017.
//  Use, modification and distribution are subject to the
//  Boost Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_MATH_SPECIAL_CHEBYSHEV_TRANSFORM_HPP
#define BOOST_MATH_SPECIAL_CHEBYSHEV_TRANSFORM_HPP
#include <cmath>
#include <type_traits>
#include <fftw3.h>
#include <boost/math/constants/constants.hpp>
#include <boost/math/special_functions/chebyshev.hpp>

#ifdef BOOST_HAS_FLOAT128
#include <quadmath.h>
#endif

namespace boost { namespace math {

namespace detail{

template <class T>
struct fftw_cos_transform;

template<>
struct fftw_cos_transform<double>
{
   fftw_cos_transform(int n, double* data1, double* data2)
   {
      plan = fftw_plan_r2r_1d(n, data1, data2, FFTW_REDFT10, FFTW_ESTIMATE);
   }
   ~fftw_cos_transform()
   {
      fftw_destroy_plan(plan);
   }
   void execute(double* data1, double* data2)
   {
      fftw_execute_r2r(plan, data1, data2);
   }
   static double cos(double x) { return std::cos(x); }
   static double fabs(double x) { return std::fabs(x); }
private:
   fftw_plan plan;
};

template<>
struct fftw_cos_transform<float>
{
   fftw_cos_transform(int n, float* data1, float* data2)
   {
      plan = fftwf_plan_r2r_1d(n, data1, data2, FFTW_REDFT10, FFTW_ESTIMATE);
   }
   ~fftw_cos_transform()
   {
      fftwf_destroy_plan(plan);
   }
   void execute(float* data1, float* data2)
   {
      fftwf_execute_r2r(plan, data1, data2);
   }
   static float cos(float x) { return std::cos(x); }
   static float fabs(float x) { return std::fabs(x); }
private:
   fftwf_plan plan;
};

template<>
struct fftw_cos_transform<long double>
{
   fftw_cos_transform(int n, long double* data1, long double* data2)
   {
      plan = fftwl_plan_r2r_1d(n, data1, data2, FFTW_REDFT10, FFTW_ESTIMATE);
   }
   ~fftw_cos_transform()
   {
      fftwl_destroy_plan(plan);
   }
   void execute(long double* data1, long double* data2)
   {
      fftwl_execute_r2r(plan, data1, data2);
   }
   static long double cos(long double x) { return std::cos(x); }
   static long double fabs(long double x) { return std::fabs(x); }
private:
   fftwl_plan plan;
};
#ifdef BOOST_HAS_FLOAT128
template<>
struct fftw_cos_transform<__float128>
{
   fftw_cos_transform(int n, __float128* data1, __float128* data2)
   {
      plan = fftwq_plan_r2r_1d(n, data1, data2, FFTW_REDFT10, FFTW_ESTIMATE);
   }
   ~fftw_cos_transform()
   {
      fftwq_destroy_plan(plan);
   }
   void execute(__float128* data1, __float128* data2)
   {
      fftwq_execute_r2r(plan, data1, data2);
   }
   static __float128 cos(__float128 x) { return cosq(x); }
   static __float128 fabs(__float128 x) { return fabsq(x); }
private:
   fftwq_plan plan;
};

#endif
}

template<class Real>
class chebyshev_transform
{
public:
    template<class F>
    chebyshev_transform(const F& f, Real a, Real b,
       Real tol = 500 * std::numeric_limits<Real>::epsilon(),
       size_t max_refinements = 16) : m_a(a), m_b(b)
    {
        if (a >= b)
        {
            throw std::domain_error("a < b is required.\n");
        }
        using boost::math::constants::half;
        using boost::math::constants::pi;
        using std::cos;
        using std::abs;
        Real bma = (b-a)*half<Real>();
        Real bpa = (b+a)*half<Real>();
        size_t n = 256;
        std::vector<Real> vf;

        size_t refinements = 0;
        while(refinements < max_refinements)
        {
            vf.resize(n);
            m_coeffs.resize(n);

            detail::fftw_cos_transform<Real> plan(static_cast<int>(n), vf.data(), m_coeffs.data());
            Real inv_n = 1/static_cast<Real>(n);
            for(size_t j = 0; j < n/2; ++j)
            {
                // Use symmetry cos((j+1/2)pi/n) = - cos((n-1-j+1/2)pi/n)
                Real y = detail::fftw_cos_transform<Real>::cos(pi<Real>()*(j+half<Real>())*inv_n);
                vf[j] = f(y*bma + bpa)*inv_n;
                vf[n-1-j]= f(bpa-y*bma)*inv_n;
            }

            plan.execute(vf.data(), m_coeffs.data());
            Real max_coeff = 0;
            for (auto const & coeff : m_coeffs)
            {
                if (detail::fftw_cos_transform<Real>::fabs(coeff) > max_coeff)
                {
                    max_coeff = detail::fftw_cos_transform<Real>::fabs(coeff);
                }
            }
            size_t j = m_coeffs.size() - 1;
            while (abs(m_coeffs[j])/max_coeff < tol)
            {
                --j;
            }
            // If ten coefficients are eliminated, the we say we've done all
            // we need to do:
            if (n - j > 10)
            {
                m_coeffs.resize(j+1);
                return;
            }

            n *= 2;
            ++refinements;
        }
    }

    inline Real operator()(Real x) const
    {
        return chebyshev_clenshaw_recurrence(m_coeffs.data(), m_coeffs.size(), m_a, m_b, x);
    }

    // Integral over entire domain [a, b]
    Real integrate() const
    {
          Real Q = m_coeffs[0]/2;
          for(size_t j = 2; j < m_coeffs.size(); j += 2)
          {
              Q += -m_coeffs[j]/((j+1)*(j-1));
          }
          return (m_b - m_a)*Q;
    }

    const std::vector<Real>& coefficients() const
    {
        return m_coeffs;
    }

    Real prime(Real x) const
    {
        Real z = (2*x - m_a - m_b)/(m_b - m_a);
        Real dzdx = 2/(m_b - m_a);
        if (m_coeffs.size() < 2)
        {
            return 0;
        }
        Real b2 = 0;
        Real d2 = 0;
        Real b1 = m_coeffs[m_coeffs.size() -1];
        Real d1 = 0;
        for(size_t j = m_coeffs.size() - 2; j >= 1; --j)
        {
            Real tmp1 = 2*z*b1 - b2 + m_coeffs[j];
            Real tmp2 = 2*z*d1 - d2 + 2*b1;
            b2 = b1;
            b1 = tmp1;

            d2 = d1;
            d1 = tmp2;
        }
        return dzdx*(z*d1 - d2 + b1);
    }

private:
    std::vector<Real> m_coeffs;
    Real m_a;
    Real m_b;
};

}}
#endif


// Copyright 2016 Peter Jankuliak
//
// 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.

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

#include <iostream>
#include "club/graph.h"
#include <club/debug/string_tools.h>

// -------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(graph) {
  using ID = unsigned int;
  using G = club::Graph<ID>;
  using IDs = std::set<ID>;

  {
    G g;

    g.nodes.insert(0);
    g.nodes.insert(1);

    auto removed = g.remove_unreachable_nodes(0);

    BOOST_CHECK(removed == IDs({1}));
    BOOST_CHECK(g.nodes == IDs({0}));
  }

  {
    G g;

    g.nodes.insert(0);
    g.nodes.insert(1);

    g.add_edge(0, 1);

    auto removed = g.remove_unreachable_nodes(0);

    BOOST_CHECK(removed == IDs({}));
    BOOST_CHECK(g.nodes == IDs({0, 1}));
  }

  {
    //       1      4
    //       |\    /|
    //       | 0--3 |
    //       |/    \|
    //       2      5
    G g;

    g.nodes.insert(0);
    g.nodes.insert(1);
    g.nodes.insert(2);
    g.nodes.insert(3);
    g.nodes.insert(4);
    g.nodes.insert(5);

    g.add_edge(0, 1);
    g.add_edge(1, 2);
    g.add_edge(2, 0);

    g.add_edge(3, 4);
    g.add_edge(4, 5);
    g.add_edge(5, 3);

    g.add_edge(1, 3);

    {
      auto removed = g.remove_unreachable_nodes(1);

      BOOST_CHECK(removed == IDs({}));
      BOOST_CHECK(g.nodes == IDs({0, 1, 2, 3, 4, 5}));
    }

    g.remove_edge(1, 3);

    {
      auto removed = g.remove_unreachable_nodes(1);

      BOOST_CHECK(removed == IDs({3, 4, 5}));
      BOOST_CHECK(g.nodes == IDs({0, 1, 2}));
    }
  }
}


//=======================================================================
// Copyright 1997-2001 University of Notre Dame.
// Authors: Andrew Lumsdaine, Lie-Quan Lee, Jeremy G. Siek
//
// This file is part of the Boost Graph Library
//
// You should have received a copy of the License Agreement for the
// Boost Graph Library along with the software; see the file LICENSE.
// If not, contact Office of Research, University of Notre Dame, Notre
// Dame, IN 46556.
//
// Permission to modify the code and to distribute modified code is
// granted, provided the text of this NOTICE is retained, a notice that
// the code was modified is included with the above COPYRIGHT NOTICE and
// with the COPYRIGHT NOTICE in the LICENSE file, and that the LICENSE
// file is distributed with the modified code.
//
// LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
// By way of example, but not limitation, Licensor MAKES NO
// REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
// PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE COMPONENTS
// OR DOCUMENTATION WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS
// OR OTHER RIGHTS.
//=======================================================================

#include <boost/config.hpp>
#include <iostream>
#include <vector>
#include <boost/graph/strong_components.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/graph/graph_utility.hpp>
/*
  Sample output:
  A directed graph:
  a --> b f h
  b --> c a
  c --> d b
  d --> e
  e --> d
  f --> g
  g --> f d
  h --> i
  i --> h j e c
  j -->

  Total number of components: 4
  Vertex a is in component 3
  Vertex b is in component 3
  Vertex c is in component 3
  Vertex d is in component 0
  Vertex e is in component 0
  Vertex f is in component 1
  Vertex g is in component 1
  Vertex h is in component 3
  Vertex i is in component 3
  Vertex j is in component 2
 */

int main(int, char*[])
{
  using namespace boost;
  const char* name = "abcdefghij";

  GraphvizDigraph G;
  read_graphviz("scc.dot", G);

  std::cout << "A directed graph:" << std::endl;
  print_graph(G, name);
  std::cout << std::endl;

  typedef graph_traits<GraphvizGraph>::vertex_descriptor Vertex;

  std::vector<int> component(num_vertices(G)), discover_time(num_vertices(G));
  std::vector<default_color_type> color(num_vertices(G));
  std::vector<Vertex> root(num_vertices(G));
  int num = strong_components(G, &component[0],
                              root_map(&root[0]).
                              color_map(&color[0]).
                              discover_time_map(&discover_time[0]));

  std::cout << "Total number of components: " << num << std::endl;
  std::vector<int>::size_type i;
  for (i = 0; i != component.size(); ++i)
    std::cout << "Vertex " << name[i]
         <<" is in component " << component[i] << std::endl;

  return 0;
}


// Copyright (C) 2015 National ICT Australia (NICTA)
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// -------------------------------------------------------------------
//
// Written by Conrad Sanderson - http://conradsanderson.id.au

#include <armadillo>
#include "catch.hpp"

using namespace arma;

TEST_CASE("fn_cross_1")
  {
  vec a = { 0.1,  2.3,  4.5 };
  vec b = { 6.7,  8.9, 10.0 };

  vec c = {-17.050, 29.150, -14.520 };

  REQUIRE( accu(abs(cross(a,b) - c)) == Approx(0.0) );

  vec x;

  REQUIRE_THROWS( x = cross(randu<vec>(4), randu<vec>(4)) );
  }