/********************************************************************************************************
 * QRNA - Comparative analysis of biological sequences 
 *         with pair hidden Markov models, pair stochastic context-free
 *        grammars, and probabilistic evolutionary  models.
 *       
 * Version 2.0.0 (JUN 2003)
 *
 * Copyright (C) 2000-2003 Howard Hughes Medical Institute/Washington University School of Medicine
 * All Rights Reserved
 * 
 *     This source code is distributed under the terms of the
 *     GNU General Public License. See the files COPYING and LICENSE
 *     for details.
 ***********************************************************************************************************/

/* othscorealign.c
 *
 * ER, Tue Jun  1 08:56:24 CDT 1999 [St. Louis]
 * 
 * score of a given alignment with the othermodel
 * 
 * calculates:
 *             P(seqX, seqY, \pi^{given} | othemodel) 
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>

#include "funcs.h"
#include "globals.h"
#include "squid.h"
#include "structs.h"

#ifdef MEMDEBUG
#include "dbmalloc.h"
#endif

/* Function: ScoreWithOTH_ungapped()
 * Date:     ER, Tue May 25 14:29:15 CDT 1999 [St. Louis]
 *
 * Purpose:  Score a gapped sequence alignment with OTH model.
 *
 * Args:     seqX, seqY   -- equal length sequences, ACGT and gaps (-) 
 *           L            -- lengths of sX,sY 
 *           othmodel     -- oth_model structure
 *
 * Returns:  log likelihood, log P(seqX,seqY,\pi_{blast} | Othmodel)
 */
double
ScoreWithOTH_ungapped(int *seqX, int *seqY, int L, struct othmodel_s *oth)
{
  int i;			/* position in seqX,seqY */
  double sc;

  sc = 0.0;
  for (i = 0; i < L; i++)
    sc += oth->mem[idx(seqX[i],seqY[i])];
  
  return sc;
}

/* Function: ScoreWithOTH()
 * Date:     ER, Tue Oct  9 18:43:51 CDT 2001  [St. Louis]
 *
 * Purpose:  Score a gapped sequence alignment with OTH model.
 *
 * Args:     seqX, seqY   -- equal length sequences, ACGT and gaps (-) 
 *           L            -- lengths of sX,sY 
 *           othmodel     -- oth_model structure
 *
 * Returns:  log likelihood, log P(seqX,seqY,\pi_{blast} | OTHmodel)
 */
void
ScoreWithOTH2(FILE *ofp, int *seqX, int *seqY, int start, int L, struct othmodel_s *oth, struct sc2_s *othsc, int ones, int traceback)
{
  int *irX, *irY;     /* reverse complements */

  othsc->pl = ScoreWithOTH(ofp, seqX, seqY, start, L, oth, traceback);

  if (!ones) {
    /* revcomp */
    irX = MallocOrDie(sizeof(int) * (L+1));
    irY = MallocOrDie(sizeof(int) * (L+1));
    RevComp(irX, start+seqX, L);
    RevComp(irY, start+seqY, L);
    
    othsc->mn = ScoreWithOTH(ofp, irX, irY, 0, L, oth, traceback);
    
    free(irX);
    free(irY);
  } 
  else othsc->mn = 0.0;

}

/* Function: scorewithOTH_onepass()
 * Date:     ER, Fri Jul 16 11:12:31 CDT 1999 [St. Louis]
 *
 * Purpose:  Score a gapped sequence alignment with OTH model.
 *
 * Args:     seqX, seqY   -- equal length sequences, ACGT and gaps (-) 
 *           L            -- lengths of sX,sY 
 *           othmodel     -- oth_model structure
 *
 * Returns:  log likelihood, log P(seqX,seqY,\pi_{blast} | OTHmodel)
 */
double
ScoreWithOTH(FILE *ofp, int *seqX, int *seqY, int start, int L, struct othmodel_s *oth, int traceback)
{
  struct tracer_s          *tr;  /* the traceback tree under construction  */
  struct tracer_s     *curr_tr;  /* ptr to node of tr we're working on     */
  struct tracerstack_s *dolist;  /* pushdown stack of active tr nodes      */
  int i, nxt_i;			 /* position in seqX, seqY                 */
  int cur_x, cur_y;              /* nucleotides at those positions         */
  int nxt_x, nxt_y;
  int cur_st, nxt_st;
  double sc, scold;

  sc = 0.0;

  if (L == 0) return  oth->t[TFLFR] + 
		ScoreWithNullDiag(seqX, seqY, start, 0, oth->FLN) +
		ScoreWithNullDiag(seqX, seqY, start, 0, oth->FRN); 
  
  /* Initialize
   * Start at i = start
   */
  tr     = InitTracer();       /* start a trace tree */
  dolist = InitTracerstack();  /* start a stack for traversing the trace tree */

  curr_tr = AttachTracer(tr, start, stB); 
  PushTracerstack(dolist, curr_tr);

  while ((curr_tr = PopTracerstack(dolist)) != NULL)
   {
     i = curr_tr->emit;

     cur_x = seqX[i];
     cur_y = seqY[i];

     cur_st = curr_tr->type;

     /* Determine the next state we have to trace down */
      
     if (cur_st == stB) /* begining state, do not move yet */
       nxt_i = i;
     else if (i < start+L-1)  /* any other state advance one position */
       nxt_i = i+1;
     else nxt_i = -1;   /* unless in the last position */

     if (nxt_i == -1) 
       nxt_st = stE;
     else {
       nxt_x = seqX[nxt_i];
       nxt_y = seqY[nxt_i];
       
       if      (nxt_x <  4 && nxt_y <  4) nxt_st = stM;
       else if (nxt_x == 4 && nxt_y <  4) nxt_st = stY;
       else if (nxt_x <  4 && nxt_y == 4) nxt_st = stX;
       else Die("ScoreWithOTH() wrong nucleotide nxt_x = %d nxt_y = %d in pos %d" , nxt_x, nxt_y, i);
     }

     scold = sc;
     if (traceback) {
       if (cur_st == stB)
	 fprintf(ofp,"start %s \n", ostNAME[cur_st]);
       else
	 fprintf(ofp,"scoring %s (%d) [%d %d] %f \n", ostNAME[cur_st], i, cur_x, cur_y, scold);
     }
     
     switch (cur_st){
     case stB:  
       sc += ScoreWithNullDiag(seqX, seqY, start, 0, oth->FLN)  + oth->t[TFLB];
       if      (nxt_st == stM)  sc += oth->t[TBM];
       else if (nxt_st == stX)  sc += oth->t[TBX];
       else if (nxt_st == stY)  sc += oth->t[TBY];
       else Die("ScoreWithOTH() wrong state %s", ostNAME[nxt_st]);
       break;
     case stM: 
       if      (nxt_st == stM)  sc += oth->mem[idx(cur_x,cur_y)] + oth->t[TMM];
       else if (nxt_st == stX)  sc += oth->mem[idx(cur_x,cur_y)] + oth->t[TMX];
       else if (nxt_st == stY)  sc += oth->mem[idx(cur_x,cur_y)] + oth->t[TMY];
       else if (nxt_st == stE)  sc += oth->mem[idx(cur_x,cur_y)] + oth->t[TME];
       else Die("ScoreWithOTH() wrong state %s", ostNAME[nxt_st]);
       break;

     case stX: 
       if      (nxt_st == stM)  sc += oth->xem[cur_x] + oth->t[TXM];
       else if (nxt_st == stX)  sc += oth->xem[cur_x] + oth->t[TXX];
       else if (nxt_st == stY)  sc += oth->xem[cur_x] + oth->t[TXY];
       else if (nxt_st == stE)  sc += oth->xem[cur_x] + oth->t[TXE];
       else Die("ScoreWithOTH() wrong state %s", ostNAME[nxt_st]);
       break;

     case stY: 
       if      (nxt_st == stM)  sc += oth->yem[cur_y] + oth->t[TYM];
       else if (nxt_st == stX)  sc += oth->yem[cur_y] + oth->t[TYX];
       else if (nxt_st == stY)  sc += oth->yem[cur_y] + oth->t[TYY];
       else if (nxt_st == stE)  sc += oth->yem[cur_y] + oth->t[TYE];
       else Die("ScoreWithOTH() wrong state %s", ostNAME[nxt_st]);
       break;
     default:
       Die("invalid state in ScoreWithOTH()");
     }

     if (traceback) fprintf(ofp," %s->%s, %f\n", ostNAME[cur_st], ostNAME[nxt_st], sc-scold);  
     if (nxt_st != stE) PushTracerstack(dolist, AttachTracer(curr_tr, nxt_i, nxt_st));
   }
  FreeTracer(tr);
  FreeTracer(curr_tr);
  FreeTracerstack(dolist);
  
  sc += oth->t[TEFR] + ScoreWithNullDiag(seqX, seqY, start+L-1, 0, oth->FRN);
  if (traceback) fprintf(ofp," %s->%s, %f\n", ostNAME[stE], ostNAME[stE], 
			 ScoreWithNullDiag(seqX, seqY, start+L-1, 0, oth->FRN));
  return sc;
}



/* Function: ScoreWithOTHMatrix()
 * Date:     ER, Mon Nov 15 11:15:35 CST 1999 [St. Louis]
 *
 * Purpose:  fill matrix mx to score nts inside loops
 *           mx[j][d] = ScoreWithOTH(stdout, sX, sY, j-d, d+1, rna->ROJ, FALSE)
 *           this is a way of speeding things up:
 *
 *           mx[j][d] = mx[j-1][d-1] + sc
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *
 * Returns:  void
 *           matrix is filled. matrix is alreadyalloc'ed and free by caller
 *
 */
double
ScoreWithOTHMatrix(int *sX, int *sY, int j, struct othmodel_s *oth)
{
  int    prv_x, prv_y;
  int    cur_x, cur_y;
  int    prv_st;
  int    cur_st;
  double sc = 0.;

  if (j == 0)
    Die("value not valid in ScoreWithOTHMatrix() %d\n", j);
 
  prv_x = sX[j-1];
  prv_y = sY[j-1];
  if      (prv_x <  4 && prv_y <  4) prv_st = stM;
  else if (prv_x == 4 && prv_y <  4) prv_st = stY;
  else if (prv_x <  4 && prv_y == 4) prv_st = stX;
  else Die("ScoreWithOTHMatrix() wrong nucleotide prv_x = %d prv_y = %d in pos %d" , prv_x, prv_y, j-1);
  
  cur_x = sX[j];
  cur_y = sY[j];
  if      (cur_x <  4 && cur_y <  4) cur_st = stM;
  else if (cur_x == 4 && cur_y <  4) cur_st = stY;
  else if (cur_x <  4 && cur_y == 4) cur_st = stX;
  else Die("ScoreWithOTHMatrix() wrong nucleotide cur_x = %d cur_y = %d in pos %d" , cur_x, cur_y, j);

     switch (prv_st){
     case stM:  
       sc -= oth->t[TME];

       if      (cur_st == stM)  sc += oth->t[TMM] + oth->t[TME] + oth->mem[idx(cur_x,cur_y)];
       else if (cur_st == stX)  sc += oth->t[TMX] + oth->t[TXE] + oth->xem[cur_x];
       else if (cur_st == stY)  sc += oth->t[TMY] + oth->t[TYE] + oth->yem[cur_y];
       else Die("ScoreWithOTHMatrix() wrong state %s", ostNAME[cur_st]);
       break;

    case stX: 
      sc -= oth->t[TXE];
      
      if      (cur_st == stM)  sc += oth->t[TXM] + oth->t[TME] + oth->mem[idx(cur_x,cur_y)];
      else if (cur_st == stX)  sc += oth->t[TXX] + oth->t[TXE] + oth->xem[cur_x];
      else if (cur_st == stY)  sc += oth->t[TXY] + oth->t[TYE] + oth->yem[cur_y];
      else Die("ScoreWithOTHMatrix() wrong state %s", ostNAME[cur_st]);
      break;
       
     case stY: 
       sc -= oth->t[TYE];
       
       if      (cur_st == stM)  sc += oth->t[TYM] + oth->t[TME] + oth->mem[idx(cur_x,cur_y)];
       else if (cur_st == stX)  sc += oth->t[TYX] + oth->t[TXE] + oth->xem[cur_x];
       else if (cur_st == stY)  sc += oth->t[TYY] + oth->t[TYE] + oth->yem[cur_y];
       else Die("ScoreWithOTHMatrix() wrong state %s", ostNAME[cur_st]);
       break;

     default:
       Die("invalid state  %s in ScoreWithOTHMatrix()", ostNAME[prv_st]);
     }

     return  sc;
}






