#ifndef VIENNA_RNA_PACKAGE_PARAMS_H
#define VIENNA_RNA_PACKAGE_PARAMS_H

#ifdef VRNA_WARN_DEPRECATED
# if defined(__clang__)
#  define DEPRECATED(func, msg) func __attribute__ ((deprecated("", msg)))
# elif defined(__GNUC__)
#  define DEPRECATED(func, msg) func __attribute__ ((deprecated(msg)))
# else
#  define DEPRECATED(func, msg) func
# endif
#else
# define DEPRECATED(func, msg) func
#endif

/**
 *  @file     params/basic.h
 *  @ingroup  energy_parameters
 *  @brief    Functions to deal with sets of energy parameters
 */

/**
 *  @addtogroup energy_parameters
 *  @{
 */

/** @brief Typename for the free energy parameter data structure #vrna_params */
typedef struct  vrna_param_s vrna_param_t;
/** @brief Typename for the Boltzmann factor data structure #vrna_exp_params */
typedef struct  vrna_exp_param_s vrna_exp_param_t;

#include <ViennaRNA/params/constants.h>
#include <ViennaRNA/datastructures/basic.h>
#include <ViennaRNA/fold_compound.h>
#include <ViennaRNA/model.h>

#define   VRNA_GQUAD_MAX_STACK_SIZE     7
#define   VRNA_GQUAD_MIN_STACK_SIZE     2
#define   VRNA_GQUAD_MAX_LINKER_LENGTH  15
#define   VRNA_GQUAD_MIN_LINKER_LENGTH  1
#define   VRNA_GQUAD_MIN_BOX_SIZE       ((4 * VRNA_GQUAD_MIN_STACK_SIZE) + \
                                         (3 * VRNA_GQUAD_MIN_LINKER_LENGTH))
#define   VRNA_GQUAD_MAX_BOX_SIZE       ((4 * VRNA_GQUAD_MAX_STACK_SIZE) + \
                                         (3 * VRNA_GQUAD_MAX_LINKER_LENGTH))

/**
 *  @brief The datastructure that contains temperature scaled energy parameters.
 */
struct vrna_param_s {
  int       id;
  int       stack[NBPAIRS + 1][NBPAIRS + 1];
  int       hairpin[31];
  int       bulge[MAXLOOP + 1];
  int       internal_loop[MAXLOOP + 1];
  int       mismatchExt[NBPAIRS + 1][5][5];
  int       mismatchI[NBPAIRS + 1][5][5];
  int       mismatch1nI[NBPAIRS + 1][5][5];
  int       mismatch23I[NBPAIRS + 1][5][5];
  int       mismatchH[NBPAIRS + 1][5][5];
  int       mismatchM[NBPAIRS + 1][5][5];
  int       dangle5[NBPAIRS + 1][5];
  int       dangle3[NBPAIRS + 1][5];
  int       int11[NBPAIRS + 1][NBPAIRS + 1][5][5];
  int       int21[NBPAIRS + 1][NBPAIRS + 1][5][5][5];
  int       int22[NBPAIRS + 1][NBPAIRS + 1][5][5][5][5];
  int       ninio[5];
  double    lxc;
  int       MLbase;
  int       MLintern[NBPAIRS + 1];
  int       MLclosing;
  int       TerminalAU;
  int       DuplexInit;
  int       Tetraloop_E[200];
  char      Tetraloops[1401];
  int       Triloop_E[40];
  char      Triloops[241];
  int       Hexaloop_E[40];
  char      Hexaloops[1801];
  int       TripleC;
  int       MultipleCA;
  int       MultipleCB;
  int       gquad[VRNA_GQUAD_MAX_STACK_SIZE + 1][3 * VRNA_GQUAD_MAX_LINKER_LENGTH + 1];
  int       gquadLayerMismatch;
  int       gquadLayerMismatchMax;

  double    temperature;      /**<  @brief  Temperature used for loop contribution scaling */

  vrna_md_t model_details;    /**<  @brief  Model details to be used in the recursions */
  char      param_file[256];  /**<  @brief  The filename the parameters were derived from, or empty string if they represent the default */
  int       SaltStack;
  int       SaltLoop[MAXLOOP + 2];
  double    SaltLoopDbl[MAXLOOP + 2];
  int       SaltMLbase;
  int       SaltMLintern;
  int       SaltMLclosing;
  int       SaltDPXInit;
};

/**
 *  @brief  The data structure that contains temperature scaled Boltzmann weights of the energy parameters.
 */
struct vrna_exp_param_s {
  int     id;   /**<  @brief  An identifier for the data structure
                 *    @deprecated This attribute will be removed in version 3
                 */
  double  expstack[NBPAIRS + 1][NBPAIRS + 1];
  double  exphairpin[31];
  double  expbulge[MAXLOOP + 1];
  double  expinternal[MAXLOOP + 1];
  double  expmismatchExt[NBPAIRS + 1][5][5];
  double  expmismatchI[NBPAIRS + 1][5][5];
  double  expmismatch23I[NBPAIRS + 1][5][5];
  double  expmismatch1nI[NBPAIRS + 1][5][5];
  double  expmismatchH[NBPAIRS + 1][5][5];
  double  expmismatchM[NBPAIRS + 1][5][5];
  double  expdangle5[NBPAIRS + 1][5];
  double  expdangle3[NBPAIRS + 1][5];
  double  expint11[NBPAIRS + 1][NBPAIRS + 1][5][5];
  double  expint21[NBPAIRS + 1][NBPAIRS + 1][5][5][5];
  double  expint22[NBPAIRS + 1][NBPAIRS + 1][5][5][5][5];
  double  expninio[5][MAXLOOP + 1];
  double  lxc;
  double  expMLbase;
  double  expMLintern[NBPAIRS + 1];
  double  expMLclosing;
  double  expTermAU;
  double  expDuplexInit;
  double  exptetra[40];
  double  exptri[40];
  double  exphex[40];
  char    Tetraloops[1401];
  double  expTriloop[40];
  char    Triloops[241];
  char    Hexaloops[1801];
  double  expTripleC;
  double  expMultipleCA;
  double  expMultipleCB;
  double  expgquad[VRNA_GQUAD_MAX_STACK_SIZE + 1][3 * VRNA_GQUAD_MAX_LINKER_LENGTH + 1];
  double  expgquadLayerMismatch;
  int     gquadLayerMismatchMax;

  double  kT;
  double  pf_scale;           /**<  @brief    Scaling factor to avoid over-/underflows */

  double  temperature;        /**<  @brief    Temperature used for loop contribution scaling */
  double  alpha;              /**<  @brief    Scaling factor for the thermodynamic temperature
                               *    @details  This allows for temperature scaling in Boltzmann
                               *              factors independently from the energy contributions.
                               *              The resulting Boltzmann factors are then computed by
                               *              @f$ e^{-E/(\alpha \cdot K \cdot T)} @f$
                               */

  vrna_md_t model_details;    /**<  @brief  Model details to be used in the recursions */
  char      param_file[256];  /**<  @brief  The filename the parameters were derived from, or empty string if they represent the default */

  double    expSaltStack;
  double    expSaltLoop[MAXLOOP + 2];
  double    SaltLoopDbl[MAXLOOP + 2];
  int       SaltMLbase;
  int       SaltMLintern;
  int       SaltMLclosing;
  int       SaltDPXInit;
};


/**
 *  @brief  Get a data structure containing prescaled free energy parameters
 *
 *  If a NULL pointer is passed for the model details parameter, the default
 *  model parameters are stored within the requested #vrna_param_t structure.
 *
 *  @see #vrna_md_t, vrna_md_set_default(), vrna_exp_params()
 *
 *  @param  md  A pointer to the model details to store inside the structure (Maybe NULL)
 *  @return     A pointer to the memory location where the requested parameters are stored
 */
vrna_param_t *
vrna_params(vrna_md_t *md);


/**
 *  @brief Get a copy of the provided free energy parameters
 *
 *  If NULL is passed as parameter, a default set of energy parameters is created
 *  and returned.
 *
 *  @see vrna_params(), #vrna_param_t
 *
 *  @param  par   The free energy parameters that are to be copied (Maybe NULL)
 *  @return       A copy or a default set of the (provided) parameters
 */
vrna_param_t *
vrna_params_copy(vrna_param_t *par);


/**
 *  @brief  Get a data structure containing prescaled free energy parameters
 *          already transformed to Boltzmann factors
 *
 *  This function returns a data structure that contains all necessary precomputed
 *  energy contributions for each type of loop.
 *
 *  In contrast to vrna_params(), the free energies within this data structure
 *  are stored as their Boltzmann factors, i.e.
 *
 *  @f$ exp(-E / kT) @f$
 *
 *  where @f$ E @f$ is the free energy.
 *
 *  If a NULL pointer is passed for the model details parameter, the default
 *  model parameters are stored within the requested #vrna_exp_param_t structure.
 *
 *  @see #vrna_md_t, vrna_md_set_default(), vrna_params(), vrna_rescale_pf_params()
 *
 *  @param  md  A pointer to the model details to store inside the structure (Maybe NULL)
 *  @return     A pointer to the memory location where the requested parameters are stored
 */
vrna_exp_param_t *
vrna_exp_params(vrna_md_t *md);


/**
 *  @brief  Get a data structure containing prescaled free energy parameters
 *          already transformed to Boltzmann factors (alifold version)
 *
 *  If a NULL pointer is passed for the model details parameter, the default
 *  model parameters are stored within the requested #vrna_exp_param_t structure.
 *
 *  @see #vrna_md_t, vrna_md_set_default(), vrna_exp_params(), vrna_params()
 *
 *  @param  n_seq   The number of sequences in the alignment
 *  @param  md      A pointer to the model details to store inside the structure (Maybe NULL)
 *  @return         A pointer to the memory location where the requested parameters are stored
 */
vrna_exp_param_t *
vrna_exp_params_comparative(unsigned int  n_seq,
                            vrna_md_t     *md);


/**
 *  @brief Get a copy of the provided free energy parameters (provided as Boltzmann factors)
 *
 *  If NULL is passed as parameter, a default set of energy parameters is created
 *  and returned.
 *
 *  @see vrna_exp_params(), #vrna_exp_param_t
 *
 *  @param  par   The free energy parameters that are to be copied (Maybe NULL)
 *  @return       A copy or a default set of the (provided) parameters
 */
vrna_exp_param_t *
vrna_exp_params_copy(vrna_exp_param_t *par);


/**
 *  @brief  Update/Reset energy parameters data structure within a #vrna_fold_compound_t
 *
 *  Passing NULL as second argument leads to a reset of the energy parameters within
 *  fc to their default values. Otherwise, the energy parameters provided will be copied
 *  over into fc.
 *
 *  @see vrna_params_reset(), #vrna_param_t, #vrna_md_t, vrna_params()
 *
 *  @param  fc    The #vrna_fold_compound_t that is about to receive updated energy parameters
 *  @param  par   The energy parameters used to substitute those within fc (Maybe NULL)
 */
void
vrna_params_subst(vrna_fold_compound_t  *fc,
                  vrna_param_t          *par);


/**
 *  @brief Update the energy parameters for subsequent partition function computations
 *
 *  This function can be used to properly assign new energy parameters for partition
 *  function computations to a #vrna_fold_compound_t. For this purpose, the data of the
 *  provided pointer `params`  will be copied into `fc` and a recomputation of the partition
 *  function scaling factor is issued, if the `pf_scale` attribute of `params` is less than `1.0`.
 *
 *  Passing NULL as second argument leads to a reset of the energy parameters within
 *  fc to their default values
 *
 *  @see  vrna_exp_params_reset(), vrna_exp_params_rescale(), #vrna_exp_param_t, #vrna_md_t,
 *        vrna_exp_params()
 *
 *  @param  fc      The fold compound data structure
 *  @param  params  A pointer to the new energy parameters
 */
void
vrna_exp_params_subst(vrna_fold_compound_t  *fc,
                      vrna_exp_param_t      *params);


/**
 *  @brief Rescale Boltzmann factors for partition function computations
 *
 *  This function may be used to (automatically) rescale the Boltzmann factors used
 *  in partition function computations. Since partition functions over subsequences
 *  can easily become extremely large, the RNAlib internally rescales them to avoid
 *  numerical over- and/or underflow. Therefore, a proper scaling factor @f$s@f$ needs to
 *  be chosen that in turn is then used to normalize the corresponding
 *  partition functions @f$\hat{q}[i,j] = q[i,j] / s^{(j-i+1)}@f$.
 *
 *  This function provides two ways to automatically adjust the scaling
 *  factor.
 *  1. Automatic guess
 *  2. Automatic adjustment according to MFE
 *
 *  Passing `NULL` as second parameter activates the _automatic guess mode_. Here,
 *  the scaling factor is recomputed according to a mean free energy of `184.3*length` cal
 *  for random sequences.
 *  @note This recomputation only takes place if the `pf_scale` attribute of the
 *        `exp_params` data structure contained in `fc` has a value below `1.0`.
 *
 *  On the other hand, if the MFE for a sequence is known, it can be used to recompute
 *  a more robust scaling factor, since it represents the lowest free energy of the entire
 *  ensemble of structures, i.e. the highest Boltzmann factor. To activate this second
 *  mode of _automatic adjustment according to MFE_, a pointer to the MFE value needs to
 *  be passed as second argument. This value is then taken to compute the scaling factor
 *  as @f$ s = exp((sfact * MFE) / kT / length )@f$, where sfact is an additional
 *  scaling weight located in the vrna_md_t data structure of `exp_params` in `fc`.
 *
 *  The computed scaling factor @f$s@f$ will be stored as `pf_scale` attribute of the
 *  `exp_params` data structure in `fc`.
 *
 *  @see vrna_exp_params_subst(), vrna_md_t, vrna_exp_param_t, #vrna_fold_compound_t
 *
 *  @param  fc  The fold compound data structure
 *  @param  mfe A pointer to the MFE (in kcal/mol) or NULL
 */
void
vrna_exp_params_rescale(vrna_fold_compound_t  *fc,
                        double                *mfe);


/**
 *  @brief  Reset free energy parameters within a #vrna_fold_compound_t
 *          according to provided, or default model details
 *
 *  This function allows one to rescale free energy parameters for subsequent structure
 *  prediction or evaluation according to a set of model details, e.g. temperature
 *  values. To do so, the caller provides either a pointer to a set of model details
 *  to be used for rescaling, or NULL if global default setting should be used.
 *
 *  @see vrna_exp_params_reset(), vrna_params_subs()
 *
 *  @param  fc    The fold compound data structure
 *  @param  md    A pointer to the new model details (or NULL for reset to defaults)
 */
void
vrna_params_reset(vrna_fold_compound_t  *fc,
                  vrna_md_t             *md);


/**
 *  @brief  Reset Boltzmann factors for partition function computations
 *          within a #vrna_fold_compound_t according to provided, or
 *          default model details
 *
 *  This function allows one to rescale Boltzmann factors for subsequent partition
 *  function computations according to a set of model details, e.g. temperature
 *  values. To do so, the caller provides either a pointer to a set of model details
 *  to be used for rescaling, or NULL if global default setting should be used.
 *
 *  @see vrna_params_reset(), vrna_exp_params_subst(), vrna_exp_params_rescale()
 *
 *  @param  fc    The fold compound data structure
 *  @param  md    A pointer to the new model details (or NULL for reset to defaults)
 */
void
vrna_exp_params_reset(vrna_fold_compound_t  *fc,
                      vrna_md_t             *md);


void
vrna_params_prepare(vrna_fold_compound_t  *fc,
                    unsigned int          options);


#ifndef VRNA_DISABLE_BACKWARD_COMPATIBILITY

/**
 *  @brief Old typename of #vrna_param_s
 *  @deprecated Use #vrna_param_t instead!
 */
typedef struct vrna_param_s paramT;

/**
 *  @brief Old typename of #vrna_exp_param_s
 *  @deprecated Use #vrna_exp_param_t instead!
 */
typedef struct vrna_exp_param_s pf_paramT;

DEPRECATED(vrna_param_t *get_parameter_copy(vrna_param_t *par),
           "Use vrna_params_copy() instead");

/**
 *  get a data structure of type @ref vrna_exp_param_t which contains
 *  the Boltzmann weights of several energy parameters scaled
 *  according to the current temperature
 *
 *  @deprecated Use vrna_exp_params() instead!
 *
 *  @return The data structure containing Boltzmann weights for use in partition function calculations
 */
DEPRECATED(vrna_exp_param_t *get_scaled_pf_parameters(void),
           "Use vrna_params() instead");

/**
 *  @brief Get precomputed Boltzmann factors of the loop type
 *  dependent energy contributions with independent thermodynamic
 *  temperature
 *
 *  This function returns a data structure that contains
 *  all necessary precalculated Boltzmann factors for each
 *  loop type contribution.<br>
 *  In contrast to get_scaled_pf_parameters(), this function
 *  enables setting of independent temperatures for both, the
 *  individual energy contributions as well as the thermodynamic
 *  temperature used in
 *  @f$ exp(-\Delta G / kT) @f$
 *
 *  @deprecated Use vrna_exp_params() instead!
 *
 *  @see get_scaled_pf_parameters(), get_boltzmann_factor_copy()
 *
 *  @param  temperature   The temperature in degrees Celcius used for (re-)scaling the energy contributions
 *  @param  betaScale     A scaling value that is used as a multiplication factor for the absolute
 *                        temperature of the system
 *  @param  md            The model details to be used
 *  @param  pf_scale      The scaling factor for the Boltzmann factors
 *  @return               A set of precomputed Boltzmann factors
 */
DEPRECATED(vrna_exp_param_t *get_boltzmann_factors(double     temperature,
                                                   double     betaScale,
                                                   vrna_md_t  md,
                                                   double     pf_scale),
           "Use vrna_exp_params() instead");

/**
 *  @brief Get a copy of already precomputed Boltzmann factors
 *
 *  @deprecated Use vrna_exp_params_copy() instead!
 *
 *  @see get_boltzmann_factors(), get_scaled_pf_parameters()
 *
 *  @param  parameters  The input data structure that shall be copied
 *  @return             A copy of the provided Boltzmann factor data set
 */
DEPRECATED(vrna_exp_param_t *get_boltzmann_factor_copy(vrna_exp_param_t *parameters),
           "Use vrna_exp_params_copy() instead");

/**
 *  @brief Get precomputed Boltzmann factors of the loop type
 *  dependent energy contributions (alifold variant)
 *
 *  @deprecated Use vrna_exp_params_comparative() instead!
 *
 */
DEPRECATED(vrna_exp_param_t *get_scaled_alipf_parameters(unsigned int n_seq),
           "Use vrna_exp_params_comparative() instead");

/**
 *  @brief Get precomputed Boltzmann factors of the loop type
 *  dependent energy contributions (alifold variant) with
 *  independent thermodynamic temperature
 *
 *  @deprecated Use vrna_exp_params_comparative() instead!
 *
 */
DEPRECATED(vrna_exp_param_t *get_boltzmann_factors_ali(unsigned int n_seq,
                                                       double       temperature,
                                                       double       betaScale,
                                                       vrna_md_t    md,
                                                       double       pf_scale),
           "Use vrna_exp_params_comparative() instead");

/**
 * @brief Get precomputed energy contributions for all the known loop types
 *
 *  @note OpenMP: This function relies on several global model settings variables and thus is
 *        not to be considered threadsafe. See get_scaled_parameters() for a completely threadsafe
 *        implementation.
 *
 *  @deprecated Use vrna_params() instead!
 *
 * @return     A set of precomputed energy contributions
 */
DEPRECATED(vrna_param_t *scale_parameters(void),
           "Use vrna_params() instead");

/**
 * @brief Get precomputed energy contributions for all the known loop types
 *
 *  Call this function to retrieve precomputed energy contributions, i.e. scaled
 *  according to the temperature passed. Furthermore, this function assumes a
 *  data structure that contains the model details as well, such that subsequent
 *  folding recursions are able to retrieve the correct model settings
 *
 *  @deprecated Use vrna_params() instead!
 *
 *  @see #vrna_md_t, set_model_details()
 *
 *  @param temperature  The temperature in degrees Celcius
 *  @param md           The model details
 *  @return             precomputed energy contributions and model settings
 */
DEPRECATED(vrna_param_t *get_scaled_parameters(double     temperature,
                                               vrna_md_t  md),
           "Usee vrna_params() instead");

DEPRECATED(vrna_param_t *copy_parameters(void), "Use vrna_params_copy() instead");
DEPRECATED(vrna_param_t *set_parameters(vrna_param_t *dest), "Use vrna_params_copy() instead");
DEPRECATED(vrna_exp_param_t *scale_pf_parameters(void), "Use vrna_exp_params() instead");
DEPRECATED(vrna_exp_param_t *copy_pf_param(void), "Use vrna_exp_params_copy() instead");
DEPRECATED(vrna_exp_param_t *set_pf_param(vrna_param_t *dest),
           "Use vrna_exp_params_copy() instead");

#endif

/**
 *  @}
 */


#endif
