/* generated by template org.nest.nestml.neuron.NeuronHeader*/

/*
*  eglif_cond_alpha_multisyn.h
*
*  This file is part of NEST.
*
*  Copyright (C) 2004 The NEST Initiative
*
*  NEST is free software: you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation, either version 2 of the License, or
*  (at your option) any later version.
*
*  NEST is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with NEST.  If not, see <http://www.gnu.org/licenses/>.
*
*/

#ifndef EGLIF_COND_ALPHA_MULTISYN
#define EGLIF_COND_ALPHA_MULTISYN
#include "config.h"

#ifdef HAVE_GSL
#include <gsl/gsl_errno.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_odeiv.h>
// forwards the declaration of the function
extern "C" inline int
eglif_cond_alpha_multisyn_dynamics(double, const double y[], double f[],
                                          void *pnode);
/*
extern "C" inline int
eglif_cond_alpha_multisyn_dynamics(double, const double*, double*,
                                          void *pnode);*/

// Includes from nestkernel:
#include "archiving_node.h"
#include "connection.h"
#include "event.h"
#include "nest_types.h"
#include "ring_buffer.h"
#include "universal_data_logger.h"

// Includes from sli:
#include "dictdatum.h"

/* BeginDocumentation
Name: eglif_cond_alpha_multisyn .

//
Name: eglif_cond_alpha_multisyn_nestml - Extended Generalized Leaky
integrate-and-fire neuron model with multiple ports based. Described in [Geminiani et al., Front Neuroinf, 2018].

Description (NESTML):

eglif_cond_alpha_multisyn_nestml from iaf_psc_exp_multisynapse_nestml.

With respect to first model version, a minimum value of Vm is added here, useful in case of oscillatory solutions

Sends: SpikeEvent

Receives: SpikeEvent, CurrentEvent, DataLoggingRequest

Author:  Geminiani Alice 
         Casellato Claudia, adapted from iaf_psc_exp_multisynapse and iaf_psc_alpha_multisyn and iaf_cond_alpha for the synapses;
Contact: alice.geminiani@polimi.it

//

Parameters:

Remarks:
Empty

References:
Empty

Sends: nest::SpikeEvent

Receives: Spike, Current, DataLoggingRequest


SeeAlso:
Empty
*/
class eglif_cond_alpha_multisyn : public nest::Archiving_Node {
public:
  /**
  * The constructor is only used to create the model prototype in the model
  * manager.
  */
  eglif_cond_alpha_multisyn();

  /**
  * The copy constructor is used to create model copies and instances of the
  * model.
  * @node The copy constructor needs to initialize the parameters and the state.
  *       Initialization of buffers and interal variables is deferred to
  *       @c init_buffers_() and @c calibrate().
  */
  eglif_cond_alpha_multisyn(const eglif_cond_alpha_multisyn &);

  /**
  * Releases resources.
  */
  ~eglif_cond_alpha_multisyn();
  /**
  * Import sets of overloaded virtual functions.
  * This is necessary to ensure proper overload and overriding resolution.
  * @see http://www.gotw.ca/gotw/005.htm.
  */
  using nest::Node::handles_test_event;
  using nest::Node::handle;

  /**
  * Used to validate that we can send nest::SpikeEvent to desired target:port.
  */
  nest::port send_test_event(nest::Node &target, nest::rport receptor_type,
                             nest::synindex, bool);

  /**
  * @defgroup mynest_handle Functions handling incoming events.
  * We tell nest that we can handle incoming events of various types by
  * defining @c handle() and @c connect_sender() for the given event.
  * @{
  */
  void handle(nest::SpikeEvent &);         //! accept spikes
  void handle(nest::CurrentEvent &);       //! accept input current
  void handle(nest::DataLoggingRequest &); //! allow recording with multimeter

  nest::port handles_test_event(nest::SpikeEvent &, nest::port);
  nest::port handles_test_event(nest::CurrentEvent &, nest::port);
  nest::port handles_test_event(nest::DataLoggingRequest &, nest::port);
  /** @} */

  // SLI communication functions:
  void get_status(DictionaryDatum &) const;
  void set_status(const DictionaryDatum &);

  // Generate function header

  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline double get_I_dep() const { return S_.y_[State_::I_dep]; }

  inline void set_I_dep(const double v) { S_.y_[State_::I_dep] = v; }

  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline double get_I_adap() const { return S_.y_[State_::I_adap]; }

  inline void set_I_adap(const double v) { S_.y_[State_::I_adap] = v; }

  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline double get_V_m() const { return S_.y_[State_::V_m]; }

  inline void set_V_m(const double v) { S_.y_[State_::V_m] = v; }

  /* Conductance-based synapses for the 3 receptors*/

  inline double get_G1() const { return S_.y_[State_::G1]; }

  inline void set_G1(const double v) { S_.y_[State_::G1] = v; }

  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

 inline double get_DG1() const { return S_.y_[State_::DG1]; }

 inline void set_DG1(const double v) { S_.y_[State_::DG1] = v; }


  inline double get_G2() const { return S_.y_[State_::G2]; }

  inline void set_G2(const double v) { S_.y_[State_::G2] = v; }

  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

 inline double get_DG2() const { return S_.y_[State_::DG2]; }

 inline void set_DG2(const double v) { S_.y_[State_::DG2] = v; }


  inline double get_G3() const { return S_.y_[State_::G3]; }

  inline void set_G3(const double v) { S_.y_[State_::G3] = v; }

  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

 inline double get_DG3() const { return S_.y_[State_::DG3]; }

 inline void set_DG3(const double v) { S_.y_[State_::DG3] = v; }



  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline double get_C_m() const { return P_.C_m; }

  inline void set_C_m(const double v) { P_.C_m = v; }

  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline double get_tau_m() const { return P_.tau_m; }

  inline void set_tau_m(const double v) { P_.tau_m = v; }


  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline double get_tau_syn1() const { return P_.tau_syn1; }

  inline void set_tau_syn1(const double v) { P_.tau_syn1 = v; }


 /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/
  inline double get_E_rev1() const { return P_.E_rev1; }

  inline void set_E_rev1(const double v) { P_.E_rev1 = v; }

  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline double get_tau_syn2() const { return P_.tau_syn2; }

  inline void set_tau_syn2(const double v) { P_.tau_syn2 = v; }


 /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/
  inline double get_E_rev2() const { return P_.E_rev2; }

  inline void set_E_rev2(const double v) { P_.E_rev2 = v; }

  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline double get_tau_syn3() const { return P_.tau_syn3; }

  inline void set_tau_syn3(const double v) { P_.tau_syn3 = v; }


 /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/
  inline double get_E_rev3() const { return P_.E_rev3; }

  inline void set_E_rev3(const double v) { P_.E_rev3 = v; }


  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline double get_t_ref() const { return P_.t_ref; }

  inline void set_t_ref(const double v) { P_.t_ref = v; }

  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline double get_E_L() const { return P_.E_L; }

  inline void set_E_L(const double v) { P_.E_L = v; }

  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline double get_V_reset() const { return P_.V_reset; }

  inline void set_V_reset(const double v) { P_.V_reset = v; }

  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline double get_V_th() const { return P_.V_th; }

  inline void set_V_th(const double v) { P_.V_th = v; }

  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/


  inline double get_Vinit() const { return P_.Vinit; }

  inline void set_Vinit(const double v) { P_.Vinit = v; }

  inline double get_Vmin() const { return P_.Vmin; }

  inline void set_Vmin(const double v) { P_.Vmin = v; }
  
  /* Added for stochasticity*/
  inline double get_lambda_0() const { return P_.lambda_0; }

  inline void set_lambda_0(const double v) { P_.lambda_0 = v; }

  inline double get_tau_V() const { return P_.tau_V; }

  inline void set_tau_V(const double v) { P_.tau_V = v; }

  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline double get_k1() const { return P_.k1; }

  inline void set_k1(const double v) { P_.k1 = v; }

  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline double get_k2() const { return P_.k2; }

  inline void set_k2(const double v) { P_.k2 = v; }

  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline double get_A1() const { return P_.A1; }

  inline void set_A1(const double v) { P_.A1 = v; }

  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline double get_A2() const { return P_.A2; }

  inline void set_A2(const double v) { P_.A2 = v; }

  inline double get_kadap() const { return P_.kadap; }

  inline void set_kadap(const double v) { P_.kadap = v; }

  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline double get_I_e() const { return P_.I_e; }

  inline void set_I_e(const double v) { P_.I_e = v; }


  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline long get_bT_size() const { return P_.bT_size; }

  inline void set_bT_size(const long v) { P_.bT_size = v; }

  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline long get_receptors() const { return V_.receptors; }

  inline void set_receptors(const long v) { V_.receptors = v; }


  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline long get_RefractoryCounts() const { return V_.RefractoryCounts; }

  inline void set_RefractoryCounts(const long v) { V_.RefractoryCounts = v; }

  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline long get_r() const { return V_.r; }

  inline void set_r(const long v) { V_.r = v; }

  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline std::vector<double> get_bufferT() const { return V_.bufferT; }

  inline void set_bufferT(const std::vector<double> v) { V_.bufferT = v; }

  inline double get_Vth() const { return V_.V_th; }

  inline void set_Vth(const double v) { V_.V_th = v; }

  inline double get_I_gen() const { return V_.I_gen; }

  inline void set_I_gen(const double v) { V_.I_gen = v; }


  inline double get_sum_buffer() const { return V_.sum_buffer; }

  inline void set_sum_buffer(const double v) { V_.sum_buffer = v; }

  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline double get_tot_cur() const { return V_.tot_cur; }

  inline void set_tot_cur(const double v) { V_.tot_cur = v; }

  /* Added for R2 update*/
  inline double get_R2() const {return V_.R2; }

  inline double set_R2(const double v) {return V_.R2 = v; }

  inline double get_old_Vm() const { return V_.old_Vm; }

  inline void set_old_Vm(const double v) { V_.old_Vm = v; }

  inline double get_new_Vm() const { return V_.new_Vm; }

  inline void set_old_new_Vm(const double v) { V_.new_Vm = v; }

  inline double get_old_slope() const { return V_.old_slope; }

  inline void set_old_slope(const double v) { V_.old_slope = v; }

  inline double get_new_slope() const { return V_.new_slope; }

  inline void set_new_slope(const double v) { V_.new_slope = v; }

 
  inline std::vector<double> get_G0() const {
    return V_.G0;
  }

  inline void set_G0(const std::vector<double> v) {
    V_.G0 = v;
  }



  inline double get_I_syn() const { return S_.I_syn; }
  inline void set_I_syn(const double v) { S_.I_syn = v; }

  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/


  /* generated by template org.nest.nestml.function.MemberVariableGetterSetter*/

  inline double get_I_tot() const { return S_.I_tot; }
  inline void set_I_tot(const double v) { S_.I_tot = v; }

  inline nest::RingBuffer &get_spikes_r1() {
    return B_.get_spikes_r1();
  };

  inline nest::RingBuffer &get_spikes_r2() {
    return B_.get_spikes_r2();
  };

  inline nest::RingBuffer &get_spikes_r3() {
    return B_.get_spikes_r3();
  };

  inline nest::RingBuffer &get_currents() { return B_.get_currents(); };


protected:
  //! Reset parameters and state of neuron.

  //! Reset state of neuron.
  void init_state_(const Node &proto);

  //! Reset internal buffers of neuron.
  void init_buffers_();

  //! Initialize auxiliary quantities, leave parameters and state untouched.
  void calibrate();

  //! Take neuron through given time interval
  void update(nest::Time const &, const long, const long);

  // The next two classes need to be friends to access the State_ class/member
  friend class nest::RecordablesMap<eglif_cond_alpha_multisyn>;
  friend class nest::UniversalDataLogger<eglif_cond_alpha_multisyn>;

  /**
  * Free parameters of the neuron.
  *
  *
  *
  * These are the parameters that can be set by the user through @c SetStatus.
  * They are initialized from the model prototype when the node is created.
  * Parameters do not change during calls to @c update() and are not reset by
  * @c ResetNetwork.
  *
  * @note Parameters_ need neither copy constructor nor @c operator=(), since
  *       all its members are copied properly by the default copy constructor
  *       and assignment operator. Important:
  *       - If Parameters_ contained @c Time members, you need to define the
  *         assignment operator to recalibrate all members of type @c Time . You
  *         may also want to define the assignment operator.
  *       - If Parameters_ contained members that cannot copy themselves, such
  *         as C-style arrays, you need to define the copy constructor and
  *         assignment operator to copy those members.
  */
  struct Parameters_ {

    /* generated by template org.nest.nestml.function.MemberDeclaration*/

    double C_m; //

    /* generated by template org.nest.nestml.function.MemberDeclaration*/

    double tau_m; //

    /* generated by template org.nest.nestml.function.MemberDeclaration*/

   double tau_syn1; //

    /* Conductance-based synapses*/
    double E_rev1;  //

   double tau_syn2; //

    /* Conductance-based synapses*/
    double E_rev2;  //

   double tau_syn3; //

    /* Conductance-based synapses*/
    double E_rev3;  //

    /* generated by template org.nest.nestml.function.MemberDeclaration*/

    double t_ref; //

    /* generated by template org.nest.nestml.function.MemberDeclaration*/

    double E_L; //

    /* generated by template org.nest.nestml.function.MemberDeclaration*/

    double V_reset; //
    

    /* generated by template org.nest.nestml.function.MemberDeclaration*/

    double V_th; //

    /* generated by template org.nest.nestml.function.MemberDeclaration*/

    double Vinit; //

    double Vmin; //

    /*Added for stochasticity*/
    double lambda_0;
    double tau_V;

    /* generated by template org.nest.nestml.function.MemberDeclaration*/

    double k1; //

    /* generated by template org.nest.nestml.function.MemberDeclaration*/

    double k2; //

    /* generated by template org.nest.nestml.function.MemberDeclaration*/

    double A1; //

    /* generated by template org.nest.nestml.function.MemberDeclaration*/

    double A2; //

    double kadap;


    /* generated by template org.nest.nestml.function.MemberDeclaration*/

    double I_e; //

    /* generated by template org.nest.nestml.function.MemberDeclaration*/

    long bT_size; //

    /** Initialize parameters to their default values. */
    Parameters_();

    /** Set parameter values from dictionary. */
    void set(const DictionaryDatum &);

    // TODO only for invariants
    /* generated by template org.nest.nestml.function.StructGetterSetter*/

    inline double get_C_m() const { return C_m; }
    inline void set_C_m(const double C_m) { this->C_m = C_m; }

    /* generated by template org.nest.nestml.function.StructGetterSetter*/

    inline double get_tau_m() const { return tau_m; }
    inline void set_tau_m(const double tau_m) { this->tau_m = tau_m; }

  
    /* generated by template org.nest.nestml.function.StructGetterSetter*/

    inline double get_tau_syn1() const { return tau_syn1; }
    inline void set_tau_syn1(const double tau_syn1) {
      this->tau_syn1 = tau_syn1;
    }

 /* generated by template org.nest.nestml.function.StructGetterSetter*/
    inline double get_E_rev1() const { return E_rev1; }
    inline void set_E_rev1(const double E_rev1) {
      this->E_rev1 = E_rev1;
    }

    /* generated by template org.nest.nestml.function.StructGetterSetter*/

    inline double get_tau_syn2() const { return tau_syn2; }
    inline void set_tau_syn2(const double tau_syn2) {
      this->tau_syn2 = tau_syn2;
    }

 /* generated by template org.nest.nestml.function.StructGetterSetter*/
    inline double get_E_rev2() const { return E_rev2; }
    inline void set_E_rev2(const double E_rev2) {
      this->E_rev2 = E_rev2;
    }

    /* generated by template org.nest.nestml.function.StructGetterSetter*/

    inline double get_tau_syn3() const { return tau_syn3; }
    inline void set_tau_syn3(const double tau_syn3) {
      this->tau_syn3 = tau_syn3;
    }

 /* generated by template org.nest.nestml.function.StructGetterSetter*/
    inline double get_E_rev3() const { return E_rev3; }
    inline void set_E_rev3(const double E_rev3) {
      this->E_rev3 = E_rev3;
    }

    /* generated by template org.nest.nestml.function.StructGetterSetter*/

    inline double get_t_ref() const { return t_ref; }
    inline void set_t_ref(const double t_ref) { this->t_ref = t_ref; }

    /* generated by template org.nest.nestml.function.StructGetterSetter*/

    inline double get_E_L() const { return E_L; }
    inline void set_E_L(const double E_L) { this->E_L = E_L; }

    /* generated by template org.nest.nestml.function.StructGetterSetter*/

    inline double get_V_reset() const { return V_reset; }
    inline void set_V_reset(const double V_reset) { this->V_reset = V_reset; }


    /* generated by template org.nest.nestml.function.StructGetterSetter*/

    inline double get_V_th() const { return V_th; }
    inline void set_V_th(const double V_th) { this->V_th = V_th; }

    /* generated by template org.nest.nestml.function.StructGetterSetter*/


    inline double get_Vinit() const { return Vinit; }
    inline void set_Vinit(const double Vinit) {
      this->Vinit = Vinit;
    }

    inline double get_Vmin() const { return Vmin; }
    inline void set_Vmin(const double Vmin) {
      this->Vmin = Vmin;
    }


    /* Added for stochasticity*/
    inline double get_lambda_0() const { return lambda_0; }
    inline void set_lambda_0(const double lambda_0) {
      this->lambda_0 = lambda_0;
    }

    inline double get_tau_V() const { return tau_V; }
    inline void set_tau_V(const double tau_V) {
      this->tau_V = tau_V;
    }

    /* generated by template org.nest.nestml.function.StructGetterSetter*/

    inline double get_k1() const { return k1; }
    inline void set_k1(const double k1) { this->k1 = k1; }

    /* generated by template org.nest.nestml.function.StructGetterSetter*/

    inline double get_k2() const { return k2; }
    inline void set_k2(const double k2) { this->k2 = k2; }

    /* generated by template org.nest.nestml.function.StructGetterSetter*/

    inline double get_A1() const { return A1; }
    inline void set_A1(const double A1) { this->A1 = A1; }

    /* generated by template org.nest.nestml.function.StructGetterSetter*/

    inline double get_A2() const { return A2; }
    inline void set_A2(const double A2) { this->A2 = A2; }

    inline double get_kadap() const { return kadap; }
    inline void set_kadap(const double kadap) { this->kadap = kadap; }


    /* generated by template org.nest.nestml.function.StructGetterSetter*/

    inline double get_I_e() const { return I_e; }
    inline void set_I_e(const double I_e) { this->I_e = I_e; }


    /* generated by template org.nest.nestml.function.StructGetterSetter*/

    inline long get_bT_size() const { return bT_size; }
    inline void set_bT_size(const long bT_size) { this->bT_size = bT_size; }
  };

  /**
  * Dynamic state of the neuron.
  *
  *
  *
  * These are the state variables that are advanced in time by calls to
  * @c update(). In many models, some or all of them can be set by the user
  * through @c SetStatus. The state variables are initialized from the model
  * prototype when the node is created. State variables are reset by @c
  * ResetNetwork.
  *
  * @note State_ need neither copy constructor nor @c operator=(), since
  *       all its members are copied properly by the default copy constructor
  *       and assignment operator. Important:
  *       - If State_ contained @c Time members, you need to define the
  *         assignment operator to recalibrate all members of type @c Time . You
  *         may also want to define the assignment operator.
  *       - If State_ contained members that cannot copy themselves, such
  *         as C-style arrays, you need to define the copy constructor and
  *         assignment operator to copy those members.
  */



  struct State_ {
    /**
     * Enumeration identifying elements in state vector State_::y_.
     * This enum identifies the elements of the vector. It must be public to be
     * accessible from the iteration function. The last two elements of this
     * enum (DG, G) will be repeated
     * n times at the end of the state vector State_::y_ with n being the number
     * of synapses.
     */
    //! Symbolic indices to the elements of the state vector y_
    enum StateVecElems {
      V_m,
      I_dep,
      I_adap,
      G1, 
      DG1,
      G2, 
      DG2,
      G3, 
      DG3,
      STATE_VEC_SIZE			// This is the minimum state vector size
    };
    //! state vector, must be C-array for GSL solver

    double y_[STATE_VEC_SIZE];//  - an array of all the state variables undergoing

    double time;

    double I_syn; //

    /* generated by template org.nest.nestml.function.MemberDeclaration*/

    double I_tot; //

    State_();

    /**
    * Set state values from dictionary.
    */
    void set(const DictionaryDatum &, const Parameters_ &);
  };

  /**
  * Internal variables of the neuron.
  *
  *
  *
  * These variables must be initialized by @c calibrate, which is called before
  * the first call to @c update() upon each call to @c Simulate.
  * @node Variables_ needs neither constructor, copy constructor or assignment
  * operator,
  *       since it is initialized by @c calibrate(). If Variables_ has members
  * that
  *       cannot destroy themselves, Variables_ will need a destructor.
  */
  struct Variables_ {
    /* generated by template org.nest.nestml.function.MemberDeclaration*/

    librandom::RngPtr rng_; // random number generator of my own thread

    long RefractoryCounts; //

    /* generated by template org.nest.nestml.function.MemberDeclaration*/

    long r; //

    /* generated by template org.nest.nestml.function.MemberDeclaration*/

    long receptors; // Number of receptors


    std::vector<double> G0; //


    /* generated by template org.nest.nestml.function.MemberDeclaration*/

    std::vector<double> bufferT; //

    /* generated by template org.nest.nestml.function.MemberDeclaration*/

    double tot_cur; //
    
    double V_th;

    double I_gen;			// To monitor external generator current

    double sum_buffer;


    // Added for R2 update
    double R2;
    double old_Vm;
    double new_Vm;
    double old_slope; //
    double new_slope;

    /* generated by template org.nest.nestml.function.StructGetterSetter*/

    inline long get_RefractoryCounts() const { return RefractoryCounts; }
    inline void set_RefractoryCounts(const long RefractoryCounts) {
      this->RefractoryCounts = RefractoryCounts;
    }

    /* generated by template org.nest.nestml.function.StructGetterSetter*/

    inline long get_r() const { return r; }
    inline void set_r(const long r) { this->r = r; }

    /* generated by template org.nest.nestml.function.StructGetterSetter*/

    inline long get_receptors() const{return receptors; }
    inline void set_receptors(const long receptors) {
      this->receptors = receptors;
    }


    /* generated by template org.nest.nestml.function.StructGetterSetter*/

    inline std::vector<double> get_G0() const { return G0; }
    inline void set_G0(const std::vector<double> G0) {
      this->G0 = G0;
    }


    /* generated by template org.nest.nestml.function.StructGetterSetter*/

    inline std::vector<double> get_bufferT() const { return bufferT; }
    inline void set_bufferT(const std::vector<double> bufferT) {
      this->bufferT = bufferT;
    }

    inline double get_Vth() const { return V_th; }
    inline void set_Vth(const double V_th) { this->V_th = V_th; }

    inline double get_I_gen() const { return I_gen; }
    inline void set_I_gen(const double I_gen) { this->I_gen = I_gen; }

    inline double get_sum_buffer() const { return sum_buffer; }
    inline void set_sum_buffer(const double sum_buffer) { this->sum_buffer = sum_buffer; }

    /* generated by template org.nest.nestml.function.StructGetterSetter*/
    inline double get_tot_cur() const { return tot_cur; }
    inline void set_tot_cur(const double tot_cur) { this->tot_cur = tot_cur; }

    inline double get_R2() const { return R2; }
    inline void set_R2(const double R2) { this->R2 = R2; }

    inline double get_old_Vm() const { return old_Vm; }
    inline void set_old_Vm(const double old_Vm) { this->old_Vm = old_Vm; }

    inline double get_new_Vm() const { return new_Vm; }
    inline void set_new_Vm(const double new_Vm) { this->new_Vm = new_Vm; }

    inline double get_old_slope() const { return old_slope; }
    inline void set_old_slope(const double old_slope) { this->old_slope = old_slope; }

    inline double get_new_slope() const { return new_slope; }
    inline void set_new_slope(const double new_slope) { this->new_slope = new_slope; }

  };

  /**
    * Buffers of the neuron.
    * Ususally buffers for incoming spikes and data logged for analog recorders.
    * Buffers must be initialized by @c init_buffers_(), which is called before
    * @c calibrate() on the first call to @c Simulate after the start of NEST,
    * ResetKernel or ResetNetwork.
    * @node Buffers_ needs neither constructor, copy constructor or assignment
   * operator,
    *       since it is initialized by @c init_nodes_(). If Buffers_ has members
   * that
    *       cannot destroy themselves, Buffers_ will need a destructor.
    */
  struct Buffers_ {
    Buffers_(eglif_cond_alpha_multisyn &);
    Buffers_(const Buffers_ &, eglif_cond_alpha_multisyn &);

    inline nest::RingBuffer &get_spikes_r1() { return spikes_r1; }
    nest::RingBuffer spikes_r1;
    //!< Buffer incoming Buffers through delay, as sum
    ;
    double spikes_r1_last_value_;

    inline nest::RingBuffer &get_spikes_r2() { return spikes_r2; }
    nest::RingBuffer spikes_r2;
    //!< Buffer incoming Buffers through delay, as sum
    ;
    double spikes_r2_last_value_;

    inline nest::RingBuffer &get_spikes_r3() { return spikes_r3; }
    nest::RingBuffer spikes_r3;
    //!< Buffer incoming Buffers through delay, as sum
    ;
    double spikes_r3_last_value_;

    inline nest::RingBuffer &get_currents() { return currents; }
    nest::RingBuffer currents;
    //!< Buffer incoming Buffers through delay, as sum
    ;
    double currents_last_value_;

    /** Logger for all analog data */
    nest::UniversalDataLogger<eglif_cond_alpha_multisyn> logger_;

    std::vector<long> receptor_types_;

    /* GSL ODE stuff */
    gsl_odeiv_step *s_;    //!< stepping function
    gsl_odeiv_control *c_; //!< adaptive stepsize control function
    gsl_odeiv_evolve *e_;  //!< evolution function
    gsl_odeiv_system sys_; //!< struct describing system
  };

private:
  /**
  * @defgroup pif_members Member variables of neuron model.
  * Each model neuron should have precisely the following four data members,
  * which are one instance each of the parameters, state, buffers and variables
  * structures. Experience indicates that the state and variables member should
  * be next to each other to achieve good efficiency (caching).
  * @note Devices require one additional data member, an instance of the @c
  * Device
  *       child class they belong to.
  * @{
  */
  Parameters_ P_; //!< Free parameters.
  State_ S_;      //!< Dynamic state.
  Variables_ V_;  //!< Internal Variables
  Buffers_ B_;    //!< Buffers.

  //! Mapping of recordables names to access functions
  static nest::RecordablesMap<eglif_cond_alpha_multisyn> recordablesMap_;

  friend int eglif_cond_alpha_multisyn_dynamics(double, const double y[],
                                                       double f[], void *pnode);
 // friend int eglif_cond_alpha_multisyn_dynamics(double, const double*,
  //                                                     double*, void *pnode);

  /** @} */
}; /* neuron eglif_cond_alpha_multisyn */

inline nest::port eglif_cond_alpha_multisyn::send_test_event(
    nest::Node &target, nest::rport receptor_type, nest::synindex, bool) {
  // You should usually not change the code in this function.
  // It confirms that the target of connection @c c accepts @c nest::SpikeEvent
  // on
  // the given @c receptor_type.
  nest::SpikeEvent e;
  e.set_sender(*this);

  return target.handles_test_event(e, receptor_type);
}

//   Questa funzione non c'รจ nell'AEIF cond alpha multisyn
inline nest::port
eglif_cond_alpha_multisyn::handles_test_event(nest::SpikeEvent &,
                                                     nest::port receptor_type) {

  if (receptor_type <= 0 ||
      receptor_type > 3	){		//static_cast<nest::port>(get_receptors())) {
    // TODO refactor me. The code assumes that there is only one. Check by coco.
    std::cout<<get_receptors()<<std::endl;
   std::cout<<receptor_type<<std::endl;
    throw nest::IncompatibleReceptorType(receptor_type, get_name(),
                                         "SpikeEvent");
  }

  return receptor_type;
}


inline nest::port
eglif_cond_alpha_multisyn::handles_test_event(nest::CurrentEvent &,
                                                     nest::port receptor_type) {
  // You should usually not change the code in this function.
  // It confirms to the connection management system that we are able
  // to handle @c CurrentEvent on port 0. You need to extend the function
  // if you want to differentiate between input ports.
  if (receptor_type != 0)
    throw nest::UnknownReceptorType(receptor_type, get_name());
  return 0;
}
inline nest::port eglif_cond_alpha_multisyn::handles_test_event(
    nest::DataLoggingRequest &dlr, nest::port receptor_type) {
  // You should usually not change the code in this function.
  // It confirms to the connection management system that we are able
  // to handle @c DataLoggingRequest on port 0.
  // The function also tells the built-in UniversalDataLogger that this node
  // is recorded from and that it thus needs to collect data during simulation.
  if (receptor_type != 0)
    throw nest::UnknownReceptorType(receptor_type, get_name());

  return B_.logger_.connect_logging_device(dlr, recordablesMap_);
}

// TODO call get_status on used or internal components
inline void
eglif_cond_alpha_multisyn::get_status(DictionaryDatum &__d) const {
  /* generated by template org.nest.nestml.function.WriteInDictionary*/

  /* generated by template org.nest.nestml.function.WriteInDictionary*/

  def<double>(__d, "C_m", get_C_m());

  /* generated by template org.nest.nestml.function.WriteInDictionary*/

  def<double>(__d, "tau_m", get_tau_m());


  /* generated by template org.nest.nestml.function.WriteInDictionary*/

  def<double>(__d, "tau_syn1", get_tau_syn1());

/* generated by template org.nest.nestml.function.WriteInDictionary*/

  def<double>(__d, "E_rev1", get_E_rev1());


  /* generated by template org.nest.nestml.function.WriteInDictionary*/

  def<double>(__d, "tau_syn2", get_tau_syn2());

/* generated by template org.nest.nestml.function.WriteInDictionary*/

  def<double>(__d, "E_rev2", get_E_rev2());


  /* generated by template org.nest.nestml.function.WriteInDictionary*/

  def<double>(__d, "tau_syn3", get_tau_syn3());

/* generated by template org.nest.nestml.function.WriteInDictionary*/

  def<double>(__d, "E_rev3", get_E_rev3());


  /* generated by template org.nest.nestml.function.WriteInDictionary*/

  def<double>(__d, "t_ref", get_t_ref());

  /* generated by template org.nest.nestml.function.WriteInDictionary*/

  def<double>(__d, "E_L", get_E_L());

  /* generated by template org.nest.nestml.function.WriteInDictionary*/

  def<double>(__d, "V_reset", get_V_reset());

  /* generated by template org.nest.nestml.function.WriteInDictionary*/

  def<double>(__d, "V_th", get_V_th());

  /* generated by template org.nest.nestml.function.WriteInDictionary*/

  def<double>(__d, "Vinit", get_Vinit());

  def<double>(__d, "Vmin", get_Vmin());

  /* Added for stochasticity*/
  def<double>(__d, "lambda_0", get_lambda_0());

  def<double>(__d, "tau_V", get_tau_V());

  /* generated by template org.nest.nestml.function.WriteInDictionary*/

  def<double>(__d, "k1", get_k1());

  /* generated by template org.nest.nestml.function.WriteInDictionary*/

  def<double>(__d, "k2", get_k2());

  /* generated by template org.nest.nestml.function.WriteInDictionary*/

  def<double>(__d, "A1", get_A1());

  /* generated by template org.nest.nestml.function.WriteInDictionary*/

  def<double>(__d, "A2", get_A2());

  def<double>(__d, "kadap", get_kadap());


  /* generated by template org.nest.nestml.function.WriteInDictionary*/

  def<double>(__d, "I_e", get_I_e());
 

  /* generated by template org.nest.nestml.function.WriteInDictionary*/

  def<long>(__d, "bT_size", get_bT_size());

  /* generated by template org.nest.nestml.function.WriteInDictionary*/

  def<double>(__d, "I_dep", get_I_dep());

  /* generated by template org.nest.nestml.function.WriteInDictionary*/

  def<double>(__d, "I_adap", get_I_adap());

  /* generated by template org.nest.nestml.function.WriteInDictionary*/

  def<double>(__d, "V_m", get_V_m());

  /* generated by template org.nest.nestml.function.WriteInDictionary*/

  def<double>(__d, "G1", get_G1());

  def<double>(__d, "DG1", get_DG1());

  def<double>(__d, "G2", get_G2());

  def<double>(__d, "DG2", get_DG2());

  def<double>(__d, "G3", get_G3());

  def<double>(__d, "DG3", get_DG3());

  /* generated by template org.nest.nestml.function.WriteInDictionary*/

  //def<double>(__d, "time", get_time());

  /* generated by template org.nest.nestml.function.WriteInDictionary*/


  (*__d)[nest::names::recordables] = recordablesMap_.get_list();
}

inline void
eglif_cond_alpha_multisyn::set_status(const DictionaryDatum &__d) {

  /* generated by template org.nest.nestml.function.ReadFromDictionary*/

  double tmp_C_m;
  if (updateValue<double>(__d, "C_m", tmp_C_m)) {
    set_C_m(tmp_C_m);
  }

  /* generated by template org.nest.nestml.function.ReadFromDictionary*/

  double tmp_tau_m;
  if (updateValue<double>(__d, "tau_m", tmp_tau_m)) {
    set_tau_m(tmp_tau_m);
  }

  /* generated by template org.nest.nestml.function.ReadFromDictionary*/

  double tmp_tau_syn1;
  if (updateValue<double>(__d, "tau_syn1", tmp_tau_syn1)) {
    set_tau_syn1(tmp_tau_syn1);
  }


/* generated by template org.nest.nestml.function.ReadFromDictionary*/
  double tmp_E_rev1;
  if (updateValue<double>(__d, "E_rev1", tmp_E_rev1)) {
    set_E_rev1(tmp_E_rev1);
  }

  /* generated by template org.nest.nestml.function.ReadFromDictionary*/

  double tmp_tau_syn2;
  if (updateValue<double>(__d, "tau_syn2", tmp_tau_syn2)) {
    set_tau_syn2(tmp_tau_syn2);
  }


/* generated by template org.nest.nestml.function.ReadFromDictionary*/
  double tmp_E_rev2;
  if (updateValue<double>(__d, "E_rev2", tmp_E_rev2)) {
    set_E_rev2(tmp_E_rev2);
  }

  /* generated by template org.nest.nestml.function.ReadFromDictionary*/

  double tmp_tau_syn3;
  if (updateValue<double>(__d, "tau_syn3", tmp_tau_syn3)) {
    set_tau_syn3(tmp_tau_syn3);
  }


/* generated by template org.nest.nestml.function.ReadFromDictionary*/
  double tmp_E_rev3;
  if (updateValue<double>(__d, "E_rev3", tmp_E_rev3)) {
    set_E_rev3(tmp_E_rev3);
  }

  /* generated by template org.nest.nestml.function.ReadFromDictionary*/

  double tmp_t_ref;
  if (updateValue<double>(__d, "t_ref", tmp_t_ref)) {
    set_t_ref(tmp_t_ref);
  }

  /* generated by template org.nest.nestml.function.ReadFromDictionary*/

  double tmp_E_L;
  if (updateValue<double>(__d, "E_L", tmp_E_L)) {
    set_E_L(tmp_E_L);
  }




  double tmp_V_reset;
  if (updateValue<double>(__d, "V_reset", tmp_V_reset)) {
    set_V_reset(tmp_V_reset);
  }


  /* generated by template org.nest.nestml.function.ReadFromDictionary*/

  double tmp_V_th;
  if (updateValue<double>(__d, "V_th", tmp_V_th)) {
    set_V_th(tmp_V_th);
  }

  /* generated by template org.nest.nestml.function.ReadFromDictionary*/


  double tmp_Vinit;
  if (updateValue<double>(__d, "Vinit", tmp_Vinit)) {
    set_Vinit(tmp_Vinit);
  }

  double tmp_Vmin;
  if (updateValue<double>(__d, "Vmin", tmp_Vmin)) {
    set_Vmin(tmp_Vmin);
  }


  /*Added for stochasticity*/
  double tmp_lambda_0;
  if (updateValue<double>(__d, "lambda_0", tmp_lambda_0)) {
    set_lambda_0(tmp_lambda_0);
  }

  double tmp_tau_V;
  if (updateValue<double>(__d, "tau_V", tmp_tau_V)) {
    set_tau_V(tmp_tau_V);
  }

  /* generated by template org.nest.nestml.function.ReadFromDictionary*/

  double tmp_k1;
  if (updateValue<double>(__d, "k1", tmp_k1)) {
    set_k1(tmp_k1);
  }

  /* generated by template org.nest.nestml.function.ReadFromDictionary*/

  double tmp_k2;
  if (updateValue<double>(__d, "k2", tmp_k2)) {
    set_k2(tmp_k2);
  }

  /* generated by template org.nest.nestml.function.ReadFromDictionary*/

  double tmp_A1;
  if (updateValue<double>(__d, "A1", tmp_A1)) {
    set_A1(tmp_A1);
  }

  /* generated by template org.nest.nestml.function.ReadFromDictionary*/

  double tmp_A2;
  if (updateValue<double>(__d, "A2", tmp_A2)) {
    set_A2(tmp_A2);
  }

  double tmp_kadap;
  if (updateValue<double>(__d, "kadap", tmp_kadap)) {
    set_kadap(tmp_kadap);
  }

  /* generated by template org.nest.nestml.function.ReadFromDictionary*/

  double tmp_I_e;
  if (updateValue<double>(__d, "I_e", tmp_I_e)) {
    set_I_e(tmp_I_e);
  }
  
  /* generated by template org.nest.nestml.function.ReadFromDictionary*/

  long tmp_bT_size;
  if (updateValue<long>(__d, "bT_size", tmp_bT_size)) {
    set_bT_size(tmp_bT_size);
  }

  /* generated by template org.nest.nestml.function.ReadFromDictionary*/

  double tmp_I_dep;
  if (updateValue<double>(__d, "I_dep", tmp_I_dep)) {
    set_I_dep(tmp_I_dep);
  }

  /* generated by template org.nest.nestml.function.ReadFromDictionary*/

  double tmp_I_adap;
  if (updateValue<double>(__d, "I_adap", tmp_I_adap)) {
    set_I_adap(tmp_I_adap);
  }


  /* generated by template org.nest.nestml.function.ReadFromDictionary*/

  double tmp_V_m;
  if (updateValue<double>(__d, "V_m", tmp_V_m)) {
    set_V_m(tmp_V_m);
  }


  double tmp_G1;
  if (updateValue<double>(__d, "G1", tmp_G1)) {
    set_G1(tmp_G1);
  }

  double tmp_DG1;
  if (updateValue<double>(__d, "DG1", tmp_DG1)) {
    set_DG1(tmp_DG1);
  }

  double tmp_G2;
  if (updateValue<double>(__d, "G2", tmp_G2)) {
    set_G2(tmp_G2);
  }

  double tmp_DG2;
  if (updateValue<double>(__d, "DG2", tmp_DG2)) {
    set_DG2(tmp_DG2);
  }

  double tmp_G3;
  if (updateValue<double>(__d, "G3", tmp_G3)) {
    set_G3(tmp_G3);
  }

  double tmp_DG3;
  if (updateValue<double>(__d, "DG3", tmp_DG3)) {
    set_DG3(tmp_DG3);
  }

  /* generated by template org.nest.nestml.function.ReadFromDictionary*/
/*
  double tmp_time;
  if (updateValue<double>(__d, "time", tmp_time)) {
    set_time(tmp_time);
  }
*/

  // We now know that (ptmp, stmp) are consistent. We do not
  // write them back to (P_, S_) before we are also sure that
  // the properties to be set in the parent class are internally
  // consistent.
  Archiving_Node::set_status(__d);

  // if we get here, temporaries contain consistent set of properties
  // P_ = ptmp;
  // S_ = stmp;
};

#endif /* #ifndef EGLIF_COND_ALPHA_MULTISYN  */
#endif /* HAVE GSL */