/* Copyright (c) 1991-2002 Doshita Lab. Speech Group, Kyoto University */
/* Copyright (c) 2000-2002 Speech and Acoustics Processing Lab., NAIST */
/*   All rights reserved   */

/* search_bestfirst_v2.c --- hypothesis handling and scoring functions
   for nextscan algorithm */

/* $Id: search_bestfirst_v2.c,v 1.7 2002/10/15 07:17:46 ri Exp $ */

/* by "fast" setting (default), search_bestfirst_v1.c is used for faster
   decoding.  Please specify option "--enable-setup=standard" or
   "--enable-strict-iwcd2" at "./configure" to activate this. */

#include <julius.h>

#ifdef PASS2_STRICT_IWCD

#undef TCD			/* define for CCD debug messages */

/**********************************************************************/
/************ 仮説ノードの基本操作                         ************/
/************ basic functions for hypothesis node handling ************/
/**********************************************************************/

/* node を解放 */
/* free node */
void
free_node(NODE *node)
{
  if (node == NULL) return;
  free(node->g);
  free(node);
}

/* src から dst へコピー */
/* copy node from 'src' to 'dst' */
NODE *
cpy_node(NODE *dst, NODE *src)
{
  
  dst->next = src->next;
  dst->prev = src->prev;
  memcpy(dst->g, src->g, sizeof(LOGPROB) * peseqlen);
  memcpy(dst->seq, src->seq, sizeof(WORD_ID) * MAXSEQNUM);
  dst->seqnum = src->seqnum;
  dst->score = src->score;
  dst->bestt = src->bestt;
  dst->estimated_next_t = src->estimated_next_t;
  dst->endflag = src->endflag;
#ifdef USE_DFA
  dst->state = src->state;
#endif
  dst->tre = src->tre;
  if (ccd_flag) {
    dst->last_ph = src->last_ph;
    dst->last_ph_sp_attached = src->last_ph_sp_attached;
  }
#ifdef USE_NGRAM
  dst->totallscore = src->totallscore;
#endif
  dst->final_g = src->final_g;
  return(dst);
}

/* 新たな仮説ノードを割り付け */
/* allocate new node */
NODE *
newnode()
{
  NODE *tmp;
  int i;

  if ((tmp=(NODE *)mymalloc(sizeof(NODE)))==NULL) {
    j_error("can't malloc\n");
  }
  /*bzero(tmp,sizeof(NODE));*/
  tmp->next=NULL;
  tmp->prev=NULL;
  tmp->g = (LOGPROB *)mymalloc(sizeof(LOGPROB)*peseqlen);
  tmp->last_ph = NULL;
  tmp->last_ph_sp_attached = FALSE;
  if (ccd_flag) {
#ifdef USE_NGRAM
    tmp->totallscore = LOG_ZERO;
#endif
  }
  tmp->endflag = FALSE;
  tmp->seqnum = 0;
  for(i=0;i<peseqlen;i++) {
    tmp->g[i] = LOG_ZERO;
  }
  tmp->final_g = LOG_ZERO;

  return(tmp);
}


/**********************************************************************/
/************ 前向きトレリス展開と尤度計算             ****************/
/************ expand trellis and update forward score *****************/
/**********************************************************************/

static LOGPROB **wordtrellis;	/* buffer for computing viterbi of a word */
static LOGPROB *g;
static HMM_Logical **phmmseq;
static int phmmlen_max;
static HMM_Logical *tailph;
static boolean *has_sp;

/* １単語分のトレリス計算用のワークエリア wordtrellis[t][]を確保 */
/* allocate work area 'wordtrellis[t][]' for trellis computation of a word */
void
malloc_wordtrellis()
{
  int i;
  int maxwn;
  LOGPROB *p;

  maxwn = winfo->maxwn + 10;	/* CCDによる変動を考慮 */
  wordtrellis = (LOGPROB **)mymalloc(sizeof(LOGPROB *) * peseqlen);
  p = (LOGPROB *)mymalloc(sizeof(LOGPROB) * peseqlen * maxwn);
  for (i=0;i<peseqlen;i++) {
    wordtrellis[i] = p + i * maxwn;
  }
  wordtrellis[0][maxwn] = 1.0;

  g = (LOGPROB *)mymalloc(sizeof(LOGPROB) * peseqlen);

  phmmlen_max = winfo->maxwlen + 2;
  phmmseq = (HMM_Logical **)mymalloc(sizeof(HMM_Logical *) * phmmlen_max);
  has_sp = (boolean *)mymalloc(sizeof(boolean) * phmmlen_max);
}
/* 上記のワークエリア wordtrellis[t][] を解放 */
/* free the 'wordtrellis[t][]' */
void
free_wordtrellis()
{
  free(wordtrellis[0]);
  free(wordtrellis);
  free(g);
  free(phmmseq);
  free(has_sp);
}



/**********************************************************************/
/************ 仮説の前向き尤度計算                  *******************/
/************ compute forward score of a hypothesis *******************/
/**********************************************************************/

/* --- 第2パス探索(RL)における単語間triphoneの扱い ---

   search_bestfirst_v2.c では，デコーディングの精度を重視して,
   次単語とその前の単語における単語間音素コンテキストは
   仮説展開時にすべて扱われる．

   backscan を行なう search_bestfirst_v1.c が仮説の POP 時に行なうのに
   比べて，仮説生成の時点で正確なスコアを計算するのでスコア精度は高く
   なる．ただし生成されるすべての仮説に対して(たとえスタックに入らない
   ような接続であっても)トレリスの再計算を行なうため，計算量は増大する．

   デフォルトは v1 を使用する．この v2 を使用する場合は
   "--enable-setup=standard" を使用する(あるいは "--enable-strict-iwcd2")
*/

/* --- handling of cross-word triphone in 2nd pass search (in RL direction) ---

   Here in search_bestfirst_v2.c, all cross-word context dependencies between
   the next word and source hypothesis are computed when new hypotheses
   are expanded.

   As the precise cross-word triphone score is applied on hypothesis
   generation with no delay, more accurate score is obtained than
   the delayed backscan method in search_bestfirst_v1.c.
   On the other hand, the computational cost grows much by re-calculating
   forward score of cross-word triphones for all generated hypothethes.
*/

/* 与えられた音素のならび phmmseq[0..phmmlen-1]に対してviterbi計算を行う．
   g[0..framelen-1] のスコアを初期値として g_new[0..framelen-1]に更新値を代入．
   最低 least_frame まではscanする．*/
/* Viterbi computation for the given phoneme sequence 'phmmseq[0..phmmlen-1]'
   with g[0..framelen-1] as initial values.  The results are stored in
   g_new[0..framelen-1].  Scan should not terminate at least it reaches
   'least_frame'. */
static void
do_viterbi(LOGPROB *g, LOGPROB *g_new, HMM_Logical **phmmseq, boolean *has_sp, int phmmlen, HTK_Param *param, int framelen, int least_frame, LOGPROB *final_g)
{
  HMM *whmm;			/* HMM */
  int wordhmmnum;		/* length of above */
  int startt;			/* scan start frame */
  LOGPROB tmpmax,tmpscore;	/* variables for Viterbi process */
  A_CELL *ac;
  int t,i;
  boolean node_exist_p;

#ifdef TCD
  j_printf("scan for:");
  for (i=0;i<phmmlen;i++) {
    j_printf(" %s", phmmseq[i]->name);
  }
  j_printf("\n");
#endif
  
  /* 単語HMMを作る */
  /* make word HMM */
  whmm = new_make_word_hmm(hmminfo, phmmseq, phmmlen, has_sp);
  wordhmmnum = whmm->len;
  if (wordhmmnum >= winfo->maxwn + 10) {
    j_error("scan_word: word too long\n");
  }

  /* scan開始点を検索 -> starttへ*/
  /* search for the start frame -> set to startt */
  for(t = framelen-1; t >=0 ; t--) {
    if (
#ifdef SCAN_BEAM
	g[t] > framemaxscore[t] - scan_beam_thres &&
#endif
	g[t] > LOG_ZERO) {
      break;
    }
  }
  if (t < 0) {			/* no node has score > LOG_ZERO */
    /* reset all scores and end */
    for(t=0;t<framelen;t++) {
      g_new[t] = LOG_ZERO;
    }
    free_hmm(whmm);
    return;
  }
  startt = t;
  
  /* 開始点以降[startt+1..framelen-1] の g_new[] をリセット */
  /* clear g_new[] for [startt+1..framelen-1] */
  for(t=framelen-1;t>startt;t--) g_new[t] = LOG_ZERO;

  /*****************/
  /* viterbi start */
  /*****************/
  
  /* メインループ: startt から始まり 0 に向かって Viterbi 計算 */
  /* main loop: start from [startt], and compute Viterbi toward [0] */
  for(t=startt; t>=0; t--) {
    
    node_exist_p = FALSE;	/* TRUE if there is at least 1 survived node in this frame */

    /* node[wordhmmnum-2..0]についてトレリスを展開 */
    /* expand trellis for node [t][wordhmmnum-2..0] */
    for(i=wordhmmnum-2;i>=0;i--) {
      
      /* 最尤パスと最尤スコア tmpmax を見つける */
      /* find most likely path and the max score 'tmpmax' */
      tmpmax = LOG_ZERO;
      for (ac=whmm->state[i].ac;ac;ac=ac->next) {
	if (ac->arc == wordhmmnum-1) tmpscore = g[t];
	else if (t + 1 > startt) tmpscore = LOG_ZERO;
	else tmpscore = wordtrellis[t+1][ac->arc];
	tmpscore += ac->a;
	if (tmpmax < tmpscore) tmpmax = tmpscore;
      }
      
      /* スコアエンベロープチェック: 一定幅外なら落とす */
      /* check if score of this node is within the score envelope */
      if (
#ifdef SCAN_BEAM
	  tmpmax <= framemaxscore[t] - scan_beam_thres ||
#endif
	  tmpmax <= LOG_ZERO
	  ) {
	/* invalid node */
	wordtrellis[t][i] = LOG_ZERO;
      } else {
	/* survived node */
	node_exist_p = TRUE;
	wordtrellis[t][i] = tmpmax;
	if (i > 0) wordtrellis[t][i] += outprob(t, &(whmm->state[i]), param);
      }
    } /* end of node loop */

    /* 時間 t のViterbi計算終了．新たな前向きスコア g_new[t] をセット */
    /* Viterbi end for frame [t].  set the new forward score g_new[t] */
    g_new[t] = wordtrellis[t][0];

    /* 指定された least_frame より先まで t が進んでおり，かつこの t において
       スコアエンベロープによって生き残ったノードが一つも無かった場合,
       このフレームで計算を打ち切りそれ以上先([0..t-1])は計算しない */
    /* if frame 't' already reached the 'least_frame' and no node was
       survived in this frame (all nodes pruned by score envelope),
       terminate computation at this frame and do not computer further
       frame ([0..t-1]). */
    if (t < least_frame && (!node_exist_p)) {
      /* crear the rest scores */
      for (i=t-1;i>=0;i--) {
	g_new[i] = LOG_ZERO;
      }
      /* terminate loop */
      break;
    }
    
  } /* end of time loop */
  /* 前向きスコアの最終値を計算 (状態 0 から時間 0 への遷移) */
  /* compute the total forward score (transition from state 0 to frame 0 */
  tmpmax = LOG_ZERO;
  for(ac=whmm->state[0].ac;ac;ac=ac->next) {
    tmpscore = wordtrellis[0][ac->arc] + ac->a;
    if (tmpmax < tmpscore) tmpmax = tmpscore;
  }
  *final_g = tmpmax;

  /* free work area */
  free_hmm(whmm);
}

/* next_word内で, now の最後の1音素(lastphone)分に対してviterbi計算を
   行なう．now->g[] を初期値として new->g[] に更新値を代入 */
/* for next_word(), do Viterbi computation for the last phone ('lastphone'),
   with now->g[] as initial value.  Results are stored in new->g[] */
static void
do_viterbi_next_word(NODE *now, NODE *new, HMM_Logical *lastphone, boolean sp, HTK_Param *param)
{
  int t, n;

  for(t=0; t<peseqlen; t++) g[t] = now->g[t];
  
  /* do viterbi computation for the last phone */
  phmmseq[0] = lastphone;
  has_sp[0] = sp;
  do_viterbi(g, new->g, phmmseq, has_sp, 1, param, peseqlen, now->estimated_next_t, &(new->final_g));
}

/* 文仮説の前向き尤度を更新する
   最後の1単語の前向きトレリスを計算する */
/* compute forward score by expanding trellis of the last word */
void
scan_word(NODE *now, HTK_Param *param)
{
  int   i,t;
  WORD_ID word;
  int phmmlen;
  boolean tail_ph_sp_attached;
  
  /* ----------------------- prepare phoneme sequence ------------------ */
  /* triphoneなら先頭の1音素はここでは対象外(あとでnext_wordでやる) */
  /*             末尾の1音素はコンテキストにしたがって置換 */
  /* with triphone, modify the tail phone of the last word according to the
     previous word, and do not compute the head phone here (that will be
     computed later in next_word() */
  word = now->seq[now->seqnum-1];
  
#ifdef TCD
    j_printf("w=");
    for(i=0;i<winfo->wlen[word];i++) {
      j_printf(" %s",(winfo->wseq[word][i])->name);
    }
    if (ccd_flag) {
      if (now->last_ph != NULL) {
	j_printf(" | %s", (now->last_ph)->name);
      }
    }
    j_printf("\n");
#endif /* TCD */
    
  if (ccd_flag) {
    
    /* the tail triphone of the last word varies by context */
    if (now->last_ph != NULL) {
      tailph = get_right_context_HMM(winfo->wseq[word][winfo->wlen[word]-1], now->last_ph->name, hmminfo);
      if (tailph == NULL) {
	/* fallback to the original bi/mono-phone */
	/* error if the original is pseudo phone (not explicitly defined
	   in hmmdefs/hmmlist) */
	/* exception: word with 1 phone (triphone may exist in the next expansion */
	if (winfo->wlen[word] > 1 && winfo->wseq[word][winfo->wlen[word]-1]->is_pseudo){
	  error_missing_right_triphone(winfo->wseq[word][winfo->wlen[word]-1], now->last_ph->name);
	}

	tailph = winfo->wseq[word][winfo->wlen[word]-1];
      }
    } else {
      tailph = winfo->wseq[word][winfo->wlen[word]-1];
    }

    /* 長さ1の単語は次のnextwordでさらに変化するのでここではscanしない */
    /* do not scan word if the length is 1, as it further varies in the
       following next_word() */
    if (winfo->wlen[word] == 1) {
      now->last_ph = tailph;
      now->last_ph_sp_attached = TRUE;
#ifdef TCD
      j_printf("suspended as %s\n", (now->last_ph)->name);
#endif
      return;
    }

    /* scan範囲の音素列を準備 */
    /* prepare HMM of the scan range */
    phmmlen = winfo->wlen[word] - 1;
    if (phmmlen > phmmlen_max) {
      j_error("short of phmmlen\n");
    }
    for (i=0;i<phmmlen-1;i++) {
      phmmseq[i] = winfo->wseq[word][i+1];
      has_sp[i] = FALSE;
    }
    phmmseq[phmmlen-1] = tailph;
    has_sp[phmmlen-1] = (enable_iwsp) ? TRUE : FALSE;
  } else {
    phmmlen = winfo->wlen[word]; 
    for (i=0;i<phmmlen;i++) {
      phmmseq[i] = winfo->wseq[word][i];
      has_sp[i] = FALSE;
    }
    if (enable_iwsp) has_sp[phmmlen-1] = TRUE;
  }

  /* 元のg[]をいったん待避しておく */
  /* temporally keeps the original g[] */
  for (t=0;t<peseqlen;t++) g[t] = now->g[t];

  /* viterbiを実行して g[] から now->g[] を更新する */
  /* do viterbi computation for phmmseq from g[] to now->g[] */
  do_viterbi(g, now->g, phmmseq, has_sp, phmmlen, param, peseqlen, now->estimated_next_t, &(now->final_g));

  if (ccd_flag) {
    /* 次回のために now->last_ph を更新 */
    /* update 'now->last_ph' for future scan_word() */
    now->last_ph = winfo->wseq[word][0];
    now->last_ph_sp_attached = FALSE; /* wlen > 1 here */
#ifdef TCD
    j_printf("last_ph = %s\n", (now->last_ph)->name);
#endif
  }
}

/**************************************************************************/
/*** 新仮説の展開とヒューリスティックを繋いだ全体スコアを計算           ***/
/*** expand new hypothesis and compute the total score (with heuristic) ***/
/**************************************************************************/

/* now に次単語 nword を接続して新しい仮説 new を生成し，その
   (ヒューリスティックを含む)仮説スコアを求める */
/* generate a new hypothesis 'new' by connecting next word 'nword' to
   the source hypothesis 'now', and compute the total score of 'new' with
   heuristics */
static HMM_Logical *lastphone, *newphone;
static LOGPROB *g_src;
void
next_word(NODE *now, NODE *new, NEXTWORD *nword, HTK_Param *param, BACKTRELLIS *backtrellis)
{
  int   t;
  int lastword;
  int   i;
  LOGPROB a_value;
  int   startt;
  int word;
  LOGPROB totalscore, tmpp;
  TRELLIS_ATOM *tre;

  word = nword->id;
  lastword = now->seq[now->seqnum-1];

  /* lastphone (直前単語の先頭音素) を準備 */
  /* prepare lastphone (head phone of previous word) */
  if (ccd_flag) {
    /* 最終音素 triphone を接続単語に会わせて変化 */
    /* modify triphone of last phone according to the next word */
    lastphone = get_left_context_HMM(now->last_ph, winfo->wseq[word][winfo->wlen[word]-1]->name, hmminfo);
    if (lastphone == NULL) {
      /* fallback to the original bi/mono-phone */
      /* error if the original is pseudo phone (not explicitly defined
	 in hmmdefs/hmmlist) */
      /* exception: word with 1 phone (triphone may exist in the next expansion */
      if (now->last_ph->is_pseudo){
	error_missing_left_triphone(now->last_ph, winfo->wseq[word][winfo->wlen[word]-1]->name);
      }
      lastphone = now->last_ph;
    }
  }

  /* newphone (接続単語の末尾音素) を準備 */
  /* prepare newphone (tail phone of next word) */
  if (ccd_flag) {
    newphone = get_right_context_HMM(winfo->wseq[word][winfo->wlen[word]-1], now->last_ph->name, hmminfo);
    if (newphone == NULL) {
      /* fallback to the original bi/mono-phone */
      /* error if the original is pseudo phone (not explicitly defined
	 in hmmdefs/hmmlist) */
      /* exception: word with 1 phone (triphone may exist in the next expansion */
      if (winfo->wlen[word] > 1 && winfo->wseq[word][winfo->wlen[word]-1]->is_pseudo){
	error_missing_right_triphone(winfo->wseq[word][winfo->wlen[word]-1], now->last_ph->name);
      }
      newphone = winfo->wseq[word][winfo->wlen[word]-1];
    }
  } else {
    newphone = winfo->wseq[word][winfo->wlen[word]-1];
  }
  
  /* 単語並び、DFA状態番号、言語スコアを new へ継承・更新 */
  /* inherit and update word sequence, DFA state and total LM score to 'new' */
  new->score = LOG_ZERO;
  for (i=0;i< now->seqnum;i++){
    new->seq[i] = now->seq[i];
  }
  new->seq[i] = word;
  new->seqnum = now->seqnum+1;
#ifdef USE_DFA
  new->state = nword->next_state;
#endif
#ifdef USE_NGRAM
  new->totallscore = now->totallscore + nword->lscore;
#endif  
  if (ccd_flag) {
    /* 次仮説の履歴情報として保存 */
    /* keep the lastphone for next scan_word() */
    new->last_ph = lastphone;
    new->last_ph_sp_attached = now->last_ph_sp_attached;
  }

  if (ccd_flag) {
    /* 最後の1音素(lastphone)分をscanし，更新したスコアを new に保存 */
    /* scan the lastphone and set the updated score to new->g[] */
    do_viterbi_next_word(now, new, lastphone, now->last_ph_sp_attached, param);
    g_src = new->g;
  } else {
    g_src = now->g;
  }
      
  /* 次回の scan_word に備えて new->g[] を変更しておく */
  /* prepare new->g[] for next scan_word() */
  startt = peseqlen-1;
  i = hmm_logical_state_num(newphone);
  a_value = (hmm_logical_trans(newphone))->a[i-2][i-1];
  for(t=0; t <= startt; t++) {
    new->g[t] = g_src[t]
#ifdef USE_NGRAM
      + nword->lscore
#else
      + penalty2
#endif
      ;
  }

  /***************************************************************************/
  /* 前向き(第２パス),後ろ向き(第１パス)トレリスを接続し最尤接続点を見つける */
  /* connect forward/backward trellis to look for the best connection time   */
  /***************************************************************************/
#ifdef WORD_GRAPH
  /*-----------------------------------------------------------------*/
  /* 単語グラフモードでは同じフレームの同じ単語でも直前単語毎にスコ
     アが違う．bestt は 固定 */
  /* there are several trellis word with the same word ID in the same
     frame, with different word context in WORD_GRAPH mode.  And the
     best t is fixed (no re-esimation) */
  /*-----------------------------------------------------------------*/
  /* 最尤接続点は, 最後に参照したTRELLIS_ATOMの終端に固定 */
  /* the best connection point is fixed to the end time of last trellis word (no re-alignment */
  new->bestt = (nword->tre)->endtime;
  /* 次回の展開単語決定のための推定始端時間は，上記の始端 */
  /* the estimated next beginning frame for the next word expansion
     corresponds to the endtime above (this is also fixed) */
  new->estimated_next_t = (nword->tre)->begintime - 1;

#if 0
  /* 出力確率を tmpp に計算 */
  /* compute output prob to 'tmpp' */
  if (newphone->is_pseudo) {
    tmpp = outprob_cd(new->bestt, &(newphone->body.pseudo->stateset[newphone->body.pseudo->state_num-2]), param);
  } else {
    tmpp = outprob_state(new->bestt, newphone->body.defined->s[newphone->body.defined->state_num-2], param);
  }
#endif

  /* 仮説全体のスコアを計算 */
  new->score = new->g[new->bestt] + (nword->tre)->backscore /*+ tmpp*/;
  
  /* 展開単語のトレリス上の情報を伝搬 */
  new->tre  =  nword->tre;
  
#else  /* not WORD_GRAPH */
  
  /*-----------------------------------------------------------------*/
  /* 単語トレリスを探して, 次単語の最尤接続点を発見する */
  /* determine the best connection time of the new word, seeking the word
     trellis */
  /*-----------------------------------------------------------------*/

#ifdef USE_DFA
  if (!looktrellis_flag) {
    /* すべてのフレームにわたって最尤を探す */
    /* search for best trellis word throughout all frame */
    for(t = startt; t >= 0; t--) {
      tre = bt_binsearch_atom(backtrellis, t, (WORD_ID) word);
      if (tre == NULL) continue;
#if 0
      if (newphone->is_pseudo) {
	tmpp = outprob_cd(t, &(newphone->body.pseudo->stateset[newphone->body.pseudo->state_num-2]), param);
      } else {
      tmpp = outprob_state(t, newphone->body.defined->s[newphone->body.defined->state_num-2], param);
      }
#endif
      totalscore = new->g[t] + tre->backscore /*+ tmpp*/;
      if (new->score < totalscore) {
	new->score = totalscore;
	new->bestt = t;
	new->estimated_next_t = tre->begintime - 1;
      }
    }
  } else {
#endif /* USE_DFA */
    
  /* 最後に参照したTRELLIS_ATOMの終端時間の前後 */
  /* newの推定時間は，上記で採用したTRELLIS_ATOMの始端時間 */

  /* この展開単語のトレリス上の終端時間の前後のみスキャンする
     前後に連続して存在するフレームについてのみ計算 */
  /* search for best trellis word only around the estimated time */
  /* 1. search forward */
  for(t = (nword->tre)->endtime; t >= 0; t--) {
    tre = bt_binsearch_atom(backtrellis, t, (WORD_ID) word);
    if (tre == NULL) break;	/* go to 2 if the trellis word disappear */
#if 0
    if (newphone->is_pseudo) {
      tmpp = outprob_cd(t, &(newphone->body.pseudo->stateset[newphone->body.pseudo->state_num-2]), param);
    } else {
      tmpp = outprob_state(t, newphone->body.defined->s[newphone->body.defined->state_num-2], param);
    }
#endif
    totalscore = new->g[t] + tre->backscore /*+ tmpp*/;
    if (new->score < totalscore) {
      new->score = totalscore;
      new->bestt = t;
      new->estimated_next_t = tre->begintime - 1;
    }
  }
  /* 2. search bckward */
  for(t = (nword->tre)->endtime + 1; t <= startt; t++) {
    tre = bt_binsearch_atom(backtrellis, t, (WORD_ID) word);
    if (tre == NULL) break;	/* end if the trellis word disapper */
#if 0
    if (newphone->is_pseudo) {
      tmpp = outprob_cd(t, &(newphone->body.pseudo->stateset[newphone->body.pseudo->state_num-2]), param);
    } else {
      tmpp = outprob_state(t, newphone->body.defined->s[newphone->body.defined->state_num-2], param);
    }
#endif
    totalscore = new->g[t] + tre->backscore /*+ tmpp*/;
    if (new->score < totalscore) {
      new->score = totalscore;
      new->bestt = t;
      new->estimated_next_t = tre->begintime - 1;
    }
  }

#ifdef USE_DFA
  }
#endif

#endif /* WORD_GRAPH */
}

/**********************************************************************/
/********** 初期仮説の生成                 ****************************/
/********** generate an initial hypothesis ****************************/
/**********************************************************************/
void
start_word(
     NODE *new,
     NEXTWORD *nword,
     HTK_Param *param,
     BACKTRELLIS *backtrellis)
{
  HMM_Logical *newphone;
  WORD_ID word;
  TRELLIS_ATOM *tre = NULL;
  int t;
  LOGPROB tmpp;

  /* initialize data */
  word = nword->id;
  new->score = LOG_ZERO;
  new->seqnum = 1;
  new->seq[0] = word;

#ifdef USE_DFA
  new->state = nword->next_state;
#endif
#ifdef USE_NGRAM
  new->totallscore = nword->lscore;
#endif  

  /* cross-word triphone need not be handled on startup */
  newphone = winfo->wseq[word][winfo->wlen[word]-1];
  if (ccd_flag) {
    new->last_ph = NULL;
  }
  
#ifdef USE_NGRAM
  new->g[peseqlen-1] = nword->lscore;
#else  /* USE_DFA */
  new->g[peseqlen-1] = 0;
#endif
  
#ifdef WORD_GRAPH
  new->score = new->g[peseqlen-1] + (nword->tre)->backscore;
  new->tre  =  nword->tre;
  new->bestt = peseqlen-1;
  new->estimated_next_t = (nword->tre)->begintime - 1;
  
#else
  
  for (t=peseqlen-1; t>=0; t--) {
    tre = bt_binsearch_atom(backtrellis, t, word);
    if (tre != NULL) {
      new->bestt = t;
#if 0
      if (newphone->is_pseudo) {
	tmpp = outprob_cd(peseqlen-1, &(newphone->body.pseudo->stateset[newphone->body.pseudo->state_num-2]), param);
      } else {
	tmpp = outprob_state(peseqlen-1, newphone->body.defined->s[newphone->body.defined->state_num-2], param);
      }
#endif
      new->score = new->g[peseqlen-1] + tre->backscore /*+ tmpp*/;
      new->estimated_next_t = tre->begintime - 1;
      break;
    }
  }
  if (tre == NULL) {		/* no word in backtrellis */
    new->score = LOG_ZERO;
  }
#endif /* WORD_GRAPH */
}

/* 最終仮説のスコア計算 */
/* generate final hypothesis from 'now' to 'new' */
void
last_next_word(NODE *now, NODE *new, HTK_Param *param)
{
  cpy_node(new, now);
  if (ccd_flag) {
    /* 最終音素分を viterbi して最終スコアを設定 */
    /* scan the last phone and update the final score */
    do_viterbi_next_word(now, new, now->last_ph, now->last_ph_sp_attached, param);
    new->score = new->final_g;
  } else {
    new->score = now->final_g;
  }
}

#endif /* PASS2_STRICT_IWCD */
