packages feed

ViennaRNA-bindings-0.233.1.1: C/ViennaRNA/hairpin_loops.c

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <string.h>
#include "ViennaRNA/fold_vars.h"
#include "ViennaRNA/energy_par.h"
#include "ViennaRNA/data_structures.h"
#include "ViennaRNA/params.h"
#include "ViennaRNA/utils.h"
#include "ViennaRNA/constraints.h"
#include "ViennaRNA/exterior_loops.h"
#include "ViennaRNA/gquad.h"
#include "ViennaRNA/structured_domains.h"
#include "ViennaRNA/unstructured_domains.h"
#include "ViennaRNA/hairpin_loops.h"

struct default_data {
  int                       n;
  int                       *idx;
  char                      *mx;
  int                       cp;
  int                       *hc_up;
  void                      *hc_dat;
  vrna_callback_hc_evaluate *hc_f;
};


/*
 #################################
 # PRIVATE FUNCTION DECLARATIONS #
 #################################
 */

PRIVATE FLT_OR_DBL
exp_eval_hp_loop(vrna_fold_compound_t *vc,
                 int                  i,
                 int                  j);


PRIVATE FLT_OR_DBL
exp_eval_ext_hp_loop(vrna_fold_compound_t *vc,
                     int                  i,
                     int                  j);


PRIVATE char
hc_default(int  i,
           int  j,
           int  k,
           int  l,
           char d,
           void *data);


PRIVATE char
hc_default_user(int   i,
                int   j,
                int   k,
                int   l,
                char  d,
                void  *data);


PRIVATE int
eval_hp_loop_fake(vrna_fold_compound_t  *vc,
                  int                   i,
                  int                   j);


PRIVATE FLT_OR_DBL
exp_eval_hp_loop_fake(vrna_fold_compound_t  *vc,
                      int                   i,
                      int                   j);

/*
 #################################
 # BEGIN OF FUNCTION DEFINITIONS #
 #################################
 */

/**
 *  @brief  Evaluate the free energy of a hairpin loop
 *          and consider possible hard constraints
 *
 *  @note This function is polymorphic! The provided #vrna_fold_compound_t may be of type
 *  #VRNA_FC_TYPE_SINGLE or #VRNA_FC_TYPE_COMPARATIVE
 *
 */
PUBLIC int
vrna_E_hp_loop(vrna_fold_compound_t *vc,
               int                  i,
               int                  j)
{
  vrna_callback_hc_evaluate *evaluate;
  struct default_data       hc_dat_local;

  hc_dat_local.idx    = vc->jindx;
  hc_dat_local.mx     = vc->hc->matrix;
  hc_dat_local.hc_up  = vc->hc->up_hp;
  hc_dat_local.n      = vc->length;
  hc_dat_local.cp     = vc->cutpoint;

  if (vc->hc->f) {
    evaluate            = &hc_default_user;
    hc_dat_local.hc_f   = vc->hc->f;
    hc_dat_local.hc_dat = vc->hc->data;
  } else {
    evaluate = &hc_default;
  }

  if ((i > 0) && (j > 0)) {
    /* is this base pair allowed to close a hairpin (like) loop ? */
    if (evaluate(i, j, i, j, VRNA_DECOMP_PAIR_HP, &hc_dat_local)) {
      if (j > i)  /* linear case */
        return vrna_eval_hp_loop(vc, i, j);
      else        /* circular case */
        return vrna_eval_ext_hp_loop(vc, j, i);
    }
  }

  return INF;
}


/**
 *  @brief High-Level function for hairpin loop energy evaluation (partition function variant)
 *
 *  @see E_hp_loop() for it's free energy counterpart
 */
PUBLIC FLT_OR_DBL
vrna_exp_E_hp_loop(vrna_fold_compound_t *vc,
                   int                  i,
                   int                  j)
{
  vrna_callback_hc_evaluate *evaluate;
  struct default_data       hc_dat_local;

  hc_dat_local.idx    = vc->jindx;
  hc_dat_local.mx     = vc->hc->matrix;
  hc_dat_local.hc_up  = vc->hc->up_hp;
  hc_dat_local.n      = vc->length;
  hc_dat_local.cp     = vc->cutpoint;

  if (vc->hc->f) {
    evaluate            = &hc_default_user;
    hc_dat_local.hc_f   = vc->hc->f;
    hc_dat_local.hc_dat = vc->hc->data;
  } else {
    evaluate = &hc_default;
  }

  if ((i > 0) && (j > 0)) {
    if (evaluate(i, j, i, j, VRNA_DECOMP_PAIR_HP, &hc_dat_local)) {
      if (j > i)  /* linear case */
        return exp_eval_hp_loop(vc, i, j);
      else        /* circular case */
        return exp_eval_ext_hp_loop(vc, j, i);
    }
  }

  return 0.;
}


/**
 *  @brief  Evaluate the free energy of an exterior hairpin loop
 *          and consider possible hard constraints
 */
PUBLIC int
vrna_E_ext_hp_loop(vrna_fold_compound_t *vc,
                   int                  i,
                   int                  j)
{
  return vrna_E_hp_loop(vc, j, i);
}


/**
 *  @brief Evaluate free energy of an exterior hairpin loop
 *
 *  @ingroup eval
 *
 */
PUBLIC int
vrna_eval_ext_hp_loop(vrna_fold_compound_t  *vc,
                      int                   i,
                      int                   j)
{
  char            **Ss, loopseq[10];
  unsigned short  **a2s;
  short           *S, **SS, **S5, **S3;
  int             u, e, s, type, *types, n_seq, length;
  vrna_param_t    *P;
  vrna_sc_t       *sc, **scs;
  vrna_md_t       *md;

  length  = vc->length;
  P       = vc->params;
  md      = &(P->model_details);
  e       = INF;

  switch (vc->type) {
    /* single sequences and cofolding hybrids */
    case  VRNA_FC_TYPE_SINGLE:
      S     = vc->sequence_encoding;
      sc    = vc->sc;
      u     = vc->length - j + i - 1;
      type  = md->pair[S[j]][S[i]];

      if (type == 0)
        type = 7;

      if (u < 7) {
        strcpy(loopseq, vc->sequence + j - 1);
        strncat(loopseq, vc->sequence, i);
      }

      e = E_Hairpin(u, type, S[j + 1], S[i - 1], loopseq, P);

      if (sc) {
        if (sc->energy_up)
          e += sc->energy_up[j + 1][vc->length - j]
               + sc->energy_up[1][i - 1];

        if (sc->f)
          e += sc->f(j, i, j, i, VRNA_DECOMP_PAIR_HP, sc->data);
      }
      break;

    /* sequence alignments */
    case  VRNA_FC_TYPE_COMPARATIVE:
      SS    = vc->S;
      S5    = vc->S5;                                 /*S5[s][i] holds next base 5' of i in sequence s*/
      S3    = vc->S3;                                 /*Sl[s][i] holds next base 3' of i in sequence s*/
      Ss    = vc->Ss;
      a2s   = vc->a2s;
      scs   = vc->scs;
      n_seq = vc->n_seq;
      e     = 0;
      types = (int *)vrna_alloc(sizeof(int) * n_seq);

      for (s = 0; s < n_seq; s++) {
        types[s] = md->pair[SS[s][j]][SS[s][i]];
        if (types[s] == 0) types[s] = 7;
      }

      for (s = 0; s < n_seq; s++) {
        char loopseq[10];
        u = a2s[s][length] - a2s[s][j] + a2s[s][i - 1];

        if (u < 9) {
          strcpy(loopseq, Ss[s] + a2s[s][j] - 1);
          strncat(loopseq, Ss[s], a2s[s][i]);
        }
        if (u < 3) e += 600;
        else e += E_Hairpin(u, types[s], S3[s][j], S5[s][i], loopseq, P);
      }
      if (scs) {
        for (s = 0; s < n_seq; s++) {
          if (scs[s]) {
            if (scs[s]->energy_up)
              e += ((i > 1) ? scs[s]->energy_up[1][a2s[s][i - 1]] : 0)
                   + ((j < length) ? scs[s]->energy_up[a2s[s][j + 1]][a2s[s][length] - a2s[s][j]] : 0);
            if (scs[s]->f)
              e += scs[s]->f(a2s[s][j], a2s[s][i], a2s[s][j], a2s[s][i], VRNA_DECOMP_PAIR_HP, scs[s]->data);
          }
        }
      }

      free(types);
      break;

    /* nothing */
    default:
      break;
  }

  return e;
}


/**
 *  @brief Evaluate free energy of a hairpin loop
 *
 *  @ingroup eval
 *
 *  @note This function is polymorphic! The provided #vrna_fold_compound_t may be of type
 *  #VRNA_FC_TYPE_SINGLE or #VRNA_FC_TYPE_COMPARATIVE
 *
 *  @param  vc  The #vrna_fold_compound_t for the particular energy evaluation
 *  @param  i   5'-position of the base pair
 *  @param  j   3'-position of the base pair
 *  @returns    Free energy of the hairpin loop closed by @f$ (i,j) @f$ in deka-kal/mol
 */
PUBLIC int
vrna_eval_hp_loop(vrna_fold_compound_t  *vc,
                  int                   i,
                  int                   j)
{
  char            **Ss;
  unsigned short  **a2s;
  short           *S, **SS, **S5, **S3;
  unsigned int    *sn;
  int             u, e, s, ij, type, *types, *idx, n_seq, en;
  vrna_param_t    *P;
  vrna_sc_t       *sc, **scs;
  vrna_md_t       *md;
  vrna_ud_t       *domains_up;

  idx         = vc->jindx;
  P           = vc->params;
  md          = &(P->model_details);
  sn          = vc->strand_number;
  domains_up  = vc->domains_up;
  e           = INF;

  if (sn[j] != sn[i])
    return eval_hp_loop_fake(vc, i, j);

  /* regular hairpin loop */
  switch (vc->type) {
    /* single sequences and cofolding hybrids */
    case  VRNA_FC_TYPE_SINGLE:
      S     = vc->sequence_encoding;
      sc    = vc->sc;
      u     = j - i - 1;
      ij    = idx[j] + i;
      type  = md->pair[S[i]][S[j]];

      if (type == 0)
        type = 7;

      e = E_Hairpin(u, type, S[i + 1], S[j - 1], vc->sequence + i - 1, P);

      /* add soft constraints */
      if (sc) {
        if (sc->energy_up)
          e += sc->energy_up[i + 1][u];

        if (sc->energy_bp)
          e += sc->energy_bp[ij];
        if (sc->f)
          e += sc->f(i, j, i, j, VRNA_DECOMP_PAIR_HP, sc->data);
      }

      /* consider possible ligand binding */
      if (domains_up && domains_up->energy_cb) {
        en = domains_up->energy_cb(vc,
                                   i + 1, j - 1,
                                   VRNA_UNSTRUCTURED_DOMAIN_HP_LOOP,
                                   domains_up->data);
        if (en != INF)
          en += e;
        e = MIN2(e, en);
      }

      break;

    /* sequence alignments */
    case  VRNA_FC_TYPE_COMPARATIVE:
      SS    = vc->S;
      S5    = vc->S5;                                 /*S5[s][i] holds next base 5' of i in sequence s*/
      S3    = vc->S3;                                 /*Sl[s][i] holds next base 3' of i in sequence s*/
      Ss    = vc->Ss;
      a2s   = vc->a2s;
      scs   = vc->scs;
      n_seq = vc->n_seq;
      ij    = idx[j] + i;
      types = (int *)vrna_alloc(sizeof(int) * n_seq);

      for (s = 0; s < n_seq; s++) {
        types[s] = md->pair[SS[s][i]][SS[s][j]];
        if (types[s] == 0) types[s] = 7;
      }

      for (e = s = 0; s < n_seq; s++) {
        u = a2s[s][j - 1] - a2s[s][i];
        e += (u < 3) ? 600 : E_Hairpin(u, types[s], S3[s][i], S5[s][j], Ss[s] + (a2s[s][i - 1]), P);                          /* ??? really 600 ??? */
      }

      if (scs) {
        for (s = 0; s < n_seq; s++) {
          if (scs[s]) {
            u = a2s[s][j - 1] - a2s[s][i];

            if (scs[s]->energy_up)
              e += scs[s]->energy_up[a2s[s][i + 1]][u];

            if (scs[s]->energy_bp)
              e += scs[s]->energy_bp[ij];

            if (scs[s]->f)
              e += scs[s]->f(a2s[s][i], a2s[s][j], a2s[s][i], a2s[s][j], VRNA_DECOMP_PAIR_HP, scs[s]->data);
          }
        }
      }

      free(types);
      break;

    /* nothing */
    default:
      break;
  }

  return e;
}


PRIVATE int
eval_hp_loop_fake(vrna_fold_compound_t  *vc,
                  int                   i,
                  int                   j)
{
  short         *S;
  unsigned int  *sn;
  int           u, e, ij, type, *idx, en;
  vrna_param_t  *P;
  vrna_sc_t     *sc;
  vrna_md_t     *md;
  vrna_ud_t     *domains_up;

  idx         = vc->jindx;
  P           = vc->params;
  md          = &(P->model_details);
  sn          = vc->strand_number;
  domains_up  = vc->domains_up;
  e           = INF;

  switch (vc->type) {
    /* single sequences and cofolding hybrids */
    case  VRNA_FC_TYPE_SINGLE:
      S     = vc->sequence_encoding;
      sc    = vc->sc;
      u     = j - i - 1;
      ij    = idx[j] + i;
      type  = md->pair[S[i]][S[j]];

      if (type == 0)
        type = 7;

      /* hairpin-like exterior loop (for cofolding) */
      short si, sj;

      si  = (sn[i + 1] == sn[i]) ? S[i + 1] : -1;
      sj  = (sn[j] == sn[j - 1]) ? S[j - 1] : -1;

      if (md->dangles)
        e = E_ExtLoop(md->rtype[type], sj, si, P);
      else
        e = E_ExtLoop(md->rtype[type], -1, -1, P);

      /* add soft constraints */
      if (sc) {
        if (sc->energy_up)
          e += sc->energy_up[i + 1][u];

        if (sc->energy_bp)
          e += sc->energy_bp[ij];
        if (sc->f)
          e += sc->f(i, j, i, j, VRNA_DECOMP_PAIR_HP, sc->data);
      }

      /* consider possible ligand binding */
      if (domains_up && domains_up->energy_cb) {
        en = domains_up->energy_cb(vc,
                                   i + 1, j - 1,
                                   VRNA_UNSTRUCTURED_DOMAIN_HP_LOOP,
                                   domains_up->data);
        if (en != INF)
          en += e;
        e = MIN2(e, en);
      }
      break;

    /* nothing */
    default:
      break;
  }

  return e;
}


PRIVATE FLT_OR_DBL
exp_eval_hp_loop_fake(vrna_fold_compound_t  *vc,
                      int                   i,
                      int                   j)
{
  short             *S, s5, s3;
  unsigned int      *sn;
  int               u, cp, type, *iidx;
  FLT_OR_DBL        qq, temp, *q, *scale;
  vrna_exp_param_t  *pf_params;
  vrna_sc_t         *sc;
  vrna_md_t         *md;
  vrna_ud_t         *domains_up;

  cp          = vc->cutpoint;
  iidx        = vc->iindx;
  pf_params   = vc->exp_params;
  md          = &(pf_params->model_details);
  q           = vc->exp_matrices->q;
  scale       = vc->exp_matrices->scale;
  sn          = vc->strand_number;
  domains_up  = vc->domains_up;

  qq = 0;

  switch (vc->type) {
    /* single sequences and cofolding hybrids */
    case  VRNA_FC_TYPE_SINGLE:
      S     = vc->sequence_encoding;
      sc    = vc->sc;
      u     = j - i - 1;
      type  = md->pair[S[j]][S[i]];

      if (type == 0)
        type = 7;

      temp = q[iidx[i + 1] - (cp - 1)] * q[iidx[cp] - (j - 1)];
      if ((j == cp) && (i == cp - 1))
        temp = scale[2];
      else if (i == cp - 1)
        temp = q[iidx[cp] - (j - 1)] * scale[1];
      else if (j == cp)
        temp = q[iidx[i + 1] - (cp - 1)] * scale[1];
      if (j > cp)
        temp *= scale[1];
      if (i < cp - 1)
        temp *= scale[1];

      s5  = (sn[j] == sn[j - 1]) ? S[j - 1] : -1;
      s3  = (sn[i + 1] == sn[i]) ? S[i + 1] : -1;

      temp *= exp_E_ExtLoop(type, s5, s3, pf_params);

      qq += temp;

      /* add soft constraints */
      if (sc) {
        if (sc->exp_energy_up)
          qq *= sc->exp_energy_up[i + 1][u];

        if (sc->exp_energy_bp)
          qq *= sc->exp_energy_bp[iidx[i] - j];

        if (sc->exp_f)
          qq *= sc->exp_f(i, j, i, j, VRNA_DECOMP_PAIR_HP, sc->data);
      }

      if (domains_up && domains_up->exp_energy_cb) {
        /* we always consider both, bound and unbound state */
        qq += qq * domains_up->exp_energy_cb(vc,
                                             i + 1, j - 1,
                                             VRNA_UNSTRUCTURED_DOMAIN_HP_LOOP,
                                             domains_up->data);
      }
      break;

    /* nothing */
    default:
      break;
  }

  return qq;
}


/*
 *************************************
 * Partition function variants below *
 *************************************
 */
PRIVATE FLT_OR_DBL
exp_eval_hp_loop(vrna_fold_compound_t *vc,
                 int                  i,
                 int                  j)
{
  char              **Ss;
  unsigned short    **a2s;
  short             *S, **SS, **S5, **S3;
  unsigned int      *sn;
  int               u, ij, type, n_seq, s, *types, *idx, *iidx;
  FLT_OR_DBL        q, qbt1, *scale;
  vrna_exp_param_t  *P;
  vrna_sc_t         *sc, **scs;
  vrna_md_t         *md;
  vrna_ud_t         *domains_up;

  idx         = vc->jindx;
  iidx        = vc->iindx;
  P           = vc->exp_params;
  md          = &(P->model_details);
  sn          = vc->strand_number;
  scale       = vc->exp_matrices->scale;
  types       = NULL;
  domains_up  = vc->domains_up;

  q   = 0.;
  ij  = idx[j] + i;

  if (sn[j] != sn[i])
    return exp_eval_hp_loop_fake(vc, i, j);

  switch (vc->type) {
    case VRNA_FC_TYPE_SINGLE:
      S     = vc->sequence_encoding;
      sc    = vc->sc;
      u     = j - i - 1;
      type  = vc->ptype[ij];

      if (type == 0)
        type = 7;

      if (sn[j] == sn[i]) {
        /* regular hairpin loop */
        q = exp_E_Hairpin(u, type, S[i + 1], S[j - 1], vc->sequence + i - 1, P);
      } else {
        /* hairpin-like exterior loop (for cofolding) */
        /* this is currently handle somewhere else */
      }

      /* add soft constraints */
      if (sc) {
        if (sc->exp_energy_up)
          q *= sc->exp_energy_up[i + 1][u];

        if (sc->exp_energy_bp)
          q *= sc->exp_energy_bp[iidx[i] - j];

        if (sc->exp_f)
          q *= sc->exp_f(i, j, i, j, VRNA_DECOMP_PAIR_HP, sc->data);
      }

      q *= scale[u + 2];

      if (domains_up && domains_up->exp_energy_cb) {
        /* we always consider both, bound and unbound state */
        q += q * domains_up->exp_energy_cb(vc,
                                           i + 1, j - 1,
                                           VRNA_UNSTRUCTURED_DOMAIN_HP_LOOP,
                                           domains_up->data);
      }
      break;

    case VRNA_FC_TYPE_COMPARATIVE:
      SS    = vc->S;
      S5    = vc->S5;                                 /*S5[s][i] holds next base 5' of i in sequence s*/
      S3    = vc->S3;                                 /*Sl[s][i] holds next base 3' of i in sequence s*/
      Ss    = vc->Ss;
      a2s   = vc->a2s;
      scs   = vc->scs;
      n_seq = vc->n_seq;
      qbt1  = 1.;
      types = (int *)vrna_alloc(sizeof(int) * n_seq);

      for (s = 0; s < n_seq; s++) {
        types[s] = md->pair[SS[s][i]][SS[s][j]];
        if (types[s] == 0) types[s] = 7;
      }

      for (s = 0; s < n_seq; s++) {
        u = a2s[s][j - 1] - a2s[s][i];
        if (a2s[s][i] < 1) continue;
        char loopseq[10];
        if (u < 9)
          strncpy(loopseq, Ss[s] + a2s[s][i] - 1, 10);
        qbt1 *= exp_E_Hairpin(u, types[s], S3[s][i], S5[s][j], loopseq, P);
      }

      /* add soft constraints */
      if (scs) {
        for (s = 0; s < n_seq; s++) {
          if (scs[s]) {
            u = a2s[s][j - 1] - a2s[s][i];

            if (scs[s]->exp_energy_bp)
              qbt1 *= scs[s]->exp_energy_bp[iidx[i] - j];

            if (scs[s]->exp_energy_up)
              qbt1 *= scs[s]->exp_energy_up[a2s[s][i + 1]][u];

            if (scs[s]->exp_f)
              qbt1 *= scs[s]->exp_f(a2s[s][i], a2s[s][j], a2s[s][i], a2s[s][j], VRNA_DECOMP_PAIR_HP, scs[s]->data);
          }
        }
      }

      q = qbt1 * scale[j - i + 1];
      break;

    default:
      break;
  }

  free(types);
  return q;
}


PRIVATE FLT_OR_DBL
exp_eval_ext_hp_loop(vrna_fold_compound_t *vc,
                     int                  i,
                     int                  j)
{
  char              **Ss, *sequence;
  unsigned short    **a2s;
  short             *S, **SS, **S5, **S3;
  int               u, u1, ij, n, type, n_seq, s, *rtype, *types, *idx, noGUclosure;
  FLT_OR_DBL        q, qbt1, *scale;
  vrna_exp_param_t  *P;
  vrna_sc_t         *sc, **scs;
  vrna_md_t         *md;
  vrna_ud_t         *domains_up;

  n           = vc->length;
  idx         = vc->jindx;
  P           = vc->exp_params;
  md          = &(P->model_details);
  noGUclosure = md->noGUclosure;
  scale       = vc->exp_matrices->scale;
  types       = NULL;
  domains_up  = vc->domains_up;
  rtype       = &(md->rtype[0]);

  q   = 0.;
  u   = n - j + i - 1;
  ij  = idx[j] + i;

  switch (vc->type) {
    case VRNA_FC_TYPE_SINGLE:
      sequence  = vc->sequence;
      S         = vc->sequence_encoding;
      sc        = vc->sc;
      type      = rtype[vc->ptype[ij]];

      if (type == 0)
        type = 7;

      if (((type == 3) || (type == 4)) && noGUclosure)
        return q;

      /* get the loop sequence */
      char loopseq[10];
      if (u < 7) {
        strcpy(loopseq, sequence + j - 1);
        strncat(loopseq, sequence, i);
      }

      q = exp_E_Hairpin(u, type, S[j + 1], S[i - 1], loopseq, P);

      /* add soft constraints */
      if (sc) {
        if (sc->exp_energy_up)
          q *= ((i > 1) ? sc->exp_energy_up[1][i - 1] : 1.)
               * ((j < n) ? sc->exp_energy_up[j + 1][n - j] : 1.);

        if (sc->exp_f)
          q *= sc->exp_f(j, i, j, i, VRNA_DECOMP_PAIR_HP, sc->data);
      }

      q *= scale[u];

      if (domains_up && domains_up->exp_energy_cb) {
        /* we always consider both, bound and unbound state */
        q += q * domains_up->exp_energy_cb(vc,
                                           j + 1, i - 1,
                                           VRNA_UNSTRUCTURED_DOMAIN_HP_LOOP,
                                           domains_up->data);
      }
      break;

    case VRNA_FC_TYPE_COMPARATIVE:
      SS    = vc->S;
      S5    = vc->S5;                                 /*S5[s][i] holds next base 5' of i in sequence s*/
      S3    = vc->S3;                                 /*Sl[s][i] holds next base 3' of i in sequence s*/
      Ss    = vc->Ss;
      a2s   = vc->a2s;
      scs   = vc->scs;
      n_seq = vc->n_seq;
      qbt1  = 1.;
      types = (int *)vrna_alloc(sizeof(int) * n_seq);

      for (s = 0; s < n_seq; s++) {
        types[s] = md->pair[SS[s][j]][SS[s][i]];
        if (types[s] == 0) types[s] = 7;
      }

      for (s = 0; s < n_seq; s++) {
        u1 = a2s[s][i] - 1 + a2s[s][n] - a2s[s][j];
        char loopseq[10];
        if (u1 < 7) {
          strcpy(loopseq, Ss[s] + a2s[s][j] - 1);
          strncat(loopseq, Ss[s], a2s[s][i]);
        }
        qbt1 *= exp_E_Hairpin(u1, types[s], S3[s][j], S5[s][i], loopseq, P);
      }

      /* add soft constraints */
      if (scs) {
        for (s = 0; s < n_seq; s++) {
          if (scs[s]) {
            if (scs[s]->exp_energy_up)
              qbt1 *= ((i > 1) ? scs[s]->exp_energy_up[a2s[s][1]][a2s[s][i] - a2s[s][1]] : 1.)
                      * ((j < n) ? scs[s]->exp_energy_up[a2s[s][j] + 1][a2s[s][n] - a2s[s][j]] : 1.);

            if (scs[s]->exp_f)
              qbt1 *= scs[s]->exp_f(a2s[s][j], a2s[s][i], a2s[s][j], a2s[s][i], VRNA_DECOMP_PAIR_HP, scs[s]->data);
          }
        }
      }

      q = qbt1 * scale[u];

      free(types);
      break;

    default:
      break;
  }

  return q;
}


/**
 *  @brief Backtrack a hairpin loop closed by @f$ (i,j) @f$
 *
 *  @note This function is polymorphic! The provided #vrna_fold_compound_t may be of type
 *  #VRNA_FC_TYPE_SINGLE or #VRNA_FC_TYPE_COMPARATIVE
 *
 */
PUBLIC int
vrna_BT_hp_loop(vrna_fold_compound_t  *vc,
                int                   i,
                int                   j,
                int                   en,
                vrna_bp_stack_t       *bp_stack,
                int                   *stack_count)
{
  int       e, u;
  vrna_sc_t *sc;

  sc = NULL;

  u = j - i - 1;

  if (vc->hc->up_hp[i + 1] < u)
    return 0;

  e = vrna_E_hp_loop(vc, i, j);

  if (e == en) {
    switch (vc->type) {
      case  VRNA_FC_TYPE_SINGLE:
        sc = vc->sc;
        break;

      case  VRNA_FC_TYPE_COMPARATIVE:
        if (vc->scs)
          sc = vc->scs[0];
        break;

      default:
        break;
    }

    if (sc) {
      if (sc->bt) {
        vrna_basepair_t *ptr, *aux_bps;
        aux_bps = sc->bt(i, j, i, j, VRNA_DECOMP_PAIR_HP, sc->data);
        for (ptr = aux_bps; ptr && ptr->i != 0; ptr++) {
          bp_stack[++(*stack_count)].i  = ptr->i;
          bp_stack[(*stack_count)].j    = ptr->j;
        }
        free(aux_bps);
      }
    }

    return 1;
  }

  return 0;
}


PRIVATE char
hc_default(int  i,
           int  j,
           int  k,
           int  l,
           char d,
           void *data)
{
  int                 ij, u, p, q;
  char                eval;
  struct default_data *dat = (struct default_data *)data;

  eval = (char)0;

  if (j > i) {
    /* linear case */
    p = i;
    q = j;
    u = q - p - 1;
  } else {
    /* circular case */
    p = j;
    q = i;
    u = dat->n - q + p - 1;
  }

  ij = dat->idx[q] + p;
  if (dat->mx[ij] & VRNA_CONSTRAINT_CONTEXT_HP_LOOP) {
    eval = (char)1;
    if (dat->hc_up[i + 1] < u)
      eval = (char)0;
  }

  return eval;
}


PRIVATE char
hc_default_user(int   i,
                int   j,
                int   k,
                int   l,
                char  d,
                void  *data)
{
  char                eval;
  struct default_data *dat = (struct default_data *)data;

  eval  = hc_default(i, j, k, l, d, data);
  eval  = (dat->hc_f(i, j, k, l, d, dat->hc_dat)) ? eval : (char)0;

  return eval;
}