/*
 *       File name:  libcn/hosted-synapses.hh
 *         Project:  cnrun
 *          Author:  Andrei Zavada <johnhommer@gmail.com>
 *                   building on original work by Thomas Nowotny <tnowotny@ucsd.edu>
 * Initial version:  2009-04-01
 *
 *         Purpose:  hosted synapse classes (those having their
 *                   state vars on parent model's integration vectors)
 *
 *         License:  GPL-2+
 */

#ifndef CNRUN_LIBCN_HOSTEDSYNAPSES_H_
#define CNRUN_LIBCN_HOSTEDSYNAPSES_H_

#if HAVE_CONFIG_H && !defined(VERSION)
#  include "config.h"
#endif

#include <vector>
#include <queue>
#include <cfloat>

#include "base-synapse.hh"
#include "hosted-attr.hh"
#include "mx-attr.hh"
#include "hosted-neurons.hh"
#include "standalone-neurons.hh"


using namespace std;

namespace cnrun {

class C_HostedSynapse
  : public C_BaseSynapse, public C_HostedAttributes {

        DELETE_DEFAULT_METHODS (C_HostedSynapse)

    protected:
        C_HostedSynapse (TUnitType type_,
                         C_BaseNeuron *source_, C_BaseNeuron *target_,
                         double g_, CModel*, int s_mask = 0,
                         TIncludeOption = TIncludeOption::is_last);
    public:
        void reset_vars();
        double &var_value( size_t);
        const double &get_var_value( size_t) const;

        double   S() const; // needs access to parent model var vector, defined in model.h
        double   S( vector<double> &b) const        { return b[idx+0]; }
        double& dS( vector<double> &b) const        { return b[idx+0]; }
};




// Note on synapses classification per source/target being a tonic
// (rate) or phasic (discrete) unit:
//
// * Where a synapse connects _to_ a Rate neuron, it will have Ediff
//   in lieu of Esyn and compute Isyn accordingly, otherwise inheriting
//   its parameters.
//
// * Where a synapse connects _from_ a Rate unit, its derivative
//   method follows a completely different equation.  It now has a
//   different set of parameters, too.

// The suffix in a class name, _xy, means x, source, y, target, with
// `d' for discrete, `r' for rate.


// The `multiplexing' part has a relevance for the source of the
// synapse, with magic to collect and multiplex more than a single
// spike per dt.
//
// * The source is a specialized (`dot'), and inherently phasic, unit.
// * All parameters are inherited from the base class.


// Alpha-Beta family

class CSynapseAB_dd
  : public C_HostedSynapse {

        DELETE_DEFAULT_METHODS (CSynapseAB_dd)

    public:
        CSynapseAB_dd (C_BaseNeuron *insource, C_BaseNeuron *intarget,
                       double ing, CModel *inM, int s_mask = 0,
                       TIncludeOption include_option = TIncludeOption::is_last,
                       TUnitType alt_type = YT_AB_DD)
              : C_HostedSynapse (alt_type, insource, intarget,
                                 ing, inM, s_mask, include_option)
                {}

        enum {
                _Esyn_, _Epre_, _alpha_, _beta_, _rtime_
        };

        double Isyn( const C_BaseNeuron &with_neuron, double g) const  __attribute__ ((hot))
                {
                        return -g * S() * (with_neuron.E() - P[_Esyn_]);
//                        return -P[_gsyn_] * S() * (_target->E() - P[_Esyn_]);
                }
        double Isyn( vector<double>& b, const C_BaseNeuron &with_neuron, double g) const  __attribute__ ((hot))
                {
                        return -g * S(b) * (with_neuron.E(b) - P[_Esyn_]);
//                        return -P[_gsyn_] * S(b) * (_target->E(b) - P[_Esyn_]);
                }

        void derivative( vector<double>&, vector<double>&)  __attribute__ ((hot));
};


class CNeuronHHRate;

// TODO
class CSynapseAB_dr;
class CSynapseAB_rd;


class CSynapseAB_rr
  : public C_HostedSynapse {

        DELETE_DEFAULT_METHODS (CSynapseAB_rr)

    public:
        CSynapseAB_rr (C_BaseNeuron *insource, C_BaseNeuron *intarget,
                       double ing, CModel *inM, int s_mask = 0,
                       TIncludeOption include_option = TIncludeOption::is_last,
                       TUnitType alt_type = YT_AB_RR)
              : C_HostedSynapse( alt_type, insource, intarget,
                                 ing, inM, s_mask, include_option)
                {}

        enum {
                _Ediff_, _alpha_, _beta_, _T_, _sigma_
        };

      // supply own Isyn to avoid referencing target->E
        double Isyn( const C_BaseNeuron &with_neuron, double g) const
                {
                        return -g * S() * P[_Ediff_];
                }
        double Isyn( vector<double>& x, const C_BaseNeuron &with_neuron, double g) const
                {
                        return -g * S(x) * P[_Ediff_];
                }

        void derivative( vector<double>&, vector<double>&);

        void param_changed_hook()
                {
                        _numerator = exp( P[_beta_] * P[_T_]) + 1;
                }
    private:
        double  _numerator;
};








class CSynapseMxAB_dd
  : public CSynapseAB_dd, public C_MultiplexingAttributes {

        DELETE_DEFAULT_METHODS (CSynapseMxAB_dd)

    public:
        CSynapseMxAB_dd (C_BaseNeuron *insource, C_BaseNeuron *intarget,
                         double ing, CModel *inM, int s_mask = 0,
                         TIncludeOption include_option = TIncludeOption::is_last,
                         TUnitType alt_type = YT_MXAB_DD)
              : CSynapseAB_dd (insource, intarget,
                               ing, inM, s_mask, include_option,
                               alt_type)
                {}

        void reset_state()
                {
                        C_HostedSynapse::reset_state();
                        C_MultiplexingAttributes::reset();
                }

      // because Mx*'s synapse source is always a standalone, non-integratable neuron,
      // which don't propagate vars onto M->V, we fold S(x) to make the actual S value available
      // from within the integrator
        double S() const                        { return C_HostedSynapse::S(); }
        double S( vector<double> &unused) const { return C_HostedSynapse::S(); }

        void derivative( vector<double>&, vector<double>&)  __attribute__ ((hot));

    private:
        friend class CModel;
        void update_queue();
};





class CSynapseMxAB_dr
  : public CSynapseMxAB_dd {

        DELETE_DEFAULT_METHODS (CSynapseMxAB_dr)

    public:
        CSynapseMxAB_dr (C_BaseNeuron *insource, C_BaseNeuron *intarget,
                         double ing, CModel *inM, int s_mask = 0,
                         TIncludeOption include_option = TIncludeOption::is_last)
              : CSynapseMxAB_dd (insource, intarget,
                                 ing, inM, s_mask, include_option,
                                 YT_MXAB_DR)
                {}

        enum { _Ediff_, /* ... */ };
        double Isyn( const C_BaseNeuron &with_neuron, double g) const
                {
                        return -g * S() * P[_Ediff_];
                }
        double Isyn( vector<double>& unused, const C_BaseNeuron &with_neuron, double g) const
                {
                        return -g * S() * P[_Ediff_];
                }
};









class CSynapseABMinus_dd
  : public CSynapseAB_dd {

        DELETE_DEFAULT_METHODS (CSynapseABMinus_dd)

    public:
        CSynapseABMinus_dd (C_BaseNeuron *insource, C_BaseNeuron *intarget,
                            double ing, CModel *inM, int s_mask = 0,
                            TIncludeOption include_option = TIncludeOption::is_last)
              : CSynapseAB_dd (insource, intarget,
                               ing, inM, s_mask, include_option,
                               YT_ABMINUS_DD)
                {}

        enum {
                _Esyn_, _Epre_, _alpha_, _beta_, _rtime_, _sigma_
        };

        void derivative( vector<double>&, vector<double>&);
};


// TODO
class CSynapseABMinus_dr;
class CSynapseABMinus_rd;
class CSynapseABMinus_rr;




// Rall

class CSynapseRall_dd
  : public C_HostedSynapse {

        DELETE_DEFAULT_METHODS (CSynapseRall_dd)

    public:
        CSynapseRall_dd (C_BaseNeuron *insource, C_BaseNeuron *intarget,
                         double ing, CModel *inM, int s_mask = 0,
                         TIncludeOption include_option = TIncludeOption::is_last)
              : C_HostedSynapse (YT_RALL_DD, insource, intarget,
                                 ing, inM, s_mask, include_option)
                {}

        double&  R( vector<double>& b)        { return b[idx+1]; }
        double& dR( vector<double>& b)        { return b[idx+1]; }

        enum {
                _Esyn_, _Epre_, _tau_, _sigma_
        };

        double Isyn( const C_BaseNeuron &with_neuron, double g) const
                {
                        return -g * S() * (with_neuron.E() - P[_Esyn_]);
                }
        double Isyn( vector<double>&b, const C_BaseNeuron &with_neuron, double g) const
                {
                        return -g * S(b) * (with_neuron.E(b) - P[_Esyn_]);
                }

        void derivative( vector<double>&, vector<double>&);
};

// TODO
class CSynapseRall_rd;
class CSynapseRall_dr;
class CSynapseRall_rr;

}

#endif

// Local Variables:
// Mode: c++
// indent-tabs-mode: nil
// tab-width: 8
// c-basic-offset: 8
// End:
