/********************************************************************************************************
 * 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.
 ***********************************************************************************************************/

/* scorewithmodelscan.c
 *
 * ER, Mon Dec  3 11:55:47 CST 2001 [St. Louis]
 * 
 * 
 * 
 */

#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


static void print_scan_ends (FILE *ofp, int leg, char *whichmodel, struct endscan_s *ends, int isreverse, int winner);
static void score_with_null_scan(FILE *ofp, int *isegX, int *isegY, int len, struct nullmodel_s *null, 
				 double nullsc, double nullrevsc, int verbose);

static void score_viterbi_diag_scan_fast(FILE *ofp, 
					 SQINFO sqinfoX, int *isegX,
					 SQINFO sqinfoY, int *isegY,
					 char *gss, int L, int win, int slide, int st, int stmod, int l, int lmax,
					 struct model_s           *model, 
					 struct dpdscanfast_s     *dpdscan, 		     		    
					 struct rnascfgscanfast_s *mx, 
					 struct sc3_s             *sc, 
					 struct ali_s             *ali, 
					 struct scanfast_s        *scanfast,
					 int alignment, int cyk, int doends, int fastintloop, int logodds, int ones, int parse, int rnass, 
					 int revstrand, int traceback, int *ret_windows);


/* Function: AllocEndScan()
 * Date:     ER, Mon Dec  2 10:49:21 CST 2002  [St. Louis] 
 * 
 * Purpose:  Allocates memory for structure endscan_s  
 * 
 * Returns:  en3scan_s are allocated 
 */  
struct endscan_s * 
AllocEndScanFast()
{
  struct endscan_s *end;   
  int               x;
   
  end = (struct endscan_s *) MallocOrDie (sizeof(struct endscan_s));

  end->lend = (int *) MallocOrDie (sizeof(int) * MAX_NUM_ENDS);
  end->rend = (int *) MallocOrDie (sizeof(int) * MAX_NUM_ENDS);

  /* initialize */
  for (x = 0 ; x < MAX_NUM_ENDS; x++) {
    end->lend[x] = -1;
    end->rend[x] = -1;
  }
  
  return end;
}

/* Function: AllocEnd3Scan()
 * Date:     ER, Mon Nov 25 10:33:52 CST 2002  [St. Louis] 
 * 
 * Purpose:  Allocates memory for structure end3scan_s  
 * 
 * Returns:  end3scan_s are allocated 
 */  
struct end3scan_s * 
AllocEnd3ScanFast()
{
  struct end3scan_s *ends;   
   
  ends = (struct end3scan_s *) MallocOrDie (sizeof(struct end3scan_s));

  ends->cod = AllocEndScanFast();
  ends->oth = AllocEndScanFast();
  ends->rna = AllocEndScanFast();
 
  return ends;
}

void
CopyEnd3ScanFast(struct end3scan_s *copy_ends, struct end3scan_s *ends)
{
  int x;

  for (x = 0; x < MAX_NUM_ENDS; x++) 
    {
      copy_ends->oth->lend[x] = ends->oth->lend[x];
      copy_ends->oth->rend[x] = ends->oth->rend[x];

      copy_ends->cod->lend[x] = ends->cod->lend[x];
      copy_ends->cod->rend[x] = ends->cod->rend[x];

      copy_ends->rna->lend[x] = ends->rna->lend[x];
      copy_ends->rna->rend[x] = ends->rna->rend[x];
    }
}

void
FreeEndScanFast(struct endscan_s *end)
{
  free(end->lend);
  free(end->rend);

  free(end);
}

void
FreeEnd3ScanFast(struct end3scan_s *ends)
{
  FreeEndScanFast(ends->oth);
  FreeEndScanFast(ends->cod);
  FreeEndScanFast(ends->rna);

  free(ends);
}

void
PatternEndScanFast(struct endscan_s *ends)
{
  int x;

  for (x = 0 ; x < MAX_NUM_ENDS; x++) {
    ends->lend[x] = -1;
    ends->rend[x] = -1;
  }
}

void
PatternEnd3ScanFast(struct end3scan_s *ends)
{
  PatternEndScanFast(ends->oth);
  PatternEndScanFast(ends->cod);
  PatternEndScanFast(ends->rna);
}

void
PrintEndScanFast(struct endscan_s *ends)
{
  int x;

  for (x = 0 ; x < MAX_NUM_ENDS; x++) 
    printf("(%d..[%d]..%d) ", ends->lend[x], ends->rend[x]-ends->lend[x]+1, ends->rend[x]);
  printf("\n");
  
}

/* Function: PosteriorScoresScanFast()
 * Date:     ER, Sun Oct 13 15:09:18 CDT 2002 [St. Louis]
 *
 * Purpose:  print scores - scanfast version
 *


 * Args:     ofp, sqinfoX, sqinfoY, nullsc, pamsc, rnasc
 *
 * Returns:  void. */
void
PosteriorScoresScanFast(FILE *ofp, struct scanfast_s *scanfast, int leg, int j, int win, int slide, int doends, int ones)
{
  char              *winn;
  double             othsc,  codsc,  rnasc;
  double             post_o, post_c, post_r;  /* posterior probs in bits */
  double             sigm_o, sigm_c, sigm_r;  /* sigmoidal scores in bits */
  int                idx;                     /* index for the window */
  int                othwinisrev = FALSE;
  int                codwinisrev = FALSE;
  int                rnawinisrev = FALSE;
  

  idx = IndexWindow(j, leg, win, slide, FALSE);

  /* ENDS (if doends = TRUE)
   */
  if (doends) {
    
    if (scanfast->sc->oth[idx] < scanfast->sc->othrev[idx]) othwinisrev = TRUE;
    if (scanfast->sc->cod[idx] < scanfast->sc->codrev[idx]) codwinisrev = TRUE;
    if (scanfast->sc->rna[idx] < scanfast->sc->rnarev[idx]) rnawinisrev = TRUE;

    print_scan_ends (ofp, leg, "OTH", scanfast->ends->fwd[idx]->oth, FALSE, othwinisrev);
    if (!ones) print_scan_ends (ofp, leg, "OTH", scanfast->ends->rev[idx]->oth, TRUE,  othwinisrev);

    print_scan_ends (ofp, leg, "COD", scanfast->ends->fwd[idx]->cod, FALSE, codwinisrev);
     if (!ones) print_scan_ends (ofp, leg, "COD", scanfast->ends->rev[idx]->cod, TRUE,  codwinisrev);

    print_scan_ends (ofp, leg, "RNA", scanfast->ends->fwd[idx]->rna, FALSE, rnawinisrev);
     if (!ones) print_scan_ends (ofp, leg, "RNA", scanfast->ends->rev[idx]->rna, TRUE,  rnawinisrev);

  }
  
  /* SCORES
   */
   if (ones) {
    othsc = scanfast->sc->oth[idx];
    codsc = scanfast->sc->cod[idx];
    rnasc = scanfast->sc->rna[idx];
  }
  else {
    othsc = scanfast->sc->oth[idx] + LOG2(1.0 + EXP2(scanfast->sc->othrev[idx] - scanfast->sc->oth[idx])) - 1.0;
    codsc = scanfast->sc->cod[idx] + LOG2(1.0 + EXP2(scanfast->sc->codrev[idx] - scanfast->sc->cod[idx])) - 1.0;
    rnasc = scanfast->sc->rna[idx] + LOG2(1.0 + EXP2(scanfast->sc->rnarev[idx] - scanfast->sc->rna[idx])) - 1.0;
  }

  if      (othsc >= codsc && othsc >= rnasc) winn = "OTH";
  else if (codsc >= othsc && codsc >= rnasc) winn = "COD";
  else if (rnasc >= othsc && rnasc >= codsc) winn = "RNA";
  else Die ("who is the winner?");
  
  /* posteriors assuming flat priors for the models */
  post_o = PosteriorLog2(othsc, codsc, rnasc);
  post_c = PosteriorLog2(codsc, othsc, rnasc);
  post_r = PosteriorLog2(rnasc, othsc, codsc);

  /* sigmoidal scores assuming flat priors for the models */
  sigm_o = SigmoidalLog2(othsc, codsc, rnasc);
  sigm_c = SigmoidalLog2(codsc, othsc, rnasc);
  sigm_r = SigmoidalLog2(rnasc, othsc, codsc);
  
  fprintf(ofp, "winner = %s \n", winn);

  fprintf(ofp, "              OTH = %12.3f             COD = %12.3f             RNA = %12.3f \n", 
	  othsc, codsc, rnasc);
  fprintf(ofp, "   logoddspostOTH = %12.3f  logoddspostCOD = %12.3f  logoddspostRNA = %12.3f \n", 
	  post_o - post_o, post_c - post_o, post_r - post_o);
    fprintf(ofp, "     sigmoidalOTH = %12.3f    sigmoidalCOD = %12.3f    sigmoidalRNA = %12.3f \n\n", 
	  sigm_o, sigm_c, sigm_r);
}

/* Function: ScoreWithModelsScan()
 * Date:     ER, Sat Feb 26 10:39:26 CST 2000 [St. Louis]
 *
 * Purpose:  decide which scoring method to use. Called by main().
 *
 * Args:     
 *
 * Returns:  void.
 */
int
ScoreWithModelsScanFast(FILE *ofp, 
			SQINFO sqinfoX, int *isegX,
			SQINFO sqinfoY, int *isegY,
			char *gss,
			int L, int win, int slide, int j, int jmod, int l, int lmax, 
			struct model_s           *model, 
			struct dpdscanfast_s     *dpdscan, 		   
			struct rnascfgscanfast_s *mx, 
			struct scores_s          *sc, 
			struct ali_s             *ali, 
			struct scanfast_s        *scanfast,
			int alignment, int cyk, int doends, int fastintloop, int logodds, int ones, int parse, 
			int rnass, int revstrand, int shuffle, int traceback, int verbose)
{
  int windows;
   
 /* Calculate the null-model scores
  */
  score_with_null_scan(ofp, isegX, isegY, l, model->null, sc->null, sc->nullrev, verbose);

  /* VITERBI (diag local aligmnent) 
   */
  score_viterbi_diag_scan_fast(ofp, sqinfoX, isegX, sqinfoY, isegY, gss, L, win, slide, j, jmod, l, lmax, model, dpdscan, 
			       mx, sc->vidiag, ali, scanfast, alignment, cyk, doends, fastintloop, logodds, ones, parse, rnass, revstrand, traceback, 
			       &windows);

  /*Other versions still not implemented in scanfast model */
  
  return windows;
}

void
score_with_null_scan(FILE *ofp, int *isegX, int *isegY, int len, struct nullmodel_s *null, 
		     double nullsc, double nullrevsc, int verbose)
{
  nullsc    = ScoreWithNullDiag(isegX, isegY, 0, len, null);
  nullrevsc = ScoreWithNullRevDiag(isegX, isegY, 0, len, null);
  if (verbose) PrintNull(ofp, null, nullsc, nullrevsc);
}

void
score_viterbi_diag_scan_fast(FILE *ofp, 
			     SQINFO sqinfoX, int *isegX, 
			     SQINFO sqinfoY, int *isegY, 
			     char *gss, int L, int win, int slide, int j, int jmod, int l, int lmax,
			     struct model_s           *model, 
			     struct dpdscanfast_s     *dpdscan, 		     		    
			     struct rnascfgscanfast_s *mx, 
			     struct sc3_s             *sc, 
			     struct ali_s             *ali, 
			     struct scanfast_s        *scanfast,
			     int alignment, int cyk, int doends, int fastintloop, int logodds, int ones, int parse, int rnass, 
			     int revstrand, int traceback, int *ret_windows)
{
  struct othdpscanfast_s  *dpoth;
  struct coddpscanfast_s  *dpcod;
  struct rnadpscanfast_s  *dprna;
  struct rnamtxscanfast_s *mtx;
  struct end3scan_s       *ends;
  double                  *othscwin;
  double                  *codscwin;
  double                  *rnascwin;
  double                   othsc;
  double                   codsc;
  double                   rnasc;
  int                      jwrite;
  int                      idx;
  int                      d, dmax;
  int                      exact;
  int                      n_windows = 0;

  ends = AllocEnd3ScanFast();
  
  d    = l - 1;
  dmax = lmax - 1;
  
  if (revstrand) {
    dpoth = dpdscan->othscan2->htoscan;
    dpcod = dpdscan->codscan2->docscan;
    dprna = dpdscan->rnascan2->anrscan;
    
    othscwin = scanfast->sc->othrev;
    codscwin = scanfast->sc->codrev;
    rnascwin = scanfast->sc->rnarev;

    mtx = mx->inrv;
  }
  else {
    dpoth = dpdscan->othscan2->othscan;
    dpcod = dpdscan->codscan2->codscan;
    dprna = dpdscan->rnascan2->rnascan;

    othscwin = scanfast->sc->oth;
    codscwin = scanfast->sc->cod;
    rnascwin = scanfast->sc->rna;

    mtx = mx->in;
  }

   /* for every (j,d) parir 
   *
   * calculate:   fjmx[jmod][d], COJ[jmod][d], ROJ[jmod][d], wx[jmod][d]
   *
   */
  
  /* The flmx's for the 8 OTH models 
   *   O(1) in memory
   */
  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->oth->FLN, dpoth->flmx);
  
  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->cod->COB->FLN, dpcod->cob->flmx);
  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->cod->COJ->FLN, dpcod->coj->flmx);
  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->cod->COE->FLN, dpcod->coe->flmx);

  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->rna->ROB->FLN,        dprna->rob->flmx);
  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->rna->ROJ->FLN,        dprna->roj->flmx);
  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->rna->ROE->FLN,        dprna->roe->flmx);
  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->rna->pi2->Rloop->FLN, mtx->othj->flmx);


  /* The frmx's for the 8 OTH models 
   *   O(1) in memory
   */
  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->oth->FRN, dpoth->frmx);
  
  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->cod->COB->FRN, dpcod->cob->frmx);
  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->cod->COJ->FRN, dpcod->coj->frmx);
  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->cod->COE->FRN, dpcod->coe->frmx);

  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->rna->ROB->FRN,        dprna->rob->frmx);
  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->rna->ROJ->FRN,        dprna->roj->frmx);
  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->rna->ROE->FRN,        dprna->roe->frmx);
  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->rna->pi2->Rloop->FRN, mtx->othj->frmx);


  /* The fjmx's for the 8 OTH models 
   *   O(1) in memory
   */
  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->oth->FJN, dpoth->fjmx);
  
  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->cod->COB->FJN, dpcod->cob->fjmx);
  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->cod->COJ->FJN, dpcod->coj->fjmx);
  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->cod->COE->FJN, dpcod->coe->fjmx);

  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->rna->ROB->FJN,        dprna->rob->fjmx);
  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->rna->ROJ->FJN,        dprna->roj->fjmx);
  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->rna->ROE->FJN,        dprna->roe->fjmx);
  ScoreWithNullFJScan(isegX, isegY, win, j, jmod, l, model->rna->pi2->Rloop->FJN, mtx->othj->fjmx);
  
  /* COJ -- ROJ 
   *   O(1) in memory if exact == FALSE
   *   O(w) in memory if exact == TRUE
   */
  exact = FALSE;
  FillOTHMtxScanFast(ofp, sqinfoX, isegX, sqinfoY, isegY, L, win, j, jmod, l, model->cod->COJ,  
		     model->null, dpcod->coj, dpcod->cod->COJ, exact, FALSE, ends->oth);
  FillOTHMtxScanFast(ofp, sqinfoX, isegX, sqinfoY, isegY, L, win, j, jmod, l, model->rna->ROJ,  
		     model->null, dprna->roj, dprna->rna->ROJ, exact, FALSE, ends->oth);
  
  /* rnaj */
  FillOTHMtxScanFast(ofp, sqinfoX, isegX, sqinfoY, isegY, L, win, j, jmod, l, model->rna->pi2->Rloop, 
		     model-> null, mtx->othj, mtx->rnaj, exact, logodds, ends->oth);
  
  /* SCFG */
  if (cyk) CYKRNAScanFast(ofp, sqinfoX, isegX, sqinfoY, isegY, win, j, jmod, l, model->rna, model->null,
			  mtx, logodds, traceback);
  else     InsideRNAScanFast(ofp, sqinfoX, isegX, sqinfoY, isegY, win, j, jmod, l, model->rna, model->null,
			     mtx, mtx->sc, mtx->vp, fastintloop, logodds, traceback);
  
  /* Calculate oth cod and rna SCORES 
   *
   *
   *           Forward "full window" [dfmax == win-1 OR j == L-1]
   *
   *                        Store F(seq)[j][dfmax]   into F(seq)[jf]
   *
   *
   *           Backward a "full window" [j = L-1 - n*off AND j-off > 0 AND if (n>0) j+off-dbmax(j+off) > 0 ]
   *
   *                        Store F(seqrv)[j][dbmax] into F(seqrv)[jb] 
   *
   *
   */
  if (IsFullWindow(revstrand, L, win, slide, j))
    { 
      
     /* Do the OTH HMM-like calculation
       */
      othsc = ViterbiOTHDiagScanFast(ofp, sqinfoX, isegX, sqinfoY, isegY, L, win, j-dmax, (jmod-dmax<0)? jmod-dmax+win:jmod-dmax, l, lmax,
				     model->oth, dpoth, revstrand, traceback, doends, ends->oth);
      /* Do the COD HMM-like calculation
       */
      codsc = ViterbiCODDiagScanFast(ofp, sqinfoX, isegX, sqinfoY, isegY, L, win, j-dmax, (jmod-dmax<0)? jmod-dmax+win:jmod-dmax, l, lmax,
				     model->cod, dpcod, model->null, ali, alignment, logodds, revstrand, traceback, doends, ends); 
      /* Do the RNA HMM-like calculation
       */
      rnasc = ViterbiRNADiagScanFast(ofp, sqinfoX, isegX, sqinfoY, isegY, gss, L, win, j-dmax, (jmod-dmax<0)? jmod-dmax+win:jmod-dmax, l, lmax,
				     model, dprna, mtx, ali, alignment, cyk, logodds, parse, revstrand, traceback, doends, ends); 
      
      /* 
       * STORE SCORES if at a "full window"
       */
      if (d >= 0 && d == dmax) {
	
	n_windows ++;
	

	if (revstrand) {  
	  if (j < win-1) jwrite = L - 1;
	  else           jwrite = (L-1) + (win-1) -j;    
	  
	  /* paranoia */
	  if (IndexForwardWindow (L, win, slide, jwrite) != IndexBackwardWindow (L, win, slide, j)) 
	    Die ("check the jwrite assignments for the backward strand [jwrite, dfmax(jwrite)] = %d %d [j, dbmax(j)] = %d %d\n",
		 jwrite, IndexForwardWindow (L, win, slide, jwrite), j, IndexBackwardWindow (L, win, slide, j));
	  
	}
	else {
	  jwrite = j;
	}  

	idx = IndexWindow(j, L, win, slide, revstrand);
	
	if (idx > NumberScanningWindows(L, win, slide)) 
	  Die("score_viterbi_diag_scan_fast(): wrong number of windows (%d %d)", idx,  NumberScanningWindows(L, win, slide));
	
	othscwin[idx] = othsc;
	codscwin[idx] = codsc;
	rnascwin[idx] = rnasc;
	
	if (revstrand) CopyEnd3ScanFast(scanfast->ends->rev[idx], ends);
	else           CopyEnd3ScanFast(scanfast->ends->fwd[idx], ends);
	
      }
      
    }
  
  FreeEnd3ScanFast(ends);
  
  *ret_windows = n_windows;
}

int
IsFullWindow (int revstrand, int L, int win, int off, int j) 
{
  int jrv;
  int jrvup;
  int jrvdown;
  int n;
  int isfull = FALSE;

  /* forward strand
   *
   * Full window is defined by 
   *
   *          j>=(w-1) AND j = (w-1) + n*off     OR  j == L-1
   *
   *       which is equivalent to,
   *
   *          dfmax(j) == win - 1   OR  j == L-1
   *
   */
  if (!revstrand) 
    {
      if (j == L-1 || IndexForwardWindow(L, win, off, j) == win-1) isfull = TRUE;
    }
  
  /* reverse strand
   *
   * Full window is defined by
   *
   *       jrv = L-1-n*off   AND  jrvup = jrv - off > 0     
   *
   *                         AND if(n>0)  define jrvdown = jrv + off
   *                             jrvdown - dbmax(jrvdown) > 0
   */
  else 
    {
      jrv   = L-1-j;
      n     = (int)(jrv/off);
      jrvup = L-1-(n+1)*off;
      
      if (jrv > 0) 
	{
	  if (jrv%off == 0) {
	    jrvdown = L-1-(n-1)*off;
	    if (jrvdown - IndexBackwardWindow(L, win, off, jrvdown) > 0) isfull = TRUE;	
	  }      
	  
	}
      else if (jrv == 0)                                                 isfull = TRUE;
      else 
	Die("is_fullwindow() L-1-j has to be positive\n");
    }

  return isfull;
}

void
print_scan_ends (FILE *ofp, int leg, char *whichmodel, struct endscan_s *ends, int isreverse, int winnerstrand)
{
  char *strand;
  int   lend, rend;
  int   x;

  if (isreverse) { if (winnerstrand) strand = "*(-) "; else strand = " (-) "; }
  else           { if (winnerstrand) strand = " (+) "; else strand = "*(+) "; }

  fprintf(ofp, "%s ends %s=  ", whichmodel, strand);

  if (isreverse)
    {
      for (x = 0; x < MAX_NUM_ENDS; x++)
	{
	  if (ends->lend[x] > -1 && ends->rend[x]-ends->lend[x] > 0) {
	    
	    /* for the reversed strand, report the ends in the other (the "given")  strand
	     */
	    rend = leg - 1 - ends->lend[x];
	    lend = leg - 1 - ends->rend[x];
	    
	    ends->rend[x] = rend;
	    ends->lend[x] = lend;
	    
	    fprintf(ofp, "(%d..[%d]..%d) ", ends->lend[x], ends->rend[x]-ends->lend[x]+1, ends->rend[x]);
	  }
	}  
      fprintf(ofp, "\n");  
    }

  else
    {
      for (x = MAX_NUM_ENDS-1; x >= 0; x--)
	if (ends->lend[x] > -1 && ends->rend[x]-ends->lend[x] > 0) {
 	  fprintf(ofp, "(%d..[%d]..%d) ", ends->lend[x], ends->rend[x]-ends->lend[x]+1, ends->rend[x]);
	}
      fprintf(ofp, "\n");  
    }
  
}
