//------------------------------------------------------------------
//  **MEDYAN** - Simulation Package for the Mechanochemical
//               Dynamics of Active Networks, v4.0
//
//  Copyright (2015-2018)  Papoian Lab, University of Maryland
//
//                 ALL RIGHTS RESERVED
//
//  See the MEDYAN web page for more information:
//  http://www.medyan.org
//------------------------------------------------------------------

#include <time.h>
#include <random>
#include <math.h>
#include <algorithm>
#include <functional>
#include <numeric>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/io.hpp>
#include <boost/numeric/ublas/operation.hpp>
#include <boost/range/numeric.hpp>
#include <fstream>

#include "Filament.h"
#include "Cylinder.h"
#include "Bead.h"

#include "SubSystem.h"

#include "SysParams.h"
#include "MathFunctions.h"
#include "GController.h"
#include "Rand.h"

#ifdef CROSSCHECK_CYLINDER
#include "CController.h"
#endif

using namespace mathfunc;

Histogram* Filament::_turnoverTimes;

Filament::Filament(SubSystem* s, short filamentType, const vector<floatingpoint>& position,
                   const vector<floatingpoint>& direction, bool nucleation, bool branch)

    : Trackable(), _subSystem(s), _filType(filamentType) {

    if(getId() >= SysParams::Chemistry().maxStableIndex) {
        LOG(ERROR) << "Filament ID assigned ("<< getId()<<
                   ") equals/exceeds the maximum permissible value "
                   "("<<SysParams::Chemistry().maxStableIndex<<"). Exiting."<<endl;
        throw std::logic_error("Max value reached");
    }
    //create beads
    Bead* b1 = _subSystem->addTrackable<Bead>(position, this, 0);

    //choose length
    floatingpoint length = 0.0;

    if(branch)          length = SysParams::Geometry().monomerSize[_filType];
    else if(nucleation) length = SysParams::Geometry().minCylinderSize[_filType];

    auto pos2 = nextPointProjection(position, length, direction);

    Bead* b2 = _subSystem->addTrackable<Bead>(pos2, this, 1);

    //create cylindera
    Cylinder* c0 = _subSystem->addTrackable<Cylinder>(this, b1, b2, _filType, 0);

    c0->setPlusEnd(true);
    c0->setMinusEnd(true);
    _cylinderVector.push_back(c0);

    // set cylinder's filID
    c0->setFilID(getId());

    //set plus end marker
    _plusEndPosition = 1;

    // update reaction rates
    if(const auto& loadForceFunc = _subSystem->getCylinderLoadForceFunc()) {
        loadForceFunc(c0, ForceFieldTypes::LoadForceEnd::Plus);
        loadForceFunc(c0, ForceFieldTypes::LoadForceEnd::Minus);
        c0->updateReactionRates();
    }
}

Filament::Filament(SubSystem* s, short filamentType, const vector<vector<floatingpoint> >& position,
                   int numBeads, string projectionType)

    : Trackable(), _subSystem(s), _filType(filamentType) {

    //create a projection of beads
    vector<vector<floatingpoint>> tmpBeadsCoord;

    //straight projection
    if(projectionType == "STRAIGHT")
        tmpBeadsCoord = straightFilamentProjection(position, numBeads);
    //zigzag projection
    else if(projectionType == "ZIGZAG")
        tmpBeadsCoord = zigZagFilamentProjection(position, numBeads);
    //arc projection
    else if(projectionType == "ARC")
        tmpBeadsCoord = arcFilamentProjection(position, numBeads);
        //predefined projection aravind sep 9, 15
    else if(projectionType == "PREDEFINED")
        tmpBeadsCoord = predefinedFilamentProjection(position, numBeads);

    //create beads
    auto direction = twoPointDirection(tmpBeadsCoord[0], tmpBeadsCoord[1]);

    Bead* b1 = _subSystem->addTrackable<Bead>(tmpBeadsCoord[0], this, 0);
    Bead* b2 = _subSystem->addTrackable<Bead>(tmpBeadsCoord[1], this, 1);
    Cylinder* c0 = _subSystem->addTrackable<Cylinder>(this, b1, b2, _filType, 0,
                                                      false, false, true);

    c0->setPlusEnd(true);
    c0->setMinusEnd(true);
    _cylinderVector.push_back(c0);

    // set cylinder's filID
    c0->setFilID(getId());

    for (int i = 2; i<numBeads; i++)
        extendPlusEnd(tmpBeadsCoord[i]);

    //set plus end marker
    _plusEndPosition = numBeads - 1;

    // update reaction rates
    if(const auto& loadForceFunc = _subSystem->getCylinderLoadForceFunc()) {
        loadForceFunc(_cylinderVector.back(), ForceFieldTypes::LoadForceEnd::Plus);
        _cylinderVector.back()->updateReactionRates();
        loadForceFunc(_cylinderVector.front(), ForceFieldTypes::LoadForceEnd::Minus);
        _cylinderVector.front()->updateReactionRates();
    }
}

Filament::~Filament() {

    //remove cylinders, beads from system
    for(auto &c : _cylinderVector) {

        _subSystem->removeTrackable<Bead>(c->getFirstBead());

        if(c->isPlusEnd())
            _subSystem->removeTrackable<Bead>(c->getSecondBead());

        #ifdef CROSSCHECK_CYLINDER
        cout<<"RemoveTrackable Cylinder "<<c->getId()<<" "<<c->getStableIndex() <<endl;
        #endif
        _subSystem->removeTrackable<Cylinder>(c);
    }
}

//Extend front for initialization
void Filament::extendPlusEnd(vector<floatingpoint>& coordinates) {

    Cylinder* cBack = _cylinderVector.back();
    cBack->setPlusEnd(false);

    int lpf = cBack->getPosition();

    Bead* b2 = cBack->getSecondBead();

    //create a new bead
//    auto direction = twoPointDirection(b2->vcoordinate(), coordinates);
//    auto newBeadCoords = nextPointProjection(b2->vcoordinate(),
//    twoPointDistance(b2->vcoordinate(), coordinates), direction);
    auto newBeadCoords=coordinates;
    //create
    Bead* bNew = _subSystem->addTrackable<Bead>(newBeadCoords, this, b2->getPosition() + 1);
    Cylinder* c0 = _subSystem->addTrackable<Cylinder> (this, b2, bNew, _filType,
                                                       lpf + 1, false, false, true);
    c0->setPlusEnd(true);
    _cylinderVector.push_back(c0);

    // set cylinder's filID

    c0->setFilID(getId());

}

//Extend back for initialization
void Filament::extendMinusEnd(vector<floatingpoint>& coordinates) {

    Cylinder* cFront = _cylinderVector.front();
    cFront->setMinusEnd(false);

    int lpf = cFront->getPosition();

    Bead* b2 = cFront->getFirstBead();

    //create a new bead
    auto direction = twoPointDirection(b2->vcoordinate(), coordinates);
    auto newBeadCoords = nextPointProjection(b2->vcoordinate(),
    SysParams::Geometry().cylinderSize[_filType], direction);

    //create
    Bead* bNew = _subSystem->addTrackable<Bead>(newBeadCoords, this, b2->getPosition() - 1);
    Cylinder* c0 = _subSystem->addTrackable<Cylinder>(this, bNew, b2, _filType,
                                                  lpf - 1, false, false, true);
    c0->setMinusEnd(true);
    _cylinderVector.push_front(c0);

    // set cylinder's filID
    c0->setFilID(getId());

}

//Initialize for restart
void Filament::initializerestart(vector<Cylinder*> cylinderVector,
        vector<restartCylData>& _rCDatavec) {

    if(SysParams::RUNSTATE){
        LOG(ERROR) << "initializerestart Function from Filament class can only be called "
                      "during restart phase. Exiting.";
        throw std::logic_error("Illegal function call pattern");
    }
    if(_cylinderVector.size())
        cout<<_cylinderVector.size()<<endl;
    for(auto cyl:cylinderVector) {
        auto rcdata = _rCDatavec[cyl->getStableIndex()];
        cyl->initializerestart( rcdata.totalmonomers, rcdata.endmonomerpos[0],
                                rcdata.endmonomerpos[1], rcdata.endstatusvec[0],
                                rcdata.endstatusvec[1], rcdata.endtypevec[0],
                                rcdata.endtypevec[1]);
	    _cylinderVector.push_back(cyl);
    }

    //set plus end marker
    _plusEndPosition = getPlusEndCylinder()->getSecondBead()->getPosition();

	// update reaction rates
	if(const auto& loadForceFunc = _subSystem->getCylinderLoadForceFunc()) {
		loadForceFunc(_cylinderVector.back(), ForceFieldTypes::LoadForceEnd::Plus);
		_cylinderVector.back()->updateReactionRates();
		loadForceFunc(_cylinderVector.front(), ForceFieldTypes::LoadForceEnd::Minus);
		_cylinderVector.front()->updateReactionRates();
	}

}

//extend front at runtime
void Filament::extendPlusEnd(short plusEnd) {

    chrono::high_resolution_clock::time_point mins, mine;

    mins = chrono::high_resolution_clock::now();
    Cylinder* cBack = _cylinderVector.back();

    int lpf = cBack->getPosition();

    Bead* b1 = cBack->getFirstBead();
    Bead* b2 = cBack->getSecondBead();

    //move last bead of last cylinder forward
    auto direction1 = twoPointDirection(b1->vcoordinate(), b2->vcoordinate());

    auto npp = nextPointProjection(b2->vcoordinate(),
    SysParams::Geometry().monomerSize[_filType], direction1);

    mine = chrono::high_resolution_clock::now();
	chrono::duration<floatingpoint> elapsed_time1(mine - mins);
	FilextendPlusendtimer1 += elapsed_time1.count();

    //create a new bead in same place as b2
    Bead* bNew = _subSystem->addTrackable<Bead>(npp, this, b2->getPosition() + 1);

#ifdef MECHANICS
    //transfer the same load force to new bead
    //(approximation until next minimization)
    bNew->loadForcesP = b2->loadForcesP;
    bNew->lfip = b2->lfip + 1;
#endif

    Cylinder* c0 = _subSystem->addTrackable<Cylinder>(this, b2, bNew, _filType,
                                                      lpf + 1, true);

    mins = chrono::high_resolution_clock::now();
    _cylinderVector.back()->setPlusEnd(false);
    _cylinderVector.push_back(c0);
    _cylinderVector.back()->setPlusEnd(true);

    #ifdef CROSSCHECK_CYLINDER
    CController::_crosscheckdumpFilechem <<"PlusEnd cylinder set"<<endl;
    #endif

    // set cylinder's filID

    c0->setFilID(getId());

    #ifdef CROSSCHECK_CYLINDER
    CController::_crosscheckdumpFilechem <<"Cyl FilID set"<<endl;
    #endif

#ifdef CHEMISTRY
    //get last cylinder, mark species
    CMonomer* m = _cylinderVector.back()->getCCylinder()->getCMonomer(0);
    m->speciesPlusEnd(plusEnd)->up();
#endif
    #ifdef CROSSCHECK_CYLINDER
    CController::_crosscheckdumpFilechem <<"Species PlusEnd set"<<endl;
    #endif
#ifdef DYNAMICRATES
    //update reaction rates
    _cylinderVector.back()->updateReactionRates();
#endif
    #ifdef CROSSCHECK_CYLINDER
    CController::_crosscheckdumpFilechem <<"Reaction Rates updated"<<endl;
    #endif

    _deltaPlusEnd++;

/*    cout<<"Extend minus End Cylinder ID = "<<cBack->getId()<<endl;
    cBack->printSelf();*/

    mine = chrono::high_resolution_clock::now();
	chrono::duration<floatingpoint> elapsed_time2(mine - mins);
	FilextendPlusendtimer2 += elapsed_time2.count();
    #ifdef CROSSCHECK_CYLINDER
    CController::_crosscheckdumpFilechem <<"extendPlusend"<<endl;
    #endif
}

//extend back at runtime
void Filament::extendMinusEnd(short minusEnd) {

    Cylinder* cFront = _cylinderVector.front();
    int lpf = cFront->getPosition();

    Bead* b2 = cFront->getFirstBead();
    Bead* b1 = cFront->getSecondBead();

    //move last bead of last cylinder forward
    auto direction1 = twoPointDirection(b1->vcoordinate(), b2->vcoordinate());

    auto npp = nextPointProjection(b2->vcoordinate(),
    SysParams::Geometry().monomerSize[_filType], direction1);

    //create a new bead in same place as b2
    Bead* bNew = _subSystem->addTrackable<Bead>(npp, this, b2->getPosition() - 1);

#ifdef MECHANICS
    //transfer the same load force to new bead
    //(approximation until next minimization)
    bNew->loadForcesM = b2->loadForcesM;
    bNew->lfim = b2->lfim + 1;
#endif

    Cylinder* c0 = _subSystem->addTrackable<Cylinder>(this, bNew, b2, _filType,
                                                      lpf - 1, false, true);
    _cylinderVector.front()->setMinusEnd(false);
    _cylinderVector.push_front(c0);
    _cylinderVector.front()->setMinusEnd(true);

    #ifdef CROSSCHECK_CYLINDER
    CController::_crosscheckdumpFilechem <<"MinusEnd cylinder set"<<endl;
    #endif

    // set cylinder's filID

    c0->setFilID(getId());

#ifdef CHEMISTRY
    //get first cylinder, mark species
    auto newCCylinder = getCylinderVector().front()->getCCylinder();
    CMonomer* m = newCCylinder->getCMonomer(newCCylinder->getSize() - 1);

    m->speciesMinusEnd(minusEnd)->up();
#ifdef CROSSCHECK_CYLINDER
    CController::_crosscheckdumpFilechem <<"MinusEnd species set"<<endl;
#endif

#endif

#ifdef DYNAMICRATES
    //update reaction rates
    _cylinderVector.front()->updateReactionRates();
#endif
    #ifdef CROSSCHECK_CYLINDER
    CController::_crosscheckdumpFilechem <<"Reaction rates updated"<<endl;
    #endif
    _deltaMinusEnd++;

/*    cout<<"Extend plus End Cylinder ID = "<<cFront->getId()<<endl;
    cFront->printSelf();*/
    #ifdef CROSSCHECK_CYLINDER
    CController::_crosscheckdumpFilechem <<"extendMinusend"<<endl;
    #endif
}

//Depolymerize front at runtime
void Filament::retractPlusEnd() {

    Cylinder* retCylinder = _cylinderVector.back();

/*    cout<<"Ret plus End Cylinder ID = "<<retCylinder->getId()<<endl;
    retCylinder->printSelf();*/

    _cylinderVector.pop_back();

#ifdef MECHANICS
    //transfer load forces
    Bead* bd = _cylinderVector.back()->getSecondBead();
    bd->loadForcesP = retCylinder->getSecondBead()->loadForcesP;
    bd->lfip = retCylinder->getSecondBead()->lfip - 1;
#endif

    _subSystem->removeTrackable<Bead>(retCylinder->getSecondBead());
    removeChild(retCylinder->getSecondBead());
    #ifdef CROSSCHECK_CYLINDER
    cout<<"RemoveTrackable Cylinder "<<retCylinder->getId()<<" "
                                                             ""<<retCylinder->getStableIndex()<<endl;
    #endif
    _subSystem->removeTrackable<Cylinder>(retCylinder);
    removeChild(retCylinder);

    _cylinderVector.back()->setPlusEnd(true);

#ifdef DYNAMICRATES
    //update rates of new front
    _cylinderVector.back()->updateReactionRates();
#endif

    _deltaPlusEnd--;

    #ifdef CROSSCHECK_CYLINDER
    CController::_crosscheckdumpFilechem <<"retractPlusend"<<endl;
    #endif
}

void Filament::retractMinusEnd() {

    Cylinder* retCylinder = _cylinderVector.front();

/*    cout<<"Ret minus End Cylinder ID = "<<retCylinder->getId()<<endl;
    retCylinder->printSelf();*/

    _cylinderVector.pop_front();

#ifdef MECHANICS
    //transfer load forces
    Bead* bd = _cylinderVector.front()->getFirstBead();
    bd->loadForcesM = retCylinder->getFirstBead()->loadForcesM;
    bd->lfim = retCylinder->getFirstBead()->lfim - 1;
#endif

    _subSystem->removeTrackable<Bead>(retCylinder->getFirstBead());
    removeChild(retCylinder->getFirstBead());
    #ifdef CROSSCHECK_CYLINDER
    cout<<"RemoveTrackable Cylinder "<<retCylinder->getId()<<" "<<retCylinder->getStableIndex() <<endl;
    #endif
    _subSystem->removeTrackable<Cylinder>(retCylinder);
    removeChild(retCylinder);

    _cylinderVector.front()->setMinusEnd(true);

#ifdef DYNAMICRATES
    //update rates of new back
    _cylinderVector.front()->updateReactionRates();
#endif

    _deltaMinusEnd--;

    ///If filament has turned over, mark as such
    if(_plusEndPosition == getMinusEndCylinder()->getFirstBead()->getPosition()) {

        //reset
        _plusEndPosition = getPlusEndCylinder()->getSecondBead()->getPosition();
        _turnoverTime = tau();
    }
    #ifdef CROSSCHECK_CYLINDER
    CController::_crosscheckdumpFilechem <<"retractMinusEnd"<<endl;
    #endif
}

void Filament::polymerizePlusEnd() {

    Cylinder* cBack = _cylinderVector.back();

    Bead* b1 = cBack->getFirstBead();
    Bead* b2 = cBack->getSecondBead();

    auto direction = twoPointDirection(b1->vcoordinate(), b2->vcoordinate());

    b2->coordinate() = vector2Vec<3, floatingpoint>(nextPointProjection(b2->vcoordinate(),
    SysParams::Geometry().monomerSize[_filType], direction));

    // Update cylinder data
    cBack->getCoordinate() = (floatingpoint)0.5 * (b1->coordinate() + b2->coordinate());

#ifdef MECHANICS
    //increment load
    b2->lfip++;

    //increase eq length, update
    floatingpoint newEqLen = cBack->getMCylinder()->getEqLength() +
                      SysParams::Geometry().monomerSize[_filType];
    cBack->getMCylinder()->setEqLength(_filType, newEqLen);
#endif

#ifdef DYNAMICRATES
    //update rates of new back
    _cylinderVector.back()->updateReactionRates();
#endif

    _polyPlusEnd++;

/*    cout<<"Poly plus End Cylinder ID = "<<cBack->getId()<<endl;
    cBack->printSelf();*/
    #ifdef CROSSCHECK_CYLINDER
    CController::_crosscheckdumpFilechem <<"polymerizePlusend"<<endl;
    #endif

}

void Filament::polymerizeMinusEnd() {

    Cylinder* cFront = _cylinderVector.front();

    Bead* b1 = cFront->getFirstBead();
    Bead* b2 = cFront->getSecondBead();

    auto direction = twoPointDirection(b2->vcoordinate(), b1->vcoordinate());

    b1->coordinate() = vector2Vec<3, floatingpoint>(nextPointProjection(b1->vcoordinate(),
    SysParams::Geometry().monomerSize[_filType], direction));

    // Update cylinder data
    cFront->getCoordinate() = (floatingpoint)0.5 * (b1->coordinate() + b2->coordinate());

#ifdef MECHANICS

    //increment load
    b1->lfim++;

    //increase eq length, update
    floatingpoint newEqLen = cFront->getMCylinder()->getEqLength() +
                      SysParams::Geometry().monomerSize[_filType];
    cFront->getMCylinder()->setEqLength(_filType, newEqLen);
#endif

#ifdef DYNAMICRATES
    //update rates of new back
    _cylinderVector.front()->updateReactionRates();
#endif

    _polyMinusEnd++;

/*    cout<<"Poly minus End Cylinder ID = "<<cFront->getId()<<endl;
    cFront->printSelf();*/
    #ifdef CROSSCHECK_CYLINDER
    CController::_crosscheckdumpFilechem <<"polymerizeMinusend"<<endl;
    #endif

}

void Filament::depolymerizePlusEnd() {

    Cylinder* cBack = _cylinderVector.back();

    Bead* b1 = cBack->getFirstBead();
    Bead* b2 = cBack->getSecondBead();

    auto direction = twoPointDirection(b2->vcoordinate(), b1->vcoordinate());

    b2->coordinate() = vector2Vec<3, floatingpoint>(nextPointProjection(b2->vcoordinate(),
    SysParams::Geometry().monomerSize[_filType], direction));

    // Update cylinder data
    cBack->getCoordinate() = (floatingpoint)0.5 * (b1->coordinate() + b2->coordinate());

#ifdef MECHANICS

    //increment load
    b2->lfip--;

    //decrease eq length, update
    floatingpoint newEqLen = cBack->getMCylinder()->getEqLength() -
                      SysParams::Geometry().monomerSize[_filType];
    cBack->getMCylinder()->setEqLength(_filType, newEqLen);
#endif
#ifdef DYNAMICRATES
    //update rates of new back
    _cylinderVector.front()->updateReactionRates();
#endif

    _depolyPlusEnd++;

/*    cout<<"DePoly plus End Cylinder ID = "<<cBack->getId()<<endl;
    cBack->printSelf();*/
    #ifdef CROSSCHECK_CYLINDER
    CController::_crosscheckdumpFilechem <<"depolymerizePlusend"<<endl;
    #endif

}

void Filament::depolymerizeMinusEnd() {

    Cylinder* cFront = _cylinderVector.front();

    Bead* b1 = cFront->getFirstBead();
    Bead* b2 = cFront->getSecondBead();

    auto direction = twoPointDirection(b1->vcoordinate(), b2->vcoordinate());

    b1->coordinate() = vector2Vec<3, floatingpoint>(nextPointProjection(b1->vcoordinate(),
    SysParams::Geometry().monomerSize[_filType], direction));

    // Update cylinder data
    cFront->getCoordinate() = (floatingpoint)0.5 * (b1->coordinate() + b2->coordinate());

#ifdef MECHANICS

    b1->lfim--;

    //decrease eq length, update
    floatingpoint newEqLen = cFront->getMCylinder()->getEqLength() -
                      SysParams::Geometry().monomerSize[_filType];
    cFront->getMCylinder()->setEqLength(_filType, newEqLen);
#endif

#ifdef DYNAMICRATES
    //update rates of new back
    _cylinderVector.front()->updateReactionRates();
#endif

    _depolyMinusEnd++;
/*    cout<<"DePoly minus End Cylinder ID = "<<cFront->getId()<<endl;
    cFront->printSelf();*/
    #ifdef CROSSCHECK_CYLINDER
    CController::_crosscheckdumpFilechem <<"depolymerizeMinusend"<<endl;
    #endif
}

void Filament::nucleate(short plusEnd, short filament, short minusEnd) {

#ifdef CHEMISTRY
    //chemically initialize species
    CCylinder* cc = _cylinderVector[0]->getCCylinder();
    int monomerPosition = SysParams::Geometry().cylinderNumMon[_filType] / 2 + 1;

    CMonomer* m1 = cc->getCMonomer(monomerPosition - 1);
    CMonomer* m2 = cc->getCMonomer(monomerPosition);
    CMonomer* m3 = cc->getCMonomer(monomerPosition + 1);

    //minus end
    m1->speciesMinusEnd(minusEnd)->up();

    //filament
    m2->speciesFilament(filament)->up();

    for(auto j : SysParams::Chemistry().bindingIndices[_filType])
        m2->speciesBound(j)->up();

    //plus end
    m3->speciesPlusEnd(plusEnd)->up();
#endif

    _nucleationReaction++;

}

Filament* Filament::sever(int cylinderPosition) {

    int vectorPosition = 0;

    //loop through cylinder vector, find position
    for(auto &c : _cylinderVector) {

        if(c->getPosition() == cylinderPosition) break;
        else vectorPosition++;
    }

    //if vector position is zero, we can't sever. return null
    if(vectorPosition == 0) return nullptr;

#ifdef CHEMISTRY
    //if any of the cylinders are only one monomer long, we can't sever
    CCylinder* ccBack  = _cylinderVector[vectorPosition - 1]->getCCylinder();
    CMonomer* cm = ccBack->getCMonomer(ccBack->getSize() - 1);

    if(cm->activeSpeciesMinusEnd() != -1) return nullptr;
#endif

    //create a new filament
    Filament* newFilament = _subSystem->addTrackable<Filament>(_subSystem, _filType);

    //Split the cylinder vector at position, transfer cylinders to new filament
    for(int i = vectorPosition; i > 0; i--) {

        Cylinder* c = _cylinderVector.front();
        _cylinderVector.pop_front();

        newFilament->_cylinderVector.push_back(c);

        //TRANSFER CHILD
        unique_ptr<Component> &&tmp = this->getChild(c);
        this->transferChild(std::move(tmp), (Composite*)newFilament);

        //Add beads and cylinder to new parent
        if(i == vectorPosition) {
            unique_ptr<Component> &&tmp2 = this->getChild(c->getFirstBead());
            this->transferChild(std::move(tmp2), (Composite*)newFilament);
        }
        unique_ptr<Component> &&tmp1 = this->getChild(c->getSecondBead());
        this->transferChild(std::move(tmp1), (Composite*)newFilament);
    }
    //new front of new filament, back of old
    auto c1 = newFilament->_cylinderVector.back();
    auto c2 = _cylinderVector.front();

    ///copy bead at severing point, attach to new filament
    Bead* newB = _subSystem->addTrackable<Bead>(*(c2->getFirstBead()));
    Bead* oldB = c2->getFirstBead();

    //offset these beads by a little for safety
    auto msize = SysParams::Geometry().monomerSize[_filType];

    vector<floatingpoint> offsetCoord =
    {(Rand::randInteger(0,1) ? -1 : +1) * Rand::randfloatingpoint(msize, 2 * msize),
     (Rand::randInteger(0,1) ? -1 : +1) * Rand::randfloatingpoint(msize, 2 * msize),
     (Rand::randInteger(0,1) ? -1 : +1) * Rand::randfloatingpoint(msize, 2 * msize)};

    oldB->coordinate()[0] += offsetCoord[0];
    oldB->coordinate()[1] += offsetCoord[1];
    oldB->coordinate()[2] += offsetCoord[2];

    newB->coordinate()[0] += -offsetCoord[0];
    newB->coordinate()[1] += -offsetCoord[1];
    newB->coordinate()[2] += -offsetCoord[2];

    //add bead
    c1->setSecondBead(newB);
    newFilament->addChild(unique_ptr<Component>(newB));

    //set plus and minus ends
    c1->setPlusEnd(true);
    c2->setMinusEnd(true);

#ifdef CHEMISTRY
    //mark the plus and minus ends of the new and old filament
    CCylinder* cc1 = c1->getCCylinder();
    CCylinder* cc2 = c2->getCCylinder();

    CMonomer* m1 = cc1->getCMonomer(cc1->getSize() - 1);
    CMonomer* m2 = cc2->getCMonomer(0);

    short filamentInt1 = m1->activeSpeciesFilament();
    short filamentInt2 = m2->activeSpeciesFilament();

    //plus end
    m1->speciesFilament(filamentInt1)->down();
    m1->speciesPlusEnd(filamentInt1)->up();

    for(auto j : SysParams::Chemistry().bindingIndices[_filType])
        m1->speciesBound(j)->down();

    //minus end
    m2->speciesFilament(filamentInt2)->down();
    m2->speciesMinusEnd(filamentInt2)->up();

    for(auto j : SysParams::Chemistry().bindingIndices[_filType])
        m2->speciesBound(j)->down();

    //remove any cross-cylinder rxns between these two cylinders
    cc1->removeCrossCylinderReactions(cc2);
    cc2->removeCrossCylinderReactions(cc1);
#endif
    _severingReaction++;
    _severingID.push_back(newFilament->getId());
    return newFilament;
}

vector<vector<floatingpoint>> Filament::straightFilamentProjection(const vector<vector<floatingpoint>>& v, int numBeads) {

    vector<vector<floatingpoint>> coordinate;
    vector<floatingpoint> tmpVec (3, 0);
    vector<floatingpoint> tau (3, 0);
    floatingpoint invD = 1/twoPointDistance(v[1], v[0]);
    tau[0] = invD * ( v[1][0] - v[0][0] );
    tau[1] = invD * ( v[1][1] - v[0][1] );
    tau[2] = invD * ( v[1][2] - v[0][2] );

    for (int i = 0; i<numBeads; i++) {

        tmpVec[0] = v[0][0] + SysParams::Geometry().cylinderSize[_filType] * i * tau[0];
        tmpVec[1] = v[0][1] + SysParams::Geometry().cylinderSize[_filType] * i * tau[1];
        tmpVec[2] = v[0][2] + SysParams::Geometry().cylinderSize[_filType] * i * tau[2];

        coordinate.push_back(tmpVec);
    }
    return coordinate;
}

vector<vector<floatingpoint>> Filament::zigZagFilamentProjection(const vector<vector<floatingpoint>>& v, int numBeads){

    vector<vector<floatingpoint>> coordinate;
    vector<floatingpoint> tmpVec (3, 0);
    vector<floatingpoint> tau (3, 0);
    floatingpoint invD = 1/twoPointDistance(v[1], v[0]);
    tau[0] = invD * ( v[1][0] - v[0][0] );
    tau[1] = invD * ( v[1][1] - v[0][1] );
    tau[2] = invD * ( v[1][2] - v[0][2] );

    vector<floatingpoint> perptau = {-tau[1], tau[0], tau[2]};

    for (int i = 0; i<numBeads; i++) {

        if(i%2 == 0) {
            tmpVec[0] = v[0][0] + SysParams::Geometry().cylinderSize[_filType] * i * tau[0];
            tmpVec[1] = v[0][1] + SysParams::Geometry().cylinderSize[_filType] * i * tau[1];
            tmpVec[2] = v[0][2] + SysParams::Geometry().cylinderSize[_filType] * i * tau[2];
        }
        else {
            tmpVec[0] = v[0][0] + SysParams::Geometry().cylinderSize[_filType] * i * perptau[0];
            tmpVec[1] = v[0][1] + SysParams::Geometry().cylinderSize[_filType] * i * perptau[1];
            tmpVec[2] = v[0][2] + SysParams::Geometry().cylinderSize[_filType] * i * perptau[2];
        }

        coordinate.push_back(tmpVec);
    }
    return coordinate;
}

/// Create a projection
/// @note - created by Aravind 12/2014
void marsagila(vector<floatingpoint>&v) {

    floatingpoint d1,d2,d3;
    floatingpoint *x=new floatingpoint[3];
    d1=2*Rand::randfloatingpoint(0,1)-1;
    d2=2*Rand::randfloatingpoint(0,1)-1;
    d3=pow(d1,2)+pow(d2,2);

    while(d3>=1) {
        d1=2*Rand::randfloatingpoint(0,1)-1;
        d2=2*Rand::randfloatingpoint(0,1)-1;
        d3=pow(d1,2)+pow(d2,2);
    }

    x[0]=2.0*d1*pow((1.0-d3),0.5);
    x[1]=2.0*d2*pow(1.0-d3,0.5);
    x[2]=1.0-2.0*d3;
    v[0]=2.0*d1*pow((1.0-d3),0.5);
    v[1]=2.0*d2*pow(1.0-d3,0.5);
    v[2]=1.0-2.0*d3;
}

/// Matrix multiply
/// @note - created by Aravind 12/2014
void matrix_mul(boost::numeric::ublas::matrix<floatingpoint>&X,
                boost::numeric::ublas::matrix<floatingpoint>&Y,
                boost::numeric::ublas::matrix<floatingpoint>&Z,
                vector<floatingpoint>&x,vector<floatingpoint>&y,
                vector<floatingpoint>&z,int nbeads,
                vector<vector<floatingpoint>> &coordinate) {

    int t,i;
    floatingpoint dt,length,cyl_length,sum;
    vector<int> id;
    vector<floatingpoint> dx,dy,dz,dx2,dy2,dz2,length2,dxdy2,dummyy(3);
    using namespace boost::numeric::ublas;
    matrix<floatingpoint> B(4,4),B2(1,4),temp1(4,1),dummy(1,1),temp2(4,1),temp3(4,1);

    // B
    B(0,0)=1; B(0,1)=0; B(0,2)=0; B(0,3)=0;
    B(1,0)=-3; B(1,1)=3; B(1,2)=0; B(1,3)=0;
    B(2,0)=3; B(2,1)=-6; B(2,2)=3; B(2,3)=0;
    B(3,0)=-1; B(3,1)=3; B(3,2)=-3; B(3,3)=1;

    axpy_prod(B,X,temp1);
    axpy_prod(B,Y,temp2);
    axpy_prod(B,Z,temp3);
    B2(0,0)=1;

    for(t=0;t<=4000;t++) {
        dt=0.00025*t;
        B2(0,1)=dt;
        B2(0,2)=dt*dt;
        B2(0,3)=dt*dt*dt;
        axpy_prod(B2,temp1,dummy);
        x.push_back(dummy(0,0));
        axpy_prod(B2,temp2,dummy);
        y.push_back(dummy(0,0));
        axpy_prod(B2,temp3,dummy);
        z.push_back(dummy(0,0));
    }

    adjacent_difference(x.begin(),x.end(),back_inserter(dx));//dx
    adjacent_difference(y.begin(),y.end(),back_inserter(dy));//dy
    adjacent_difference(z.begin(),z.end(),back_inserter(dz));//dz

    transform(dx.begin(), dx.end(),dx.begin(),
              back_inserter(dx2), multiplies<floatingpoint>());
    transform(dy.begin(), dy.end(),dy.begin(),
              back_inserter(dy2), multiplies<floatingpoint>());
    transform(dz.begin(), dz.end(),dz.begin(),
              back_inserter(dz2), multiplies<floatingpoint>());

    //array of sum(dx^2+dy^2)
    transform(dx2.begin(),dx2.end(),dy2.begin(),
              back_inserter(dxdy2),plus<floatingpoint>());
    //array of sum(dx^2+dy^2+dz^2)
    transform(dxdy2.begin(),dxdy2.end(),dz2.begin(),
              back_inserter(length2),plus<floatingpoint>());

    std::vector<floatingpoint> tempLength;
    for(auto x: length2) tempLength.push_back(sqrt(x));
    length2 = tempLength; length2[0]=0.0;

    length = boost::accumulate(length2, 0.0);//arc length.

    //making equal divisions.
    i=0;sum=0.0;id.push_back(0.0);
    cyl_length=length/(nbeads-1);

    while(i<=4000) {
        sum+=length2[i];
        if(sum>=cyl_length||i==4000) {
            id.push_back(i);
            sum=0.0;
        }
        i++;
    }

    for(i=0;i<id.size();i++) {
        dummyy[0]=x[id[i]];
        dummyy[1]=y[id[i]];
        dummyy[2]=z[id[i]];
        coordinate.push_back(dummyy);
    }
}

void arcOutward(vector<floatingpoint>&v1,vector<floatingpoint>&v2, const vector<vector<floatingpoint>>&v) {

    vector<floatingpoint> center,tempv1,tempv2,temp2,temp3(3),
                   temp4(3),mid,mid2(3),mid3(3),temp5;

    center = GController::getCenter();

    // point v[0]
    temp3[0]=v[0][0];
    temp3[1]=v[0][1];
    temp3[2]=v[0][2];

    //point v[1]
    temp4[0]=v[1][0];
    temp4[1]=v[1][1];
    temp4[2]=v[1][2];

    mid=midPointCoordinate(temp3, temp4, 0.5);
    mid2=midPointCoordinate(mid, temp4, 0.5);
    mid3=midPointCoordinate(mid, temp3, 0.5);

    //vector between v[1] and center stored in tempv1
    std::transform(v[1].begin(), v[1].end(), center.begin(),
                   std::back_inserter(tempv1), std::minus<floatingpoint>());

    floatingpoint dist=twoPointDistance(center, temp4);
    dist=300/dist;

    std::transform(tempv1.begin(),tempv1.end(),tempv1.begin(),
                   [&](auto x) { return x * dist; });
    std::transform(mid.begin(), mid.end(), tempv1.begin(),
                   std::back_inserter(v1), std::plus<floatingpoint>());

    //vector between v[0] and center stored in tempv2
    std::transform(v[0].begin(), v[0].end(), center.begin(),
                   std::back_inserter(tempv2), std::minus<floatingpoint>());

    dist=twoPointDistance(center, temp3);
    dist=100/dist;

    std::transform(tempv2.begin(),tempv2.end(),tempv2.begin(),
                   [&](auto x) { return x * dist; });
    std::transform(mid3.begin(), mid3.end(), tempv2.begin(),
                   std::back_inserter(v2), std::plus<floatingpoint>());
}

vector<vector<floatingpoint>> Filament::arcFilamentProjection(const vector<vector<floatingpoint>>& v, int numBeads) {

    using namespace boost::numeric::ublas;

    std::vector<floatingpoint> X3,x3(3),x4(3),X4,x,y,z;
    matrix<floatingpoint> C(3,3),B(4,4),X(4,1),Y(4,1),Z(4,1);
    std::vector< std::vector<floatingpoint> > coordinates;

    arcOutward(X3,X4,v);
    X(0,0)=v[0][0]; X(1,0)=X3[0]; X(2,0)=X4[0]; X(3,0)=v[1][0];
    Y(0,0)=v[0][1]; Y(1,0)=X3[1]; Y(2,0)=X4[1]; Y(3,0)=v[1][1];
    Z(0,0)=v[0][2]; Z(1,0)=X3[2]; Z(2,0)=X4[2]; Z(3,0)=v[1][2];
    //
    matrix_mul(X,Y,Z,x,y,z,numBeads,coordinates);
    return coordinates;
}
// predefined projection
vector<vector<floatingpoint>> Filament::predefinedFilamentProjection(const vector<vector<floatingpoint>>& v, int numBeads) {
    return v;
}
//@
void Filament::printSelf()const {

    cout << endl;

    cout << "Filament: ptr = " << this << endl;
    cout << "Filament ID = " << getId() << endl;
    cout << "Filament type = " << _filType << endl;

    cout << endl;
    cout << "Cylinder information..." << endl;

    for(auto c : _cylinderVector)
        c->printSelf();

    cout << endl;

}

bool Filament::isConsistent() {

#ifdef CHEMISTRY
    //check consistency of each individual cylinder
    for(auto &c : _cylinderVector) {
        if(!c->getCCylinder()->isConsistent()) {

            cout << "Cylinder at position " << c->getPosition()
                 << " is chemically inconsistent" << endl;
            return false;
        }
    }

    //check that it only has one plus end and one minus end
    int numPlusEnd = 0;
    int numMinusEnd = 0;

    for(auto &c : _cylinderVector) {

        for(int i = 0; i < c->getCCylinder()->getSize(); i++) {
            auto m = c->getCCylinder()->getCMonomer(i);

            if(m->activeSpeciesPlusEnd() != -1) numPlusEnd++;
            if(m->activeSpeciesMinusEnd() != -1) numMinusEnd++;
        }
    }
    if(numPlusEnd != 1) {
        cout << "This filament has more than one plus end species." << endl;
        return false;
    }
    if(numMinusEnd != 1) {
        cout << "This filament has more than one minus end species." << endl;
        return false;
    }

#endif
    return true;
}

species_copy_t Filament::countSpecies(short filamentType, const string& name) {

    species_copy_t copyNum = 0;

    for(auto f : getElements()) {

        if(f->getType() != filamentType) continue;

        //loop through the filament
        for(auto c : f->_cylinderVector) {

            for(int i = 0; i < c->getCCylinder()->getSize(); i++) {
                auto m = c->getCCylinder()->getCMonomer(i);

                //filament species
                int activeIndex = m->activeSpeciesFilament();

                if(activeIndex != -1) {

                    auto s = m->speciesFilament(activeIndex);
                    string sname = SpeciesNamesDB::removeUniqueFilName(s->getName());

                    if(sname == name)
                        copyNum += s->getN();

                    continue;
                }

                //plus end species
                activeIndex = m->activeSpeciesPlusEnd();

                if(activeIndex != -1) {

                    auto s = m->speciesPlusEnd(activeIndex);
                    string sname = SpeciesNamesDB::removeUniqueFilName(s->getName());

                    if(sname == name)
                        copyNum += s->getN();

                    continue;
                }

                //minus end species
                activeIndex = m->activeSpeciesMinusEnd();

                if(activeIndex != -1) {

                    auto s = m->speciesMinusEnd(activeIndex);
                    string sname = SpeciesNamesDB::removeUniqueFilName(s->getName());

                    if(sname == name)
                        copyNum += s->getN();

                    continue;
                }

            }
        }
    }
    return copyNum;
}

floatingpoint Filament::FilextendPlusendtimer1 = 0.0;
floatingpoint Filament::FilextendPlusendtimer2 = 0.0;
floatingpoint Filament::FilextendPlusendtimer3 = 0.0;
floatingpoint Filament::FilextendMinusendtimer1 = 0.0;
floatingpoint Filament::FilextendMinusendtimer2 = 0.0;
floatingpoint Filament::FilextendMinusendtimer3 = 0.0;


// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
//
// 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/.

#include "main.h"

#include <Eigen/CXX11/Tensor>

using Eigen::Tensor;
using Eigen::RowMajor;

static void test_assign()
{
  float data1[6];
  TensorMap<Tensor<const float, 2>> mat1(data1, 2, 3);
  float data2[6];
  const TensorMap<Tensor<float, 2>> mat2(data2, 2, 3);

  for (int i = 0; i < 6; ++i) {
    data1[i] = i;
    data2[i] = -i;
  }

  Tensor<float, 2> rslt1;
  rslt1 = mat1;
  Tensor<float, 2> rslt2;
  rslt2 = mat2;

  Tensor<float, 2> rslt3 = mat1;
  Tensor<float, 2> rslt4 = mat2;

  Tensor<float, 2> rslt5(mat1);
  Tensor<float, 2> rslt6(mat2);

  for (int i = 0; i < 2; ++i) {
    for (int j = 0; j < 3; ++j) {
      VERIFY_IS_APPROX(rslt1(i,j), static_cast<float>(i + 2*j));
      VERIFY_IS_APPROX(rslt2(i,j), static_cast<float>(-i - 2*j));
      VERIFY_IS_APPROX(rslt3(i,j), static_cast<float>(i + 2*j));
      VERIFY_IS_APPROX(rslt4(i,j), static_cast<float>(-i - 2*j));
      VERIFY_IS_APPROX(rslt5(i,j), static_cast<float>(i + 2*j));
      VERIFY_IS_APPROX(rslt6(i,j), static_cast<float>(-i - 2*j));
    }
  }
}

static void test_plus()
{
  float data1[6];
  TensorMap<Tensor<const float, 2>> mat1(data1, 2, 3);
  float data2[6];
  TensorMap<Tensor<float, 2>> mat2(data2, 2, 3);

  for (int i = 0; i < 6; ++i) {
    data1[i] = i;
    data2[i] = -i;
  }

  Tensor<float, 2> sum1;
  sum1 = mat1 + mat2;
  Tensor<float, 2> sum2;
  sum2 = mat2 + mat1;

  for (int i = 0; i < 2; ++i) {
    for (int j = 0; j < 3; ++j) {
      VERIFY_IS_APPROX(sum1(i,j), 0.0f);
      VERIFY_IS_APPROX(sum2(i,j), 0.0f);
    }
  }
}

static void test_plus_equal()
{
  float data1[6];
  TensorMap<Tensor<const float, 2>> mat1(data1, 2, 3);
  float data2[6];
  TensorMap<Tensor<float, 2>> mat2(data2, 2, 3);

  for (int i = 0; i < 6; ++i) {
    data1[i] = i;
    data2[i] = -i;
  }
  mat2 += mat1;

  for (int i = 0; i < 2; ++i) {
    for (int j = 0; j < 3; ++j) {
      VERIFY_IS_APPROX(mat2(i,j), 0.0f);
    }
  }
}

void test_cxx11_tensor_of_const_values()
{
  CALL_SUBTEST(test_assign());
  CALL_SUBTEST(test_plus());
  CALL_SUBTEST(test_plus_equal());
}


/* copyright Grant Rostig (c)2019 see LICENSE file */
#include <iostream>
#include <iomanip>
#include <string>
#include <string_view>

#include <map>
#include <unordered_map>
#include <iterator>
#include <random>
#include <thread>
#include <algorithm>
#include <functional>
#include <future>
#include <cassert>
#include <stdexcept>
#include <jthread.hpp>  // WARNING c++20 experimental, but std:: namespace anyway!
#include <boost/math/special_functions/binomial.hpp>
//#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
//#include <tr1/cmath>  // to pick up beta()
#include <eigen3/Eigen/Dense>
using std::cout;
using std::cerr;
using std::cin;
using std::endl;

namespace grostig  {

//   * Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013.
//   ISBN: 978-0-321-56384-2. Chapter 11.5: Explicit type conversion. page 299.
template <class Target, class Source>
Target narrow_cast_runtime(Source v)
{
  auto r = static_cast<Target>(v);  // todo: probably some undefined behavior with float/double
  if (static_cast<Source>(r)!=v)
    throw std::runtime_error("narrow_cast<>() failed");
  return r;
}

class thread_guard
{
    std::thread& t;
public:
    explicit thread_guard(std::thread& t_):
        t(t_)
    {}
    ~thread_guard()
    {
        if(t.joinable())
        {
            t.join();
        }
    }
    thread_guard(thread_guard const&)=delete;
    thread_guard& operator=(thread_guard const&)=delete;
};

/* std::cout << "Pascal's triangle:\n";  // may be wrong.
for(int n = 1; n < 10; ++n) {
    std::cout << std::string(20-n*2, ' ');
    for(int k = 1; k < n; ++k)
        std::cout << std::setw(3) << binomial_coefficient_nCk(n,k) << ' ';
    std::cout << '\n';
}
double binomial_coefficient_nCk(int const n, int const k) {  // THIS IS partly WRONG, use boost.
    if (n == 0 || n == k) return 1;
    return 1/ ((n+1) * std::tr1::beta( n-k+1, k+1 ));
} */

}
std::string         PGM_NAME               {"binomial"};
constexpr size_t    CPU_CORES_QTY =        8;
constexpr size_t    THREADS_MIN =          2;
constexpr size_t    THREADS_MAX =          CPU_CORES_QTY;
constexpr size_t    MAX_ASYNC_FUTURES =    CPU_CORES_QTY;
constexpr size_t    BINOMIAL_TRIALS_MAX =  1000;  // bernouli trials max number of them.
constexpr size_t    TRIALS_ARRAY_SZ =      (BINOMIAL_TRIALS_MAX+1)*THREADS_MAX;
using Histogram = std::array< size_t, BINOMIAL_TRIALS_MAX >;     // todo : gets quite large...
using Histogram_Parallel = std::array< size_t, TRIALS_ARRAY_SZ >;     // todo : gets quite large...

void boost_log_init() {  // BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
        boost::log::core::get()->set_filter( boost::log::trivial::severity >= boost::log::trivial::info);
}

auto threads_on_hardware( size_t const operations_qty_total, size_t const operations_qty_min,
                          size_t const threads_min, size_t const threads_max ) {
    assert( threads_min <= threads_max);
    if (operations_qty_min >= operations_qty_total) {
        return std::tuple< size_t,size_t,size_t > {threads_min, operations_qty_total, operations_qty_total };
    }
    size_t threads_hardware_qty = std::thread::hardware_concurrency();
    size_t threads_calc_max = (operations_qty_total + operations_qty_min-1 ) / operations_qty_min;  // truncation is desired
    size_t threads_qty { std::min( { 0 != threads_hardware_qty
                                        ? threads_hardware_qty
                                        : threads_min
                                        , threads_calc_max
                                        , threads_max } ) };
    size_t chunk_operations_qty = operations_qty_total / threads_qty;  // truncation is desired.
    size_t chunk_operations_qty_mod = operations_qty_total % threads_qty;  // number with the extra samples.
    return std::tuple< size_t,size_t,size_t > {threads_qty, chunk_operations_qty, chunk_operations_qty_mod};
}

double binomial_distribution_probability_mass_function( size_t const n_trials, size_t const k_successes, double const probability_of_success) {
    double bc =           boost::math::binomial_coefficient<double>( 5, 2);
    double p_k =          std::pow(probability_of_success, k_successes);
    errno = 0;
    double p_complement = std::pow(1 - probability_of_success, n_trials - k_successes);
    if ( 0 != errno ) { std::perror("binomial:ERROR: pow() failed."); }  // todo: what type do I need to use PGM_NAME
    return bc * p_k * p_complement;         // todo:  what happens if this cal overflows?  How do we check for that?
}

double print_binomial_dist_PMF(size_t const n_samples_drawn, size_t const k_successes, double const mu_probability_in_bin)
{
    try {
    auto r1 = binomial_distribution_probability_mass_function(n_samples_drawn, k_successes,  mu_probability_in_bin);
    cout << "binomial_probability_mass_function(n_trials,k_successes,p): " << n_samples_drawn << ", " << k_successes << ", " << mu_probability_in_bin << endl;
    cout << r1 << endl;
    return r1;
    } catch (std::exception & e) { cerr<<PGM_NAME+":binomial_rel_freq_histogram:error exception: "<<e.what()<<endl; throw;
    } catch (...) { cerr<<PGM_NAME+":binomial_rel_freq_histogram:error unknown exception: "<<endl; throw; };
}

void binomial_rel_freq_histogram_map(size_t const trials, double const p_success, size_t const num_samplings ) {
    try {
    std::random_device rd;
    std::mt19937 gen(rd());
    std::binomial_distribution<size_t> dist(trials, p_success);
    std::map<size_t, size_t> histogram;
    cout << "Probability Mass/Density Function Graph - Binomial Distribution with trials, likelyhood: " << trials << ", " << p_success << std::endl;
    for (size_t n = 0; n < num_samplings; ++n) {
        ++histogram[dist(gen)];
    }
    for (auto p : histogram) {
        cout << std::setw(4) << p.first << ' '
                  << std::setw(10) << p.second/static_cast<double>(num_samplings) << ' '
                  << '\n';
    }
    } catch (std::exception & e) { cerr<<PGM_NAME+":binomial_rel_freq_histogram:error exception: "<<e.what()<<endl; throw;
    } catch (...) { cerr<<PGM_NAME+":binomial_rel_freq_histogram:error unknown exception: "<<endl; throw; };
}

void binomial_rel_freq_histogram_vector(size_t const trials, double const p_success, size_t const num_samplings ) {
    try {
    std::random_device rd;
    std::mt19937 gen( rd() );
    std::binomial_distribution<size_t> dist( trials, p_success );
    std::vector<size_t> histogram( trials+1, 0);  // create it with full size, init with 0
    cout << "Probability Mass/Density Function Graph - Binomial Distribution with trials, likelyhood: " << trials << ", " << p_success << std::endl;
    for (size_t n = 0; n < num_samplings; ++n) {
        ++histogram[ static_cast<size_t>( dist(gen) )];
    }
    size_t i = 0;
    for (auto p : histogram) {
        cout << std::setw(4) << i++ << ' '
                  << std::setw(10) << p/grostig::narrow_cast_runtime<double>(num_samplings) << ' '
                  << '\n';
    }
    //    std::copy(histogram.begin(), histogram.end(), std::ostream_iterator<int>( cout, " \n" ) );
    } catch (std::exception & e) { cerr<<PGM_NAME+":binomial_rel_freq_histogram:error exception: "<<e.what()<<endl; throw;
    } catch (...) { cerr<<PGM_NAME+":binomial_rel_freq_histogram:error unknown exception: "<<endl; throw; };
}

class Sample_the_distribution_fo {
private:
    Histogram_Parallel & histogram_pll;                     // pll = parallel // this is located at begining of the class for cache performance
public:                                                     // todo: data layout for cache performance works for functions too?
    void operator()()=delete;
    void operator()(size_t const offset_, bool const is_do_one_more = false) {
        try {
        offset =                    offset_;
//        std::random_device          rd;
//        std::mt19937                gen ( rd() );
//        std::binomial_distribution<unsigned long>  dist { trials, p_success };  // assumed not to be thread-safe
//        gen( rd() );

        size_t                      current_samples { is_do_one_more ? chunk_sample_qty+1 : chunk_sample_qty };  // this loop takes care of one of the additional (mod remainder) operations that are required.
        if (!p1_done) {current_samples = chunk_sample_qty_p1; p1_done = true; };
        for (unsigned j=0; j < current_samples; ++j) {
            ++histogram_pll[ static_cast<size_t>( dist(gen) ) + offset ];
        }
        } catch (std::exception & e) { cerr<<PGM_NAME+":binomial_rel_freq_histogram:error exception: "<<e.what()<<endl; throw;
        } catch (...) { cerr<<PGM_NAME+":binomial_rel_freq_histogram:error unknown exception: "<<endl; throw; };
    }
private:
    size_t offset {};
    size_t chunk_sample_qty_p1 {};  // number with the extra samples
    size_t chunk_sample_qty {};
    size_t trials {};
    double p_success {};
    bool   p1_done { false };  // one set of samples has to do the number with the extra samples
    //  @OFFICER_TUBA: if only the following line is uncommented: compile error on no matching function? inititializer list?
    //                 unless it is marked static!
    static std::random_device           rd;     // todo: NOT USED yet!
    std::binomial_distribution<size_t>  dist( rd() );   // todo: NOT USED yet!
    std::mt19937                        gen;    // todo: NOT USED yet!
public:
    Sample_the_distribution_fo()=delete;
                        //        : histogram_pll(histogram_), chunk_sample_qty_p1(chunk_sample_qty_p1_), chunk_sample_qty(chunk_sample_qty_), trials(trials_), p_success(p_success_) {}
    explicit Sample_the_distribution_fo(Histogram_Parallel & histogram_,
                               size_t const chunk_sample_qty_p1_, size_t const chunk_sample_qty_, size_t const trials_, double const p_success_)
        : histogram_pll(histogram_)
    {
                                            // histogram = histogram_;      // todo: NOTE: compiler wants this initialization to be done on the "member intializer list", probably because it is a "ref", why?
        chunk_sample_qty_p1 = chunk_sample_qty_p1_;
        chunk_sample_qty = chunk_sample_qty_;
        trials = trials_;
        p_success = p_success_;
        //        std::mt19937                gen ( (this->rd)() );                           // todo: NOT USED yet!
        //        std::binomial_distribution<unsigned long>  dist ( trials, p_success );      // todo: NOT USED yet!
        //                  cout << "Probability Mass/Density Function Graph - Binomial Distribution with trials, likelyhood: " << trials << ", " << p_success << std::endl;
    }
};

Histogram sample_the_distribution_a( size_t const num_samplings, size_t const trials, double const p_success) {
    try {
    Histogram histogram;                                    // todo: data layout?
    std::random_device          rd;
    std::mt19937                gen ( rd() );
    std::binomial_distribution<unsigned long>  dist { trials , p_success };
    for (unsigned j=0; j < num_samplings; ++j) {
        ++histogram[ static_cast<size_t>( dist(gen) ) ];
    }
    return histogram;
    } catch (std::exception & e) { cerr<<PGM_NAME+":binomial_rel_freq_histogram:error exception: "<<e.what()<<endl; throw;
    } catch (...) { cerr<<PGM_NAME+":binomial_rel_freq_histogram:error unknown exception: "<<endl; throw; };
};

/* void binomial_rel_freq_histogram_array_async(size_t trials, double const p_success, size_t const num_samplings ) {
    assert( 1 <= trials && trials <= MAX_BINOMIAL_TRIALS );
    assert( !(0.0 > p_success || p_success > 1.0) );  // between 0 and 1 ie. [0,1]
    try {
    cout << "Probability Mass/Density Function Graph - Binomial Distribution with trials, likelyhood: " << trials << ", " << p_success << std::endl;
    //    std::random_device                          rd;                           // todo: NOT USED yet! would need to thread-safe the distribution.
    //    std::mt19937                                gen( rd() );                  // todo: NOT USED yet!
    //    std::binomial_distribution<size_t>          dist( trials, p_success );    // todo: NOT USED yet!
    Histogram histogram;
    size_t                              num_trial_values      { trials+1 };  // include the case for zero successes within the set of trials.
    std::vector<std::future<Histogram>> futures;
    for (size_t chunk = 0; chunk < MAX_ASYNC_FUTURES; ++chunk)
    {
        std::future<Histogram> future_histogram = std::async( sample_the_distribution_a, num_samplings, trials, p_success);
        futures.push_back( future_histogram );
    }
    std::for_each(futures.begin(), futures.end(), // for (auto this_t : threads) {  // todo: why not?
                  std::mem_fn( &std::thread::join ));
    for (auto ff:futures ) {  // don't add the first chuck (ie. = 0) to itself!
        Histogram histogram_partial = futures.pop_back().get();
        for (size_t my_trial = 0; my_trial < num_trial_values; my_trial++) {
            histogram[my_trial] += histogram[my_trial];
        }
    }
    for (size_t i = 0; i < num_trial_values; ++i ) {
        cout << std::setw(4) << i << ' ' << std::setw(10) << histogram[i]/static_cast<double>(num_samplings) << ' '<< '\n';
    }
} catch (std::exception & e) { cerr<<PGM_NAME+":binomial_rel_freq_histogram:error exception: "<<e.what()<<endl;
} catch (...) { cerr<<PGM_NAME+":binomial_rel_freq_histogram:error unknown exception: "<<endl; throw; };
 }
} */

void binomial_rel_freq_histogram_array_thread(size_t trials, double const p_success, size_t const samplings_qty ) {
    assert( 1 <= trials && trials <= BINOMIAL_TRIALS_MAX );
    assert( !(0.0 > p_success || p_success > 1.0) );  // between 0 and 1 ie. [0,1]
    try {  // sorry no indent for whole function.

    cout << "Probability Mass/Density Function Graph - Binomial Distribution with trials, likelyhood: " << trials << ", " << p_success << std::endl;
    //    std::random_device                          rd;                           // todo: NOT USED yet! would need to thread-safe the distribution.
    //    std::mt19937                                gen( rd() );                  // todo: NOT USED yet!
    //    std::binomial_distribution<size_t>          dist( trials, p_success );    // todo: NOT USED yet!
    Histogram_Parallel              histogram;                  // this is at the top of the function for performance.
    // size_t const                    chunk_sample_quantity { num_samplings / MAX_THREADS };  // todo: truncation error, off by one error
    size_t const                    num_trial_values      { trials+1 };  // include the case for zero successes within the set of trials.
    size_t const                    chunk_stride          { num_trial_values };
    size_t const                    samplings_qty_min     { 10'000 };
    size_t                          chunk_offset          { 0 };
    auto [threads_qty, chunk_sample_quantity, chunk_sample_qty_mod]
            = threads_on_hardware( samplings_qty, samplings_qty_min, THREADS_MIN, THREADS_MAX);

    std::vector<std::jthread>        threads;
    Sample_the_distribution_fo sample_chunk_fo2 {histogram, chunk_sample_quantity, chunk_sample_qty_mod, trials, p_success};  // histogram is a reference, so we need to join within this containing function.
    // must verify that we have more than one thread
    for (size_t chunk = 0; chunk < threads_qty-1; ++chunk)        //  *** Parallel Section ***
    {
        try {
            bool is_do_one_more {false};
            if ( 0 != chunk_sample_qty_mod && chunk_sample_qty_mod-- > 0) is_do_one_more = true;  // iff we have a mod qty, then do_one and decrement.
            std::jthread t = std::jthread( sample_chunk_fo2, chunk_offset, is_do_one_more );
            threads.push_back( std::move(t) );
        } catch (...) { cerr << PGM_NAME+":fatal error: thread creation failed\n"<<endl; throw; }; // don't need thread guard due to vector. todo: even with thowing vector will be destructed and so also threads?
        chunk_offset = chunk_offset + chunk_stride;
    }
    assert( 0 == chunk_sample_qty_mod );
    sample_chunk_fo2( chunk_offset /* is_do_one_more = false (default)*/);  // do put the last thread on the main thread.
    std::for_each(threads.begin(), threads.end(),               // *** BARRIER *** //for (auto this_t : threads) {  // todo: why not?
                  std::mem_fn( &std::jthread::join ));
    chunk_offset = chunk_stride;
    for (size_t thread = 0; thread < threads_qty; ++thread) {   // *** Combine Results *** // don't add the first chuck (ie. = 0) to itself!
        size_t trial_offset {0};
        for (size_t my_trial = 0; my_trial < num_trial_values; my_trial++) {
            trial_offset = chunk_offset + my_trial;
            histogram[my_trial] += histogram[trial_offset];
        }
        chunk_offset = chunk_offset + chunk_stride;
    }
    for (size_t i = 0; i < num_trial_values; ++i ) {
        cout << std::setw(4) << i << ' ' << std::setw(10) << histogram[i]/grostig::narrow_cast_runtime<double>(samplings_qty) << ' '<< '\n';
    }
    } catch (std::exception & e) { cerr<<PGM_NAME+":binomial_rel_freq_histogram:error exception: "<<e.what()<<endl; throw;
    } catch (...) { cerr<<PGM_NAME+":binomial_rel_freq_histogram:error unknown exception: "<<endl; throw; };
}

void poisson_distribution_values(double const mean, long int const num_values = 100 ) { // creates a vector loaded with values from the distribution during its initialization.
    try {
    std::random_device rd;
    std::mt19937 gen( rd() ); // generator

    std::poisson_distribution<int> dist( mean /*4.1*/);       // http://eigen.tuxfamily.org/bz/show_bug.cgi?id=720
    auto poisson = [&dist, &gen] (size_t) { return dist( gen ); };
    Eigen::RowVectorXi values = Eigen::RowVectorXi::NullaryExpr(num_values, poisson );
    std::cout << "Eigen::RowVextorXi:Poisson( mean ): (" << mean << "), "  << values << "\n";
    } catch (std::exception & e) { cerr<<PGM_NAME+":binomial_rel_freq_histogram:error exception: "<<e.what()<<endl; throw;
    } catch (...) { cerr<<PGM_NAME+":binomial_rel_freq_histogram:error unknown exception: "<<endl; throw; };
}

void binomial_distribution_values(size_t const trials, double const p_success, long int const num_values = 100 ) { // creates a vector loaded with values from the distribution during its initialization.
    try {
    std::random_device rd;
    std::mt19937 gen( rd() ); // generator

    std::binomial_distribution<size_t> dist( trials, p_success );       // http://eigen.tuxfamily.org/bz/show_bug.cgi?id=720
    auto binomial = [&dist, &gen] (size_t) { return dist( gen ); };
    Eigen::RowVectorXi values = Eigen::RowVectorXi::NullaryExpr(num_values, binomial );
    std::cout << "Eigen::RowVextorXi:Binomial( trials, p_success ): (" << trials <<", "<< p_success <<"), "  << values << "\n";
    } catch (std::exception & e) { cerr<<PGM_NAME+":binomial_rel_freq_histogram:error exception: "<<e.what()<<endl; throw;
    } catch (...) { cerr<<PGM_NAME+":binomial_rel_freq_histogram:error unknown exception: "<<endl; throw; };
}

int main()
{
    try {
        size_t trials = 10;
        constexpr size_t  num_samplings     = 100'000'000;            // c++17
        //    binomial_rel_freq_histogram_map(    trials, 0.5, num_samplings );
        //    binomial_rel_freq_histogram_vector( trials, 0.7, num_samplings );
        binomial_rel_freq_histogram_array_thread(  trials, 1.0, num_samplings );

        poisson_distribution_values( 4.1, 100 );
        binomial_distribution_values( trials, 0.5, 100 );

        cout << "nCk(5,5): "<<boost::math::binomial_coefficient<double>(5,5)<<endl
             << "nCk(5,2): "<< boost::math::binomial_coefficient<double>(5,2)<<endl
             << "nCk(10'000,2): "<< boost::math::binomial_coefficient<double>(10000,2)<<endl;

        double mu_probability_in_bin        = 0.9;
        size_t n_samples_drawn              = 10;
        double nu_probability_of_sample     = 0.1;
        size_t k_successes = grostig::narrow_cast_runtime<size_t>( lround( nu_probability_of_sample * n_samples_drawn ));
        auto r1 = print_binomial_dist_PMF( n_samples_drawn, k_successes, mu_probability_in_bin );

        mu_probability_in_bin               = 0.9;
        n_samples_drawn                     = 10;
        nu_probability_of_sample            = 0;
        k_successes = grostig::narrow_cast_runtime<size_t>( lround( nu_probability_of_sample * n_samples_drawn ));
        auto r2 = print_binomial_dist_PMF( n_samples_drawn, k_successes, mu_probability_in_bin );
        cout << r1+r2 << endl;

        mu_probability_in_bin               = 0.9;
        n_samples_drawn                     = 10;
        nu_probability_of_sample            = 0.4;
        k_successes = grostig::narrow_cast_runtime<size_t>( lround( nu_probability_of_sample * n_samples_drawn ));
        r1 = print_binomial_dist_PMF( n_samples_drawn, k_successes, mu_probability_in_bin );

        mu_probability_in_bin               = 0.6;
        n_samples_drawn                     = 10;
        nu_probability_of_sample            = 0;
        k_successes = grostig::narrow_cast_runtime<size_t>( lround( nu_probability_of_sample * n_samples_drawn ));
        r1 = print_binomial_dist_PMF( n_samples_drawn, k_successes, mu_probability_in_bin );

        std::cout << "###" << std::endl;
        return 0;
    } catch (std::exception & e) {
        cerr << PGM_NAME+": exception error: "<<e.what()<<endl;
        return 1;
    } catch (...) {
        cerr << PGM_NAME+": unknown exception error.\n";
        return 2;
    }
}


/*

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

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

This file is part of Chaste.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.
 * Neither the name of the University of Oxford nor the names of its
   contributors may be used to endorse or promote products derived from this
   software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef TESTCELLCYCLEMODELODESOLVER_HPP_
#define TESTCELLCYCLEMODELODESOLVER_HPP_

#include <cxxtest/TestSuite.h>

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <cmath>

#include "CellCycleModelOdeSolver.hpp"

#include "TysonNovakCellCycleModel.hpp"
#include "Alarcon2004OxygenBasedCellCycleModel.hpp"
#include "OutputFileHandler.hpp"
#include "RungeKutta4IvpOdeSolver.hpp"
#include "BackwardEulerIvpOdeSolver.hpp"
#include "CvodeAdaptor.hpp"
#include "OdeSystemInformation.hpp"
#include "AbstractCellBasedTestSuite.hpp"
//This test is always run sequentially (never in parallel)
#include "FakePetscSetup.hpp"

/**
 * Simple ODE system for use in the test suite. Defines the
 * IVP dy/dt = 1, y(0) = 0.
 */
class SimpleOde : public AbstractOdeSystem
{
public:
    SimpleOde() : AbstractOdeSystem(1) // 1 here is the number of variables
    {
        mpSystemInfo = OdeSystemInformation<SimpleOde>::Instance();
        ResetToInitialConditions();
    }

    void EvaluateYDerivatives(double time, const std::vector<double>& rY, std::vector<double>& rDY)
    {
        rDY[0] = 1.0;
    }
};

template<>
void OdeSystemInformation<SimpleOde>::Initialise()
{
    this->mVariableNames.push_back("Variable_1");
    this->mVariableUnits.push_back("Units_1");
    this->mInitialConditions.push_back(0.0);

    this->mInitialised = true;
}

/**
 * Simple ODE system with stopping event for use in the test suite.
 * Defines the IVP dx/dt = y, dy/dt = -x, x(0) = 1, y(0) = 0.
 * Solutions to this system form circles about the origin. The
 * stopping event is x(0) < 0, which should first occur at time
 * t = pi/2.
 */
class OdeSecondOrderWithEvents : public AbstractOdeSystem
{
public:
    OdeSecondOrderWithEvents() : AbstractOdeSystem(2)
    {
        mpSystemInfo = OdeSystemInformation<OdeSecondOrderWithEvents>::Instance();
    }

    void EvaluateYDerivatives(double time, const std::vector<double>& rY, std::vector<double>& rDY)
    {
        rDY[0] =  rY[1];
        rDY[1] = -rY[0];
    }

    bool CalculateStoppingEvent(double time, const std::vector<double>& rY)
    {
        return (rY[0] < 0);
    }
};

template<>
void OdeSystemInformation<OdeSecondOrderWithEvents>::Initialise()
{
    this->mVariableNames.push_back("Variable_1");
    this->mVariableUnits.push_back("dimensionless");
    this->mInitialConditions.push_back(1.0);

    this->mVariableNames.push_back("Variable_2");
    this->mVariableUnits.push_back("dimensionless");
    this->mInitialConditions.push_back(0.0);

    this->mInitialised = true;
}

class TestCellCycleModelOdeSolver : public AbstractCellBasedTestSuite
{
public:

    void TestMethods()
    {
        typedef CellCycleModelOdeSolver<TysonNovakCellCycleModel,RungeKutta4IvpOdeSolver> RkSolver;

        // Check we can create an instance
        boost::shared_ptr<RkSolver> p_solver = RkSolver::Instance();
        TS_ASSERT(p_solver.get() != NULL);

        // Check singleton-ness
        boost::shared_ptr<RkSolver> p_solver2 = RkSolver::Instance();
        TS_ASSERT_EQUALS(p_solver, p_solver2);

        // Coverage
        TS_ASSERT_THROWS_NOTHING(p_solver->Reset());

        p_solver->Initialise();

        // Check the solver can be called for a simple ODE system
        SimpleOde ode;
        double last_time = 0.0;
        double current_time = 2;
        double dt = 1e-5;

        ode.SetStateVariables(ode.GetInitialConditions());
        p_solver->SolveAndUpdateStateVariable(&ode, last_time, current_time, dt);

        // No stopping event is specified, so check the solver did not stop
        TS_ASSERT_EQUALS(p_solver->StoppingEventOccurred(), false);

        // Check the solver can be called for another ODE system, this time with a stopping event
        OdeSecondOrderWithEvents ode_with_events;

        ode_with_events.SetStateVariables(ode_with_events.GetInitialConditions());
        p_solver->SolveAndUpdateStateVariable(&ode_with_events, last_time, current_time, dt);

        // Check the solver stopped at the correct time
        TS_ASSERT_EQUALS(p_solver->StoppingEventOccurred(), true);
        TS_ASSERT_DELTA(p_solver->GetStoppingTime(), M_PI_2, 1e-4);
    }

    void TestWithBackwardEulerIvpOdeSolver()
    {
        typedef CellCycleModelOdeSolver<TysonNovakCellCycleModel, BackwardEulerIvpOdeSolver> EulerSolver;

        // Check we can create an instance
        boost::shared_ptr<EulerSolver> p_solver = EulerSolver::Instance();
        TS_ASSERT(p_solver.get() != NULL);

        // Check singleton-ness
        boost::shared_ptr<EulerSolver> p_solver2 = EulerSolver::Instance();
        TS_ASSERT_EQUALS(p_solver, p_solver2);

        TS_ASSERT_THROWS_THIS(p_solver->Initialise(), "SetSizeOfOdeSystem() must be called before calling Initialise()");
        p_solver->SetSizeOfOdeSystem(1);
        p_solver->Initialise();

        // Check the solver can be called for a simple ODE system
        SimpleOde ode;
        double last_time = 0.0;
        double current_time = 2;
        double dt = 1e-4;

        ode.SetStateVariables(ode.GetInitialConditions());
        p_solver->SolveAndUpdateStateVariable(&ode, last_time, current_time, dt);

        // No stopping event is specified, so check the solver did not stop
        TS_ASSERT_EQUALS(p_solver->StoppingEventOccurred(), false);

        p_solver->Reset();
        p_solver->SetSizeOfOdeSystem(2);
        p_solver->Initialise();

        TS_ASSERT_EQUALS(p_solver->GetSizeOfOdeSystem(), 2u);

        // Check the solver can be called for another ODE system, this time with a stopping event
        OdeSecondOrderWithEvents ode_with_events;

        ode_with_events.SetStateVariables(ode_with_events.GetInitialConditions());
        p_solver->SolveAndUpdateStateVariable(&ode_with_events, last_time, current_time, dt);

        // Check the solver stopped at the correct time
        TS_ASSERT_EQUALS(p_solver->StoppingEventOccurred(), true);

        TS_ASSERT_DELTA(p_solver->GetStoppingTime(), M_PI_2, 1e-2);
    }

    void TestWithCvodeAdaptor()
    {
#ifdef CHASTE_CVODE
        typedef CellCycleModelOdeSolver<TysonNovakCellCycleModel, CvodeAdaptor> CvodeSolver;

        // Check we can create an instance
        boost::shared_ptr<CvodeSolver> p_solver = CvodeSolver::Instance();
        TS_ASSERT(p_solver.get() != NULL);

        // Check singleton-ness
        boost::shared_ptr<CvodeSolver> p_solver2 = CvodeSolver::Instance();
        TS_ASSERT_EQUALS(p_solver, p_solver2);

        p_solver->Initialise();

        TS_ASSERT_THROWS_NOTHING(p_solver->CheckForStoppingEvents());
        TS_ASSERT_THROWS_NOTHING(p_solver->SetMaxSteps(1000));
        TS_ASSERT_THROWS_NOTHING(p_solver->SetTolerances(1e-5, 1e-5));
#else
        std::cout << "CVODE is not enabled. " << std::endl;
        std::cout << "If required please install and alter your hostconfig settings to switch on chaste support." << std::endl;
#endif //CHASTE_CVODE
    }

    void TestArchiving()
    {
        OutputFileHandler handler("archive", false);
        std::string archive_filename = handler.GetOutputDirectoryFullPath() + "ode_solver.arch";

        typedef CellCycleModelOdeSolver<Alarcon2004OxygenBasedCellCycleModel, BackwardEulerIvpOdeSolver> EulerSolver;

        // Create an output archive
        {
            boost::shared_ptr<EulerSolver> p_solver = EulerSolver::Instance();
            p_solver->SetSizeOfOdeSystem(4);
            p_solver->Initialise();

            std::ofstream ofs(archive_filename.c_str());
            boost::archive::text_oarchive output_arch(ofs);

            output_arch << p_solver;

            TS_ASSERT_EQUALS(p_solver->GetSizeOfOdeSystem(), 4u);
            p_solver->Reset();
            TS_ASSERT_EQUALS(p_solver->GetSizeOfOdeSystem(), UNSIGNED_UNSET);
        }

        {
            boost::shared_ptr<EulerSolver> p_solver = EulerSolver::Instance();

            TS_ASSERT_EQUALS(p_solver->GetSizeOfOdeSystem(), UNSIGNED_UNSET);

            // Create an input archive
            std::ifstream ifs(archive_filename.c_str(), std::ios::binary);
            boost::archive::text_iarchive input_arch(ifs);

            // Restore from the archive
            input_arch >> p_solver;

            TS_ASSERT_EQUALS(p_solver->GetSizeOfOdeSystem(), 4u);
        }
    }
};

#endif /*TESTCELLCYCLEMODELODESOLVER_HPP_*/


/*
* Software License Agreement (BSD License)
*
*  Point Cloud Library (PCL) - www.pointclouds.org
*  Copyright (c) 2014- Centrum Wiskunde Informatica
*
*  All rights reserved.
*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions
*  are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of its copyright holders nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
*  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
*  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
*  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
*  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
*  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
*  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
*  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
*  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
*  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
*  POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef RIGID_TRANSFORM_CODING_CCV2_HPP
#define RIGID_TRANSFORM_CODING_CCV2_HPP

// class for quaternion encoding
#include <Eigen/Geometry>
#include <stdint.h>

// use quaternions
#include <pcl/cloud_codec_v2/rigid_transform_coding.h>
#include <pcl/cloud_codec_v2/impl/quaternion_coding_impl.hpp>
#include <limits>

namespace pcl{

  namespace io
  {

    /*!
    * \brief  compression of Rigid Transform resulting from ICP procedure (either as quaternion or 2 vectors and sign byte)
    * \author Rufael Mekuria rufael.mekuria@cwi.nl
    * \param  tr_in  input rigid transform
    * \param  comp_dat_out output words containing compressed transform
    * \param  quat_out output quaternion
    * \note The output vector will contain 6 16-bit words if quaternion encoding was used, otherwise 7.
    */

    template <typename Scalar> bool
      RigidTransformCoding<Scalar>::compressRigidTransform(
        const Eigen::Matrix<Scalar, 4, 4>& tr_in,
        std::vector<int16_t>& comp_dat_out,
        Eigen::Quaternion<Scalar>& quat_out)
    {
      // 2,5 is the maximum translation
      const float scaling_factor = (float) std::numeric_limits<int16_t>::max()/2.5;

      // convert the rotation to a quaternion
#if _WIN32
      Eigen::Matrix<Scalar,3,3> rotm = tr_in.block<3,3>(0,0);
      quat_out = Eigen::Quaternion<Scalar>(tr_in.block<3,3>(0,0));
#else
      Eigen::Matrix<Scalar,3,3> rotm = tr_in.block(0,0,3,3);
      quat_out = Eigen::Quaternion<Scalar>(rotm);
#endif//_WIN32
      // test the quaternion mode for coding the rotation (encode,decode check if stable)
      Eigen::Quaternion<Scalar> quat_test;
      comp_dat_out.resize(3);
      QuaternionCoding::compressQuaternion(quat_out,(int16_t *) &comp_dat_out[0]);
      QuaternionCoding::deCompressQuaternion(&comp_dat_out[0],quat_test);
      Eigen::Matrix<Scalar, 3, 3> res_rot = quat_test.toRotationMatrix();

      //std::cout << "rigid tf coding original " << std::endl << tr_in.block<3,3>(0,0);
      //std::cout << "rigid tf test  quaternion " << std::endl << res_rot;

      // check if decoding the matrix from the quaternion gives a stable result
      bool quaternion_is_stable=true;

      for(int i=0; i<9;i++)
      {
        float diff=0.0;
        if((diff=std::abs(res_rot(i/3,i%3) - tr_in(i/3,i%3))) > 0.001){
          quaternion_is_stable=false;
          //std::cout << "instable"<<std::endl;
          break;
        }
      }
      //if(quaternion_is_stable)
      //  std::cout << " stable " <<std::endl;

      // compress the rotation matrix as two vectors if quaternion method is not numerically stable
      if(!quaternion_is_stable){
         comp_dat_out.resize(7);
         // only store two vectors of the rotation matrix
         for(int l=0;l<3;l++){
           comp_dat_out[l] =  (int16_t) int(rotm(0,l) * (std::numeric_limits<int16_t>::max() -1));
           comp_dat_out[l+3] =  (int16_t) int(rotm(1,l) * (std::numeric_limits<int16_t>::max() -1));

           // add a byte for storing the signs
           comp_dat_out[6]+= rotm(2,l)<0? 1<<l:0;
           std::cout << " stored bits: " <<  int(1<<l) << std::endl;
         }
      }
      else{
        // make sure to code the rotation properly to the outputvector as a quantized quaternion
        QuaternionCoding::compressQuaternion(quat_out,(int16_t *) &comp_dat_out[0]);
      }

      // quantize the translation

      float t1 = (float) tr_in(0,3);
      if(t1>2.5)
        t1=2.5;
      if(t1<-2.5)
        t1=-2.5;

      float t2 = (float) tr_in(1,3);
      if(t2>2.5)
        t2=2.5;
      if(t2<-2.5)
        t2=-2.5;

      float t3 = (float) tr_in(2,3);
      if(t3>2.5)
        t3=2.5;
      if(t3<-2.5)
        t3=-2.5;

      // compress the translation by quantization using 16 bits
      comp_dat_out.push_back( (int16_t) (int) (t1 * (scaling_factor-1)));
      comp_dat_out.push_back( (int16_t) (int) (t2 * (scaling_factor-1)));
      comp_dat_out.push_back( (int16_t)  (int)(t3 * (scaling_factor-1)));

      return true;
    }

    /*!
    * \brief  decompression of Rigid Transform resulting from ICP procedure (either as quaternion or 2 vectors and sign byte)
    * \author Rufael Mekuria rufael.mekuria@cwi.nl
    * \param  comp_dat_in input words containing compressed transform
    * \param  tr_out  output rigid transform
    * \param  quat_out output quaternion (will be only changed when quaternion encoding was used)
	*/
    template <typename Scalar> bool
      RigidTransformCoding<Scalar>::deCompressRigidTransform(
        const std::vector<int16_t>& comp_dat_in,
        Eigen::Matrix<Scalar, 4, 4>& tr_out,
        Eigen::Quaternion<Scalar>& quat_out)
    {
      // 2 is the maximum
      const float scaling_factor = (float) std::numeric_limits<int16_t>::max()/2.5;

      if(comp_dat_in.size() == 6){
        // decode rotation offset which is coded as quaternion
        QuaternionCoding::deCompressQuaternion((int16_t *) &comp_dat_in[0], quat_out);
#if _WIN32
        tr_out.block<3,3>(0,0) = quat_out.toRotationMatrix();
        //std::cout << "decoded rotation" << std::endl << tr_out.block<3,3>(0,0) << std::endl;
#else
          tr_out.block(0,0,3,3) = quat_out.toRotationMatrix();
          //std::cout << "decoded rotation" << std::endl << tr_out.block<3,3>(0,0) << std::endl;
#endif//_WIN323
      }
      else{
        //decode the rotation matrix from two vectors and the sign vectors
         for(int l=0;l<3;l++){
           tr_out(0,l) = ((float) comp_dat_in[l]) / (std::numeric_limits<int16_t>::max() -1);
           tr_out(1,l) = ((float) comp_dat_in[l+3]) / (std::numeric_limits<int16_t>::max() -1);
           tr_out(2,l) =  std::sqrt(1 - tr_out(0,l) * tr_out(0,l) - tr_out(1,l) * tr_out(1,l));

           // decode the sign
           if( ((1<<l) & ((int)comp_dat_in[6])) == 1<<l)
             tr_out(2,l)= - tr_out(2,l);
         }
      }

      // decode offset
      tr_out(0,3) = ((Scalar) comp_dat_in[comp_dat_in.size()-3]) / ((Scalar) (scaling_factor -1));
      tr_out(1,3) = ((Scalar) comp_dat_in[comp_dat_in.size()-2]) / ((Scalar) (scaling_factor -1));
      tr_out(2,3) = ((Scalar) comp_dat_in[comp_dat_in.size()-1]) / ((Scalar) (scaling_factor -1));

      // construct transform matrix
      tr_out(3,0) = 0;
      tr_out(3,1) = 0;
      tr_out(3,2) = 0;
      tr_out(3,3) = 1;

      return true;
    }
  }
}
#endif


//Copyright (c) 2008-2016 Emil Dotchevski and Reverge Studios, Inc.

//Distributed under the Boost Software License, Version 1.0. (See accompanying
//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <boost/qvm/quat_operations.hpp>
#include "test_qvm_quaternion.hpp"

namespace
    {
    void
    test()
        {
        using namespace boost::qvm;
        test_qvm::quaternion<Q1> q=identity_quat<float>();
        BOOST_TEST(q.a[0]==1);
        BOOST_TEST(q.a[1]==0);
        BOOST_TEST(q.a[2]==0);
        BOOST_TEST(q.a[3]==0);
        test_qvm::quaternion<Q2> p(42,1);
        set_identity(p);
        BOOST_TEST(p.a[0]==1);
        BOOST_TEST(p.a[1]==0);
        BOOST_TEST(p.a[2]==0);
        BOOST_TEST(p.a[3]==0);
        }
    }

int
main()
    {
    test();
    return boost::report_errors();
    }


//
// Copyright (c) 2004-2006
// Andreas Kloeckner
//
// Permission to use, copy, modify, distribute and sell this software
// and its documentation for any purpose is hereby granted without fee,
// provided that the above copyright notice appear in all copies and
// that both that copyright notice and this permission notice appear
// in supporting documentation.  The authors make no representations
// about the suitability of this software for any purpose.
// It is provided "as is" without express or implied warranty.
//

#ifndef HEADER_SEEN_HELPERS_HPP
#define HEADER_SEEN_HELPERS_HPP

#include <complex>
#include <pyublas/generic_ublas.hpp>
#include <boost/numeric/ublas/vector.hpp>

namespace helpers {
namespace ublas = boost::numeric::ublas;

template <class Function>
struct reverse_binary_function :
public std::binary_function<typename Function::second_argument_type,
typename Function::first_argument_type, typename Function::result_type>
{
  typename Function::result_type
  operator()(
      const typename Function::second_argument_type &a2,
      const typename Function::first_argument_type &a1) const
  {
    return Function()(a1, a2);
  }
};

// decomplexify ---------------------------------------------------------------
template <typename T>
struct decomplexify
{
  typedef T type;
};

template <typename ELT>
struct decomplexify<std::complex<ELT> >
{
  typedef ELT type;
};

// complexify -----------------------------------------------------------------
template <typename T>
struct complexify
{
  typedef std::complex<T> type;
};

template <typename ELT>
struct complexify<std::complex<ELT> >
{
  typedef std::complex<ELT> type;
};

// isComplex ------------------------------------------------------------------
template <typename T>
inline bool isComplex(const T &)
{
  return false;
}

template <typename T2>
inline bool isComplex(const std::complex<T2> &)
{
  return true;
}

// conjugate ------------------------------------------------------------------
template <typename T>
inline T conjugate(const T &x)
{
  return x;
}

template <typename T2>
inline std::complex<T2> conjugate(const std::complex<T2> &x)
{
  return conj(x);
}

// conjugate_if ---------------------------------------------------------------
template <typename T>
inline T conjugate_if(bool do_it, const T &x)
{
  return x;
}

template <typename T2>
inline std::complex<T2> conjugate_if(bool do_it, const std::complex<T2> &x)
{
  return do_it ? conj(x) : x;
}

// absolute_value -------------------------------------------------------------
template <typename T>
inline T absolute_value(const T &x)
{
  return fabs(x);
}

template <typename T2>
inline T2 absolute_value(const std::complex<T2> &x)
{
  return sqrt(norm(x));
}

// absolute_value_squared -----------------------------------------------------
template <typename T>
inline T absolute_value_squared(const T &x)
{
  return fabs(x)*fabs(x);
}

template <typename T2>
inline T2 absolute_value_squared(const std::complex<T2> &x)
{
  return norm(x);
}

// end namespaces -------------------------------------------------------------
}

#endif


/**
 * @file dcmt64mpi.cpp
 *
 * @brief The main function of parameter generator of 64 bit Mersenne Twister.
 *
 * The functions in this file are simple. They parse the command line
 * options and call all_in_one function which does almost all things.
 * Users can change this file so that it fits to their applications
 * and OS.
 *
 * @author Mutsuo Saito
 * @author Makoto Matsumoto (Hiroshima University)
 * @author Takuji Nishimura (Yamagata University)
 *
 * Copyright (C) 2019 Mutsuo Saito, Makoto Matsumoto,
 * Takuji Nishimura and Hiroshima University.
 * All rights reserved.
 *
 * The MIT License is applied to this software, see
 * LICENSE
 */
#include <stdint.h>
#include <inttypes.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <string>
#include <sstream>
#include <fstream>
#include <MTToolBox/AlgorithmRecursionSearch.hpp>
#include <MTToolBox/AlgorithmEquidistribution.hpp>
#include <MTToolBox/MersenneTwister.hpp>
#include <MTToolBox/MersenneTwister.hpp>
#include <NTL/GF2X.h>
#include <getopt.h>
#include "mpicontrol.hpp"
#include "mt64Search.hpp"
#include "search.h"
#include "options.h"

using namespace std;
using namespace MTToolBox;
using namespace NTL;

/**
 * parse command line option, and search parameters
 * @param argc number of arguments
 * @param argv value of arguments
 * @return 0 if this ends normally
 */
int main(int argc, char** argv) {
    MPIControl mpi(&argc, &argv);
    options opt;
    bool parse = parse_opt(opt, argc, argv);
    if (!parse) {
        return -1;
    }
    // MPI
    char buff[200];
    opt.id = opt.id + mpi.getRank();
    //if (opt.mexp == 19937) {
    //    opt.fixedPOS = 156;
    //    opt.max_defect = 8000; // mt19937-64 is 7820
    //}
    //opt.seq; count down, -1 means uint32_t max
    //opt.count;
    //opt.logcount;
    if (!opt.outfilename.empty()) {
        sprintf(buff, ".s%04ld-%03d.txt", opt.seed, mpi.getRank());
        opt.outfilename += buff;
    }
    if (!opt.logfilename.empty()) {
        sprintf(buff, ".s%04ld-%03d.log", opt.seed, mpi.getRank());
        opt.logfilename += buff;
    }
    // MPI end
    ofstream ofs;
    ofstream log;
    ostream *os;
    ostream *ls;
    if (!opt.outfilename.empty()) {
        ofs.open(opt.outfilename.c_str());
        if (!ofs) {
            cerr << "can't open file:" << opt.outfilename << endl;
            return -1;
        }
        os = &ofs;
    } else {
        os = &cout;
    }
    if (!opt.logfilename.empty()) {
        log.open(opt.logfilename.c_str());
        if (!log) {
            cerr << "can't open file:" << opt.logfilename << endl;
            return -1;
        }
        ls = &log;
    } else {
        ls = os;
    }
    return search(opt, *os, *ls, opt.count);
    //return best_search(opt, *os, *ls, opt.count); bug? or too slow?
}