// -*- C++ -*-

/*!
  \file stochastic/HistogramsDirect.h
  \brief The direct method for SSA.
*/

#if !defined(__stochastic_HistogramsDirect_h__)
#define __stochastic_HistogramsDirect_h__

#include "HistogramsBase.h"
#include "TimeEpochOffset.h"

namespace stochastic {

//! Accumulate histograms at specified frames using Gillespie's direct method.
/*!
  \param _DiscreteGenerator Random deviate generator for the discrete,
  finite distribution with reaction propensities as scaled probabilities.
  \param _ExponentialGenerator Random deviate generator for the exponential
  distribution. By default the ziggurat algorithm is used.
  \param _PropensitiesFunctor Can calculate propensities as a function of the
  reaction index and the populations.
*/
template < class _DiscreteGenerator,
         class _ExponentialGenerator =
         numerical::ExponentialGeneratorZiggurat<>,
         class _PropensitiesFunctor = PropensitiesSingle<true> >
class HistogramsDirect :
   public HistogramsBase < _DiscreteGenerator, _ExponentialGenerator,
      _PropensitiesFunctor > {
   //
   // Private types.
   //
private:

   typedef HistogramsBase < _DiscreteGenerator, _ExponentialGenerator,
           _PropensitiesFunctor > Base;

   //
   // Public types.
   //
public:

   //! The propensities functor.
   typedef typename Base::PropensitiesFunctor PropensitiesFunctor;
   //! The exponential generator.
   typedef typename Base::ExponentialGenerator ExponentialGenerator;
   //! The discrete, finite generator.
   typedef typename Base::DiscreteGenerator DiscreteGenerator;
   //! The discrete, uniform generator.
   typedef typename Base::DiscreteUniformGenerator DiscreteUniformGenerator;

   //
   // Member data.
   //
protected:

   using Base::_state;
   using Base::_time;
   using Base::_error;
   using Base::_exponentialGenerator;
   using Base::_discreteGenerator;
   using Base::_frames;
   using Base::_recordedSpecies;
   using Base::_histograms;

   //
   // Not implemented.
   //
private:

   //! Default constructor not implemented.
   HistogramsDirect();
   //! Copy constructor not implemented.
   HistogramsDirect(const HistogramsDirect&);
   //! Assignment operator not implemented.
   HistogramsDirect&
   operator=(const HistogramsDirect&);

   //--------------------------------------------------------------------------
   //! \name Constructors etc.
   //@{
public:

   //! Construct.
   HistogramsDirect
   (const State& state,
    const PropensitiesFunctor& propensitiesFunctor,
    const array::StaticArrayOfArrays<std::size_t>& reactionInfluence,
    const std::vector<double>& frames,
    const std::vector<std::size_t>& recordedSpecies,
    const std::size_t numberOfBins,
    const std::size_t multiplicity,
    const double maxSteps) :
      Base(state, propensitiesFunctor, reactionInfluence, frames,
           recordedSpecies, numberOfBins, multiplicity, maxSteps) {
   }

   // Use the default destructor.

   //@}
   //--------------------------------------------------------------------------
   //! \name Simulation.
   //@{
public:

   //! Initialize the state with the initial populations and time.
   using Base::initialize;

   //! Generate a trajectory and record the state in the histograms.
   void
   simulate() {
      std::size_t frameIndex = 0;
      // Step until we have recorded the state at each of the frames.
      while (true) {
         // Check that we have not exceed the allowed number of steps.
         if (! Base::incrementStepCount()) {
            setStepCountError();
            break;
         }

         // Compute the time of the next reaction.
         if (_discreteGenerator.isValid()) {
            const double mean = 1.0 / _discreteGenerator.sum();
            _time.updateEpoch(mean);
            _time += mean * _exponentialGenerator();
         }
         else {
            _time = std::numeric_limits<double>::max();
         }

         // For each frame that we will cross with this reaction.
         while (_time >= _frames[frameIndex]) {
            // Record the probabilities for the current state.
            for (std::size_t i = 0; i != _recordedSpecies.size(); ++i) {
               _histograms(frameIndex, i).accumulate
               (_state.getPopulation(_recordedSpecies[i]), 1.);
            }
            // Move to the next frame.
            ++frameIndex;
            // If we have recorded the state at all of the frames.
            if (frameIndex == _frames.size()) {
               // End the simulation.
               return;
            }
         }

         // Determine the reaction to fire.
         const std::size_t reactionIndex = _discreteGenerator();
#ifdef DEBUG_stlib
         assert(_discreteGenerator[reactionIndex] > 0);
#endif
         // Fire the reaction.
         _state.fireReaction(reactionIndex);
         // Recompute the propensities and update the discrete, finite generator.
         Base::updatePropensities(reactionIndex);
      }
   }

   //! Synchronize the two sets of histograms so that corresponding historams have the same lower bounds and widths.
   using Base::synchronize;

protected:

   //! Record a step count error message. Record the current time.
   using Base::setStepCountError;

   //@}
   //--------------------------------------------------------------------------
   //! \name Accessors.
   //@{
public:

   using Base::getStepCount;

   //! Return a const reference to the state.
   using Base::getState;

   //! Return a const reference to the discrete, uniform generator.
   using Base::getDiscreteUniformGenerator;

   //! Return the vector of recorded species.
   using Base::getRecordedSpecies;

   //! Return the set of histograms.
   using Base::getHistograms;

   //! Return the current time.
   double
   getTime() const {
      // Convert the time epoch and offset to a single time.
      return _time;
   }

   //@}
};

//@}

} // namespace stochastic

#endif
