#ifndef VIENNA_RNA_PACKAGE_TWO_D_PF_FOLD_H
#define VIENNA_RNA_PACKAGE_TWO_D_PF_FOLD_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 2Dpfold.h
 *  @ingroup kl_neighborhood
 *  @brief Partition function implementations for base pair distance classes
 *
 */

/**
 *  @addtogroup kl_neighborhood_pf
 *  @{
 */

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

/**
 *  @brief Solution element returned from vrna_pf_TwoD()
 *
 *  This element contains the partition function for the appropriate
 *  kappa (k), lambda (l) neighborhood
 *  The datastructure contains two integer attributes 'k' and 'l'
 *  as well as an attribute 'q' of type #FLT_OR_DBL
 *
 *  A value of #INF in k denotes the end of a list
 *
 *  @see  vrna_pf_TwoD()
 */
typedef struct {
  int         k;  /**<  @brief  Distance to first reference */
  int         l;  /**<  @brief  Distance to second reference */
  FLT_OR_DBL  q;  /**<  @brief  partition function */
} vrna_sol_TwoD_pf_t;

/**
 * @brief Compute the partition function for all distance classes
 *
 * This function computes the partition functions for all distance classes
 * according the two reference structures specified in the datastructure 'vars'.
 * Similar to vrna_mfe_TwoD() the arguments maxDistance1 and maxDistance2 specify
 * the maximum distance to both reference structures. A value of '-1' in either of
 * them makes the appropriate distance restrictionless, i.e. all basepair distancies
 * to the reference are taken into account during computation.
 * In case there is a restriction, the returned solution contains an entry where
 * the attribute k=l=-1 contains the partition function for all structures exceeding
 * the restriction.
 * A value of #INF in the attribute 'k' of the returned list denotes the end of the list
 *
 * @see vrna_fold_compound_TwoD(), vrna_fold_compound_free(), #vrna_fold_compound
 *      #vrna_sol_TwoD_pf_t
 *
 * @param fc            The datastructure containing all necessary folding attributes and matrices
 * @param maxDistance1  The maximum basepair distance to reference1 (may be -1)
 * @param maxDistance2  The maximum basepair distance to reference2 (may be -1)
 * @returns             A list of partition funtions for the corresponding distance classes
 */
vrna_sol_TwoD_pf_t *
vrna_pf_TwoD(vrna_fold_compound_t *fc,
             int                  maxDistance1,
             int                  maxDistance2);


/** @} */ /* End of group kl_neighborhood_pf */

/**
 *  @addtogroup kl_neighborhood_stochbt
 *  @{
 */

/**
 *  @brief Sample secondary structure representatives from a set of distance classes according to their
 *  Boltzmann probability
 *
 *  If the argument 'd1' is set to '-1', the structure will be backtracked in the distance class
 *  where all structures exceeding the maximum basepair distance to either of the references reside.
 *
 *  @pre      The argument 'vars' must contain precalculated partition function matrices,
 *            i.e. a call to vrna_pf_TwoD() preceding this function is mandatory!
 *
 *  @see      vrna_pf_TwoD()
 *
 *  @param[inout]  fc The #vrna_fold_compound_t datastructure containing all necessary folding attributes and matrices
 *  @param[in]  d1    The distance to reference1 (may be -1)
 *  @param[in]  d2    The distance to reference2
 *  @returns    A sampled secondary structure in dot-bracket notation
 */
char *
vrna_pbacktrack_TwoD(vrna_fold_compound_t *fc,
                     int                  d1,
                     int                  d2);


/**
 * @brief Sample secondary structure representatives with a specified length from a set of distance classes according to their
 *  Boltzmann probability
 *
 * This function does essentially the same as vrna_pbacktrack_TwoD() with the only difference that partial structures,
 * i.e. structures beginning from the 5' end with a specified length of the sequence, are backtracked
 *
 * @note      This function does not work (since it makes no sense) for circular RNA sequences!
 *
 * @pre       The argument 'vars' must contain precalculated partition function matrices,
 *            i.e. a call to vrna_pf_TwoD() preceding this function is mandatory!
 *
 * @see       vrna_pbacktrack_TwoD(), vrna_pf_TwoD()
 *
 *  @param[inout] fc    The #vrna_fold_compound_t datastructure containing all necessary folding attributes and matrices
 *  @param[in]  d1      The distance to reference1 (may be -1)
 *  @param[in]  d2      The distance to reference2
 *  @param[in]  length  The length of the structure beginning from the 5' end
 *  @returns            A sampled secondary structure in dot-bracket notation
 */
char *
vrna_pbacktrack5_TwoD(vrna_fold_compound_t  *fc,
                      int                   d1,
                      int                   d2,
                      unsigned int          length);


/**
 *  @}
 */ /* End of group kl_neighborhood_stochbt */

#ifndef VRNA_DISABLE_BACKWARD_COMPATIBILITY

#define TwoDpfold_solution       vrna_sol_TwoD_pf_t         /* restore compatibility of struct rename */

/**
 *  @brief  Variables compound for 2Dfold partition function folding
 *
 *  @deprecated This data structure will be removed from the library soon!
 *              Use #vrna_fold_compound_t and the corresponding functions vrna_fold_compound_TwoD(),
 *              vrna_pf_TwoD(), and vrna_fold_compound_free() instead!
 */
typedef struct {
  unsigned int          alloc;
  char                  *ptype;       /**<  @brief  Precomputed array of pair types */
  char                  *sequence;    /**<  @brief  The input sequence  */
  short                 *S, *S1;      /**<  @brief  The input sequences in numeric form */
  unsigned int          maxD1;        /**<  @brief  Maximum allowed base pair distance to first reference */
  unsigned int          maxD2;        /**<  @brief  Maximum allowed base pair distance to second reference */

  double                temperature;  /* temperature in last call to scale_pf_params */
  double                init_temp;    /* temperature in last call to scale_pf_params */
  FLT_OR_DBL            *scale;
  FLT_OR_DBL            pf_scale;
  vrna_exp_param_t      *pf_params; /* holds all [unscaled] pf parameters */

  int                   *my_iindx;  /**<  @brief  Index for moving in quadratic distancy dimensions */
  int                   *jindx;     /**<  @brief  Index for moving in the triangular matrix qm1 */

  short                 *reference_pt1;
  short                 *reference_pt2;

  unsigned int          *referenceBPs1; /**<  @brief  Matrix containing number of basepairs of reference structure1 in interval [i,j] */
  unsigned int          *referenceBPs2; /**<  @brief  Matrix containing number of basepairs of reference structure2 in interval [i,j] */
  unsigned int          *bpdist;        /**<  @brief  Matrix containing base pair distance of reference structure 1 and 2 on interval [i,j] */

  unsigned int          *mm1;           /**<  @brief  Maximum matching matrix, reference struct 1 disallowed */
  unsigned int          *mm2;           /**<  @brief  Maximum matching matrix, reference struct 2 disallowed */

  int                   circ;
  int                   dangles;
  unsigned int          seq_length;

  FLT_OR_DBL            ***Q;
  FLT_OR_DBL            ***Q_B;
  FLT_OR_DBL            ***Q_M;
  FLT_OR_DBL            ***Q_M1;
  FLT_OR_DBL            ***Q_M2;

  FLT_OR_DBL            **Q_c;
  FLT_OR_DBL            **Q_cH;
  FLT_OR_DBL            **Q_cI;
  FLT_OR_DBL            **Q_cM;

  int                   **l_min_values;
  int                   **l_max_values;
  int                   *k_min_values;
  int                   *k_max_values;

  int                   **l_min_values_b;
  int                   **l_max_values_b;
  int                   *k_min_values_b;
  int                   *k_max_values_b;

  int                   **l_min_values_m;
  int                   **l_max_values_m;
  int                   *k_min_values_m;
  int                   *k_max_values_m;

  int                   **l_min_values_m1;
  int                   **l_max_values_m1;
  int                   *k_min_values_m1;
  int                   *k_max_values_m1;

  int                   **l_min_values_m2;
  int                   **l_max_values_m2;
  int                   *k_min_values_m2;
  int                   *k_max_values_m2;

  int                   *l_min_values_qc;
  int                   *l_max_values_qc;
  int                   k_min_values_qc;
  int                   k_max_values_qc;

  int                   *l_min_values_qcH;
  int                   *l_max_values_qcH;
  int                   k_min_values_qcH;
  int                   k_max_values_qcH;

  int                   *l_min_values_qcI;
  int                   *l_max_values_qcI;
  int                   k_min_values_qcI;
  int                   k_max_values_qcI;

  int                   *l_min_values_qcM;
  int                   *l_max_values_qcM;
  int                   k_min_values_qcM;
  int                   k_max_values_qcM;

  /* auxilary arrays for remaining set of coarse graining (k,l) > (k_max, l_max) */
  FLT_OR_DBL            *Q_rem;
  FLT_OR_DBL            *Q_B_rem;
  FLT_OR_DBL            *Q_M_rem;
  FLT_OR_DBL            *Q_M1_rem;
  FLT_OR_DBL            *Q_M2_rem;

  FLT_OR_DBL            Q_c_rem;
  FLT_OR_DBL            Q_cH_rem;
  FLT_OR_DBL            Q_cI_rem;
  FLT_OR_DBL            Q_cM_rem;

  vrna_fold_compound_t  *compatibility;
} TwoDpfold_vars;

/**
 * @brief Get a datastructure containing all necessary attributes and global folding switches
 *
 * This function prepares all necessary attributes and matrices etc which are needed for a call
 * of TwoDpfold() .
 * A snapshot of all current global model switches (dangles, temperature and so on) is done and
 * stored in the returned datastructure. Additionally, all matrices that will hold the partition
 * function values are prepared.
 *
 *  @deprecated Use the new API that relies on #vrna_fold_compound_t and the corresponding functions
 *              vrna_fold_compound_TwoD(), vrna_pf_TwoD(), and vrna_fold_compound_free() instead!
 *
 * @param seq         the RNA sequence in uppercase format with letters from the alphabet {AUCG}
 * @param structure1  the first reference structure in dot-bracket notation
 * @param structure2  the second reference structure in dot-bracket notation
 * @param circ        a switch indicating if the sequence is linear (0) or circular (1)
 * @returns           the datastructure containing all necessary partition function attributes
 */
DEPRECATED(TwoDpfold_vars *
           get_TwoDpfold_variables(const char *seq,
                                   const char *structure1,
                                   char       *structure2,
                                   int        circ),
          "Use the new API and vrna_fold_compound_TwoD() instead");

/**
 * @brief Free all memory occupied by a TwoDpfold_vars datastructure
 *
 * This function free's all memory occupied by a datastructure obtained from from
 * get_TwoDpfold_variabless() or get_TwoDpfold_variables_from_MFE()
 *
 *  @deprecated Use the new API that relies on #vrna_fold_compound_t and the corresponding functions
 *              vrna_fold_compound_TwoD(), vrna_pf_TwoD(), and vrna_fold_compound_free() instead!
 *
 * @see get_TwoDpfold_variables(), get_TwoDpfold_variables_from_MFE()
 *
 * @param vars   the datastructure to be free'd
 */
DEPRECATED(void
           destroy_TwoDpfold_variables(TwoDpfold_vars *vars),
           "Use the new API and vrna_fold_compound_free() instead");

/**
 * @brief Compute the partition function for all distance classes
 *
 * This function computes the partition functions for all distance classes
 * according the two reference structures specified in the datastructure 'vars'.
 * Similar to TwoDfold() the arguments maxDistance1 and maxDistance2 specify
 * the maximum distance to both reference structures. A value of '-1' in either of
 * them makes the appropriate distance restrictionless, i.e. all basepair distancies
 * to the reference are taken into account during computation.
 * In case there is a restriction, the returned solution contains an entry where
 * the attribute k=l=-1 contains the partition function for all structures exceeding
 * the restriction.
 * A values of #INF in the attribute 'k' of the returned list denotes the end of the list
 *
 *  @deprecated Use the new API that relies on #vrna_fold_compound_t and the corresponding functions
 *              vrna_fold_compound_TwoD(), vrna_pf_TwoD(), and vrna_fold_compound_free() instead!
 *
 * @see get_TwoDpfold_variables(), destroy_TwoDpfold_variables(), #vrna_sol_TwoD_pf_t
 *
 * @param vars          the datastructure containing all necessary folding attributes and matrices
 * @param maxDistance1  the maximum basepair distance to reference1 (may be -1)
 * @param maxDistance2  the maximum basepair distance to reference2 (may be -1)
 * @returns             a list of partition funtions for the appropriate distance classes
 */
DEPRECATED(TwoDpfold_solution *
           TwoDpfoldList(TwoDpfold_vars *vars,
                         int            maxDistance1,
                         int            maxDistance2),
          "Use the new API and vrna_pf_TwoD() instead");

/**
 *  @brief Sample secondary structure representatives from a set of distance classes according to their
 *  Boltzmann probability
 *
 *  If the argument 'd1' is set to '-1', the structure will be backtracked in the distance class
 *  where all structures exceeding the maximum basepair distance to either of the references reside.
 *
 *  @pre      The argument 'vars' must contain precalculated partition function matrices,
 *            i.e. a call to TwoDpfold() preceding this function is mandatory!
 *
 *  @deprecated Use the new API that relies on #vrna_fold_compound_t and the corresponding functions
 *              vrna_fold_compound_TwoD(), vrna_pf_TwoD(), vrna_pbacktrack_TwoD(), and
 *              vrna_fold_compound_free() instead!
 *
 *  @see      TwoDpfold()
 *
 *  @param[in]  vars  the datastructure containing all necessary folding attributes and matrices
 *  @param[in]  d1    the distance to reference1 (may be -1)
 *  @param[in]  d2    the distance to reference2
 *  @returns    A sampled secondary structure in dot-bracket notation
 */
DEPRECATED(char *
           TwoDpfold_pbacktrack(TwoDpfold_vars  *vars,
                                int             d1,
                                int             d2),
          "Use the new API and vrna_pbacktrack_TwoD() instead");

/**
 * @brief Sample secondary structure representatives with a specified length from a set of distance classes according to their
 *  Boltzmann probability
 *
 * This function does essentially the same as TwoDpfold_pbacktrack() with the only difference that partial structures,
 * i.e. structures beginning from the 5' end with a specified length of the sequence, are backtracked
 *
 * @note      This function does not work (since it makes no sense) for circular RNA sequences!
 *
 * @pre       The argument 'vars' must contain precalculated partition function matrices,
 *            i.e. a call to TwoDpfold() preceding this function is mandatory!
 *
 *  @deprecated Use the new API that relies on #vrna_fold_compound_t and the corresponding functions
 *              vrna_fold_compound_TwoD(), vrna_pf_TwoD(), vrna_pbacktrack5_TwoD(), and
 *              vrna_fold_compound_free() instead!
 *
 * @see       TwoDpfold_pbacktrack(), TwoDpfold()
 *
 *  @param[in]  vars    the datastructure containing all necessary folding attributes and matrices
 *  @param[in]  d1      the distance to reference1 (may be -1)
 *  @param[in]  d2      the distance to reference2
 *  @param[in]  length  the length of the structure beginning from the 5' end
 *  @returns    A sampled secondary structure in dot-bracket notation
 */
DEPRECATED(char *
           TwoDpfold_pbacktrack5(TwoDpfold_vars *vars,
                                 int            d1,
                                 int            d2,
                                 unsigned int   length),
          "Use the new API and vrna_pbacktrack5_TwoD() instead");

/**
 * @brief
 *
 *
 */
DEPRECATED(FLT_OR_DBL **TwoDpfold(TwoDpfold_vars  *our_variables,
                                  int             maxDistance1,
                                  int             maxDistance2),
          "Use the new API and vrna_pf_TwoD() instead");

/**
 * @brief
 *
 *
 */
DEPRECATED(FLT_OR_DBL **TwoDpfold_circ(TwoDpfold_vars *our_variables,
                                       int            maxDistance1,
                                       int            maxDistance2),
          "Use the new API and vrna_pf_TwoD() instead");

#endif

#endif
