// $Id: event.h,v 1.38 2018/01/03 21:32:58 mkkuhner Exp $

/*
Copyright 2002-2005 Mary K. Kuhner, Peter Beerli, and Joseph Felsenstein

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.

*/

/* Authors: Peter Beerli, Mary Kuhner, Jon Yamato and Joseph Felsenstein */


#ifndef EVENT_H
#define EVENT_H

#include <vector>
#include "vectorx.h"
#include "defaults.h"
#include "forceparam.h"
#include "tree.h" // for branchpair typedef
#include "epoch.h"

class ForceSummary;
class ResimArranger;
class Branch;
class TimeList;
class FC_Status;
class Epoch;

enum event_class {activeCoalEvent, inactiveCoalEvent, migEvent, diseaseEvent,
                  activeRecEvent, inactiveRecEvent, epochEvent};

/***************************************************************
  This class contains expertise needed to handle one type of "event" in the rearrangment process.

  Events are things such as a migration, a recombination on an active lineage, a recombination on an
  inactive lineage, etc.  The PickTime() routine calculates the time of the next such event.  The DoEvent()
  routine calls Tree code needed to actually implement the event.  The Done() routine indicates whether
  resimulation needs to continue; only if all events are Done() will resimulation terminate before
  the end of the tree is reached.  The TieAllowed() routine says whether the event is allowed to occur
  at the exact end of the time interval under consideration or not.

  The class is polymorphic on event type, which is related to Force, but more than one event type
  can be associated with the same Force (for example, the two types of recombination).

  Events must be initialized with the parameters of the current chain via a call to
  InstallParameters() before they are used.

  Written by Mary Kuhner
  March 2002--revised to enable Growth force

  December 2003--revised to move parameters up to ArrangerVec class to enable BayesArranger.  Mary

  November 2004--revised to split DoEvent into DoEvent (which picks all the random stuff) and FinishEvent
    (which uses the stuff chosen by DoEvent).  DoEventThatMatches written to parallel the new DoEvent
    so it could make non-random choices.  --Lucian

  March 2006--removed DoEventThatMatches due to Branch_ptr refactor and subsequent realization that
    TreeSizeArranger need not erase the tree!

  September 2010--added EpochEvent for divergence, deleted FinishEvent

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

//------------------------------------------------------------------------------------

class Event
{
  public:
    Event(const ForceSummary& fs);
    virtual        ~Event() {};

    virtual double PickTime(double, double) = 0;
    virtual void   DoEvent(double eventT, FC_Status& fcstatus) = 0;
    virtual void   InstallParameters(const ForceParameters& starts);
    virtual bool   Done() const;
    virtual Event* Clone() const = 0;
    virtual event_class Type() const = 0;   // RTII
    void           SetArranger(ResimArranger& newowner) { m_pOwningArranger = &newowner; };
    virtual bool   IsInactive() { return false; };
    virtual bool   IsEpoch() const { return false; };
    virtual bool   TiesAllowed() const { return false; };

  protected:
    Event(const Event& src);
    ResimArranger* m_pOwningArranger;   // points back to owning arranger

    // these are all set by InstallParameters()
    long           m_maxEvents;         // maximum for this force
    long           m_pindex;            // index into a standard partition force

    // ordered vector for an event
    // theta and growth values for use with m_CalcSizeAtTime plus other use (empty is OK)
    DoubleVec1d    m_thetas, m_growths;
    ForceParameters m_allparams;        // general parameter interface for use m_CalcSizeAtTime

  private:
    Event& operator=(const Event& src); // not defined
    Event();                            // not defined
}; // Event class

//------------------------------------------------------------------------------------

class XPartitionEvent : public Event
{
  public:
    XPartitionEvent(const ForceSummary& fs);
    virtual       ~XPartitionEvent() {};
    // we accept default copy ctor; def ctor and op= disabled in base class

  protected:
    long m_nxparts;

    virtual void  ThrowIfSubPopSizeTiny(double eventT) const;
}; // XPartition Event

//------------------------------------------------------------------------------------

class CoalEvent : public XPartitionEvent
{
  public:
    CoalEvent(const ForceSummary& fs, bool isactive);
    virtual        ~CoalEvent() {};
    // we accept default copy ctor; def ctor and op= disabled in base class

    virtual void   DoEvent(double eventT, FC_Status& fcstatus);
    virtual void   InstallParameters(const ForceParameters& starts);
    virtual bool   IsInactive() { return (!m_isactive); };

  protected:
    long m_chosenxpart;
    const bool m_isactive;
}; // CoalEvent class

//------------------------------------------------------------------------------------

class ActiveCoal : public CoalEvent
{
  public:
    ActiveCoal(const ForceSummary& fs);
    virtual        ~ActiveCoal() {};
    // we accept default copy ctor; def ctor and op= disabled in base class

    virtual double PickTime(double starttime, double maxtime);
    virtual void   InstallParameters(const ForceParameters& starts);
    virtual Event* Clone() const;
    virtual event_class Type() const { return activeCoalEvent; };   // RTII

  private:                              // these are set up by InstallParameters
    vector<double> m_invTheta;          // precomputed 1/Theta array
}; // ActiveCoal Event

class InactiveCoal : public CoalEvent
{
  public:
    InactiveCoal(const ForceSummary& fs);
    virtual        ~InactiveCoal() {};
    // we accept default copy ctor; def ctor and op= disabled in base class

    virtual double PickTime(double starttime, double maxtime);
    virtual void   InstallParameters(const ForceParameters& starts);
    virtual Event* Clone() const;
    // RTII
    virtual event_class Type() const { return inactiveCoalEvent; };

  private:                              // these are set up by InstallParameters
    vector<double> m_inv2Theta;         // precomputed 2/Theta array
}; // InactiveCoal Event

//------------------------------------------------------------------------------------

class PartitionEvent : public Event
{
  public:
    PartitionEvent(const ForceSummary& fs);
    virtual       ~PartitionEvent() {};
    // we accept default copy ctor; def ctor and op= disabled in base class

  protected:
    long m_nparts;

    force_type m_forcetype;

}; // Partition Event

//------------------------------------------------------------------------------------

class MigEvent : public PartitionEvent
{
  public:
    MigEvent(const ForceSummary& fs);
    virtual        ~MigEvent() {};
    // we accept default copy ctor; def ctor and op= disabled in base class

    virtual double PickTime(double starttime, double maxtime);
    virtual void   DoEvent(double eventT, FC_Status& fcstatus);
    virtual void   InstallParameters(const ForceParameters& starts);
    virtual Event* Clone() const;
    virtual event_class Type() const { return migEvent; };   // RTII

  private:                              // these are set up by InstallParameters
    DoubleVec2d    m_rescaledMigRates;  // rescaled parameters
    DoubleVec1d    m_immigrationRates;  // combined over source populations
    long           m_frompop;
    long           m_topop;

    void           ComputeCumulativeRates(const ForceParameters& starts);

}; // MigEvent

//------------------------------------------------------------------------------------

class DivMigEvent : public PartitionEvent
{
  public:
    DivMigEvent(const ForceSummary& fs);
    virtual        ~DivMigEvent() {};
    // we accept default copy ctor; def ctor and op= disabled in base class

    virtual double PickTime(double starttime, double maxtime);
    virtual void   DoEvent(double eventT, FC_Status& fcstatus);
    virtual void   InstallParameters(const ForceParameters& starts);
    virtual Event* Clone() const;
    virtual event_class Type() const { return migEvent; };   // RTII

  private:                              // these are set up by InstallParameters
    DoubleVec2d    m_rescaledMigRates;  // rescaled parameters
    DoubleVec1d    m_immigrationRates;  // combined over source populations
    long           m_frompop;
    long           m_topop;
    const std::vector<Epoch>* m_epochptr; // Epoch structure
    long           m_currentepoch;        // scratchpad for current epoch
    std::vector<double> m_epochtimes;     // epoch times, starting from 0

    void           ComputeCumulativeRates();

}; // DivMigEvent

//------------------------------------------------------------------------------------

class DiseaseEvent : public PartitionEvent
{
    // All member variables are initialized by InstallParameters().

  protected:
    long m_startdis;
    long m_enddis;

  public:
    DiseaseEvent(const ForceSummary& fs);
    virtual        ~DiseaseEvent() {};
    // we accept default copy ctor; def ctor and op= disabled in base class

    virtual Event* Clone() const;
    virtual double PickTime(double starttime, double maxtime);
    virtual void   DoEvent(double eventT, FC_Status& fcstatus);
    virtual void   InstallParameters(const ForceParameters& starts);
    virtual event_class Type() const { return diseaseEvent; };   // RTTI
}; // DiseaseEvent

//------------------------------------------------------------------------------------

class ActiveRec : public Event
{
  public:
    ActiveRec(const ForceSummary& fs);
    virtual        ~ActiveRec() {};
    // we accept default copy ctor; def ctor and op= disabled in base class

    virtual double PickTime(double starttime, double maxtime);
    virtual void   DoEvent(double eventT, FC_Status& fcstatus);
    virtual void   InstallParameters(const ForceParameters& starts);
    virtual Event* Clone() const;
    virtual event_class Type() const { return activeRecEvent; };   // RTII

    void   AddSizeForce(force_type fname) { m_onPartitionForces.push_back(fname); };

  private:                              // these are set up by InstallParameters
    double  m_recrate;                  // recombination rate

    // m_onPartitionForces is filled by Force::ModifyEvents
    vector<force_type> m_onPartitionForces;
}; // ActiveRec Event

//------------------------------------------------------------------------------------

class InactiveRec : public Event
{
  public:
    InactiveRec(const ForceSummary& fs);
    virtual        ~InactiveRec() {};

    virtual double PickTime(double starttime, double maxtime);
    virtual void   DoEvent(double eventT, FC_Status& fcstatus);
    virtual void   InstallParameters(const ForceParameters& starts);
    virtual Event* Clone() const;
    // RTII
    virtual event_class Type() const { return inactiveRecEvent; };

    void   AddSizeForce(force_type fname)
    { m_onPartitionForces.push_back(fname); };
    virtual bool  IsInactive() { return true; };

    // In the absence of a more correct algorithm for detecting upcoming
    // InactiveRecombinations, we presume that resimulation must continue
    // to the bottom of the tree.  NB:  RecTree::Prune and ResimArranger rely
    // on this continuation of the rearrangement to the bottom.  DenovoArranger
    // does not, due to lack of any structure below the active face.

    virtual bool   Done() const           { return false; };

  private:                              // these are set up by InstallParameters
    double  m_recrate;                  // recombination rate

    // m_onPartitionForces is filled by Force::ModifyEvents
    vector<force_type> m_onPartitionForces;
}; // InactiveRec Event

//------------------------------------------------------------------------------------

class EpochEvent : public Event
{
  public:
    EpochEvent(const ForceSummary& fs);
    virtual ~EpochEvent() {};

    virtual double PickTime(double starttime, double maxtime);
    virtual void DoEvent(double eventT, FC_Status& fcstatus);
    virtual void InstallParameters(const ForceParameters& starts);
    virtual Event* Clone() const;
    // RTII
    virtual event_class Type() const { return epochEvent; };
    virtual bool IsEpoch() const { return true; };
    virtual bool TiesAllowed() const { return true; };

  private:
    const std::vector<Epoch>* m_epochptr;
    std::vector<double> m_epochtimes;
};

//------------------------------------------------------------------------------------

// Helper function.
std::string ToString(const Event &e);

//------------------------------------------------------------------------------------

#endif // EVENT_H

//____________________________________________________________________________________
