text: #pragma once
  
  #include "tools/Assert.h"
  #include "tools/CommonTypes.h"
  #include "tools/InputParser.h"
  #include "parallel/OpenMP_buffer.h"
  #include "tools/RandomNumber.h"
  #include "Output_values.h"
  #include "Event_base.h"
  #include "tools/GenericFactory.h"
  
  #include <algorithm>
  #include <memory>
  #include <vector>
  #include <string>
  #include <map>
  #include <iostream>
  #include <functional>
  
  class simulation
  {
      public:
          using Real  = CommonTypes::Real;
          using Real2 = CommonTypes::Real2;
          using index2= CommonTypes::index2;
          using indexbuffer = OpenMP::OpenMP_buffer<std::vector<index2>>;
          using vectorindex = std::vector<index2>;
          using Outputregistry = std::map<std::string,OutputValue>; 
          using Eventptr  = std::unique_ptr<Event>;
  
          simulation(){};
          simulation(const ParameterPack& input);
          virtual ~simulation(){};
  
          // purely virtual function of update & step
          virtual void update();
          virtual void step(); 
  
          // getters
          Real get_time()const {return time_;};
          int  get_steps()const {return num_steps_;};
          std::vector<Real> get_rate() const{return rates_;};
          Real get_output_val(std::string name) const;
          const Outputregistry& get_outputregistry() const {return outputval_registry_;};
          Real get_tend() const{return tend_;};
          std::string get_name() const {return name_;};
          const sys& get_sys() const {return *sys_.get();}
  
          // setters
          void set_name(std::string name) {name_ = name;};
  
  
          // register the Outputvalue
          void register_OutputValue(std::string name, OutputValue::ValueFunction func);
  
          // print debug msg
          virtual void printDebugMsg(){};
  
          // run if we are debugging
          virtual void runDebug(){};
      
      protected:
          // Time of the system, initialized to zero
          Real time_ = 0.0;
  
          Real tend_ = 0.0;
  
          // Number of steps being taken, initialized to zero
          int num_steps_ = 0;
  
          // Number of events 
          int num_events_ = 0;
  
          // name of the simulation
          std::string name_;
  
          // vector of rates for each of the event
          std::vector<Real> rates_;
          std::vector<Real> cum_sum_;
          Real total_rate_;
  
          // Outputregistry
          Outputregistry outputval_registry_;
  
          std::vector<Eventptr> events_;
  
          // vector of omp buffers
          std::vector<indexbuffer> buffers_;
  
          // vector of vector that holds the indices possible for each event
          std::vector<vectorindex> site_idx_; 
  
          // pointer to system object
          std::unique_ptr<sys> sys_;
          std::string sys_type_;
  
          // whether or not to be verbose and print to the console
          bool verbose_ = false;
  
          // Draw a uniform number for selecting the event
          Real randnum_event_;
          Real randnum_site_;
          Real randnum_time_;
  
          int event_idx_;
  
          // chosen site information
          Site chosen_site_copy_;
  
          // whether or not we want to run it in debug
          bool debug_ = false;
  };
  
  namespace simulations
  {
      using Base = simulation;
      using Key  = std::string;
  
      using factory=GenericFactory<simulation, std::string, const ParameterPack&>;
  
      template <class D>
      using register_ = RegisterInFactory<simulation,D, std::string, const ParameterPack&>;
  };