/* 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_main.c --- 2nd pass: stack decoding (main) */

/* $Id: search_bestfirst_main.c,v 1.12 2002/11/06 08:36:05 ri Exp $ */


/* NODE = hypothesis */

#include <julius.h>

static NODE *get_best_from_stack(NODE **start, int *stacknum);
static int put_to_stack(NODE *new, NODE **start, NODE **bottom, int *stacknum, int stacksize);
static void put_all_in_stack(NODE **start, int *stacknum);
static void free_all_nodes(NODE *node);
static void put_hypo_woutput(NODE *hypo);
static void put_hypo_wname(NODE *hypo);
static void put_hypo_phoneme(NODE *hypo);



/**********************************************************************/
/********** 次単語格納領域の割り当て          *************************/
/********** allocate memory for nextword data *************************/
/**********************************************************************/

/* 予測次単語格納領域 NEXTWORD 配列を割り付けてポインタを返す */
/* allocate NEXTWORD array and return the newly allocated pointer */
static NEXTWORD **
nw_malloc(int *maxlen, NEXTWORD **root)
{
  NEXTWORD *nwtmp;
  NEXTWORD **nw;
  int i;
  int max;

  /* the initial maximum number of nextwords is the size of vocabulary */
  max = winfo->num;
  
  nw = (NEXTWORD **)mymalloc(max * sizeof(NEXTWORD *));
  nwtmp = (NEXTWORD *)mymalloc(max * sizeof(NEXTWORD));
  for (i=0;i<max; i++) {
    nw[i] = &(nwtmp[i]);
  }
  *maxlen = max;
  *root = nwtmp;
  return nw;
}

/* 予測次単語格納領域を解放 */
/* free the given nextword data 'nw' */
static void
nw_free(NEXTWORD **nw, NEXTWORD *root)
{
  free(root);
  free(nw);
}

#ifdef USE_DFA
/* DFA モードでは次単語集合の大きさが語彙サイズを越えることがある．
   これはショートポーズのスキップ処理によって複数状態(spあり/なし)が
   同時に展開される場合に起こりうる．以下の関数はその場合に呼ばれ,
   さらに語彙数分次単語集合エリアを伸長する．*/
/* In DFA mode, the number of nextwords can exceed the vocabulary size when
   more than one DFA states are expanded by short-pause skipping.
   In such case, the nextword data area should expanded */
static NEXTWORD **
nw_expand(NEXTWORD **nwold, int *maxlen, NEXTWORD **root)
{
  NEXTWORD *nwtmp;
  NEXTWORD **nw;
  int i;
  int nwmaxlen;

  nwmaxlen = *maxlen + winfo->num;

  nwtmp = (NEXTWORD *)myrealloc(*root, nwmaxlen * sizeof(NEXTWORD));
  nw = (NEXTWORD **)myrealloc(nwold, nwmaxlen * sizeof(NEXTWORD *));
  nw[0] = nwtmp;
  for (i=1;i<nwmaxlen; i++) {
    nw[i] = &(nwtmp[i]);
  }
  *maxlen = nwmaxlen;
  *root = nwtmp;
  return nw;
}
#endif



/**********************************************************************/
/********** 仮説スタックの操作         ********************************/
/********** hypothesis stack operation ********************************/
/**********************************************************************/

/* 文仮説スタックは double linked list である．また常にスコア順が保たれる */
/* the hypothesis stack is double-linked list, and holds entries in sorted order */

/* スタックトップの仮説を取り出す */
/* pop the top hypothesis from the stack */
static NODE *
get_best_from_stack(NODE **start, /* pointer to stack top node */
		    int *stacknum /* pointer to stack size */
		    )
{
  NODE *tmp;

  /* return top */
  tmp=(*start);
  if ((*start)!=NULL) {		/* delete it from stack */
    (*start)=(*start)->next;
    if ((*start)!=NULL) (*start)->prev=NULL;
    (*stacknum)--;
    return(tmp);
  }
  else {
    return(NULL);
  }
}

/* スタックに仮説 new を挿入する   返り値: 成功=0, 失敗=-1 */
/* insert 'new' hypothesis to stack (keep order)
   retrun 0 on success, -1 on failure */
static int
put_to_stack(
     NODE *new,			/* new hypothesis */
     NODE **start,		/* pointer to stack top node */
     NODE **bottom,		/* pointer to stack bottom node */
     int *stacknum,		/* pointer to stack size */
     int stacksize)		/* maximum stack size */
{
  NODE *tmp;

  (*stacknum)++;

  /* stack size check */
  if ((*stacknum)>=stacksize) {
    /* stack size overflow */
    (*stacknum)--;
    if ((*bottom)->score < new->score) {
      /* new node will be inserted in the stack: free the bottom */
      tmp=(*bottom);
      (*bottom)->prev->next=NULL;
      (*bottom)=(*bottom)->prev;
      free_node(tmp);
    } else {
      /* new node is below the bottom: discard it */
      free_node(new);
      return(-1);
    }
  }

  /* insert new node on edge */
  if ((*start)==NULL) {		/* no node in stack */
    /* new node is the only node */
    (*start)=new;
    (*bottom)=new;
    new->next=NULL;
    new->prev=NULL;
    return(0);
  }
  if ((*start)->score <= new->score) {
    /* insert on the top */
    new->next = (*start);
    new->next->prev = new;
    (*start)=new;
    new->prev=NULL;
    return(0);
  }
  if ((*bottom)->score >= new->score) {
    /* insert on the bottom */
    new->prev = (*bottom);
    new->prev->next = new;
    (*bottom)=new;
    new->next=NULL;
    return(0);
  }
    
  /* now the new node is between (*start) and (*bottom) */
  if (((*start)->score + (*bottom)->score) / 2 > new->score) {
    /* search from bottom */
    tmp=(*bottom);
    while(tmp->score < new->score) tmp=tmp->prev;
    new->prev=tmp;
    new->next=tmp->next;
    tmp->next->prev=new;
    tmp->next=new;
  } else {
    /* search from start */
    tmp=(*start);
    while(tmp->score > new->score) tmp=tmp->next;
    new->next=tmp;
    new->prev=tmp->prev;
    tmp->prev->next=new;
    tmp->prev=new;
  }
  return(0);
}

/* スタックの中身を全て出力する．スタックの中身は失われる．(デバッグ用) */
/* output all nodes in the stack.  all nodes will be lost (for debug) */
static void
put_all_in_stack(NODE **start, int *stacknum)
{
  NODE *ntmp;
  
  j_printf("stack remains::\n");
  while ((ntmp = get_best_from_stack(start, stacknum)) != NULL) {
    j_printf("%3d: s=%f ",*stacknum, ntmp->score);
    put_hypo_woutput(ntmp);
    free_node(ntmp);
  }
}

/* スタック内の全仮説を解放 */
/* free all nodes in stack */
static void
free_all_nodes(NODE *start)
{
  NODE *tmp;
  NODE *next;

  tmp=start;
  while(tmp) {
    next=tmp->next;
    free_node(tmp);
    tmp=next;
  }
}


/**********************************************************************/
/********** enveloped best-first search *******************************/
/**********************************************************************/

/*****************/
/* word envelope */
/*****************/

/* 一種の仮説ビーム幅を設定: 展開元となった仮説の数をその仮説長(単語数)
   ごとにカウントする．一定数を越えたらそれより短い仮説は以後展開しない */
/* introduce a kind of beam width: count the number of popped hypotheses
   per the length.  When a count in a certain length reaches given threshold,
   hypotheses shorter than the length will be discarded */

static int hypo_len_count[MAXSEQNUM+1];	/* count per hypothesis length */
static int maximum_filled_length;

/* initialize count */
static void
wb_init()
{
  int i;
  for(i=0;i<=MAXSEQNUM;i++) hypo_len_count[i] = 0;
  maximum_filled_length = -1;
}

/* Count number of popped hypotheses, and return FALSE if it reaches the
   limit.  Otherwise return TRUE */
static boolean
wb_ok(NODE *now)
{
  int i;

  if (now->seqnum <= maximum_filled_length) {
    if (debug2_flag) {
      j_printf("popped but pruned by word envelope: ");
      put_hypo_woutput(now);
    }
    return FALSE;
  } else {
    hypo_len_count[now->seqnum]++;
    if (hypo_len_count[now->seqnum] > enveloped_bestfirst_width) {
      if (maximum_filled_length < now->seqnum) maximum_filled_length = now->seqnum;
    }
    return TRUE;
  }
}

#ifdef SCAN_BEAM
/******************/
/* score envelope */
/******************/

/* Viterbi計算量削減: 全仮説にわたるフレームごとの最大尤度を
   記録しておく．トレリス展開時(前向き尤度計算時)にその最大尤度系列
   (すなわちenvelope) から一定スコア幅以上離れたViterbiパスは,
   それ以降のフレームは計算から除外する．詳細は scan_word() 参照のこと */
/* reduce computation cost in hypothesis scoring: keep the maximum
   score at each frame throughout the expanded hypotheses as 'score envelope'.
   When expanding HMM trellis for updating score, only Viterbi paths that
   are within a certain range from the maximum scores are expanded.
   see scan_word() for detail */

/* the score envelope is kept in framemaxscore[0..framelen-1] */

/* initialize score envelope */
void
envl_init(int framenum)		/* maximum frame num */
{
  int i;
  for(i=0;i<framenum;i++) framemaxscore[i] = LOG_ZERO;
}

/* update score envelope using forward score g[] of hypothesis 'n' */
static void
envl_update(NODE *n, int framenum)
{
  int t;
  for(t=framenum-1;t>=0;t--) {
    if (framemaxscore[t] < n->g[t]) framemaxscore[t] = n->g[t];
  }
}
#endif /* SCAN_BEAM */


#ifdef USE_DFA
static NEXTWORD fornoise;		/* noise dummy */
#endif


#ifdef SP_BREAK_CURRENT_FRAME
/**********************************************************************/
/********** short pause segmentation **********************************/
/**********************************************************************/
/* 次のセグメントの第１パスを開始する際の初期単語履歴を hypo から伝搬させる．
   初期単語履歴は, 仮説の最終単語(hypo->seq[0..])から数えて,
   探索を再開する初期仮説単語と異なりかつ透過語でない最初の単語．
   該当単語無し(このセグメントが1単語や透過単語だけだった)の場合,
   元の値(以前のセグメントの値)をそのまま保持
*/
/* set the initial word context info of the next segment from hypothesis
   'hypo'.  the initial context word will be the last word which is not
   equal to the resuming word (initial hypo in the next segment) and
   not transparent word.  If no suitable word is found in hypothesis 'hypo',
   the previous value (given by the previous input and used in this segment)
   is kept */
void
sp_segment_set_last_nword(NODE *hypo)
{
  int i;
  WORD_ID w;
  for(i=0;i<hypo->seqnum;i++) {
    w = hypo->seq[i];
    if (w != sp_break_last_word
       && !is_sil(winfo->wseq[w][0]->name)
       && !winfo->is_transparent[w]
       ) {
      sp_break_last_nword = w;
      break;
    }
  }
}
#endif


/**********************************************************************/
/********* debug output of hypothesis *********************************/
/**********************************************************************/

/* 通常の出力関数は result_*.c にある */
/* normal output functions are in result_*.c */

static HTK_Param *tparam;	/* temporal for forced alignment */

/* output word sequence of a hypothesis */
static void
put_hypo_woutput(NODE *hypo)
{
  int i,w;

  if (hypo != NULL) {
    for (i=hypo->seqnum-1;i>=0;i--) {
      w = hypo->seq[i];
      j_printf(" %s",winfo->woutput[w]);
    }
    /*    if (speech_input == SP_ADINSERV) {
      for (i=hypo->seqnum-1;i>=0;i--) {
	w = hypo->seq[i];
	if (strlen(winfo->woutput[w]) > 0)
	  wt(adinnet_asd, winfo->woutput[w], strlen(winfo->woutput[w]));
      }
      wt(adinnet_asd, (char *)&i, 0);
      }*/
  }
  j_printf("\n");  
}

/* output N-gram entry (or DFA ID) sequence of a hypothesis */
static void
put_hypo_wname(NODE *hypo)
{
  int i,w;

  if (hypo != NULL) {
    for (i=hypo->seqnum-1;i>=0;i--) {
      w = hypo->seq[i];
      j_printf(" %s",winfo->wname[w]);
    }
  }
  j_printf("\n");  
}

/* output phoneme sequence (with word delimiter '|') of a hypothesis */
static void
put_hypo_phoneme(NODE *hypo)
{
  int i,j,w;
  char buf[20];

  if (hypo != NULL) {
    for (i=hypo->seqnum-1;i>=0;i--) {
      w = hypo->seq[i];
      for (j=0;j<winfo->wlen[w];j++) {
	center_name(winfo->wseq[w][j]->name, buf);
	j_printf(" %s", buf);
      }
      if (i > 0) j_printf(" |");
    }
  }
  j_printf("\n");  
}

/**********************************************************************/
/******** output top 'ncan' hypotheses in a stack and free all ********/
/**********************************************************************/
static void
result_reorder_and_output(NODE **r_start, /* top node */
			  NODE **r_bottom, /* bottom node */
			  int *r_stacknum, /* total number of node in stack */
			  int ncan, /* number to pop */
			  WORD_INFO *winfo)
{
  NODE *now;
  int num;

  num = 0;
  while ((now = get_best_from_stack(r_start,r_stacknum)) != NULL && num < ncan) {
    num++;
    /* output result */
    result_pass2(now, num, winfo);
#ifdef SP_BREAK_CURRENT_FRAME
    /* set the last context-aware word for next recognition */
    if (sp_break_last_nword_allow_override && num == 1) sp_segment_set_last_nword(now);
#endif
    /* do forced alignment if needed */
    if (align_result_word_flag) word_rev_align(now->seq, now->seqnum, tparam);
    if (align_result_phoneme_flag) phoneme_rev_align(now->seq, now->seqnum, tparam);
    if (align_result_state_flag) state_rev_align(now->seq, now->seqnum, tparam);
    free_node(now);
  }
  /* free the rest */
  if (now != NULL) free_node(now);
  free_all_nodes(*r_start);
}  



/**********************************************************************/
/********* main stack decoding function *******************************/
/**********************************************************************/

/* subfunctions called by this main function:
     for word prediction: ngram_decode.c or dfa_decode.c
     for hypothesis expansion and forward viterbi: search_bestfirst_v{1,2}.c
                                  (v1: for efficiency   v2: for accuracy)
*/

void
wchmm_fbs(
     HTK_Param *param,		/* input parameter vector */
     BACKTRELLIS *backtrellis,	/* backward word trellis from 1st pass */
     LOGPROB backmax,		/* maximum score in backward trellis */
     int stacksize,		/* stack size of hypothesis */
     int ncan,			/* num of hypothesis to get */
     int maxhypo		/* search abort when 'maxhypo' hypotheses are popped */
     )
{
  /* 文仮説スタック */
  /* hypothesis stack (double-linked list) */
  int stacknum;			/* current stack size */
  NODE *start = NULL;		/* top node */
  NODE *bottom = NULL;		/* bottom node */

  /* 認識結果格納スタック(結果はここへいったん集められる) */
  /* result sentence stack (found results will be stored here and then re-ordered) */
  int r_stacksize = ncan + 1;
  int r_stacknum;
  NODE *r_start = NULL;
  NODE *r_bottom = NULL;

  /* 種々のカウンター */
  /* monitoring counters */
  int popctr = 0;		/* num of popped hypotheses from stack */
  int genectr = 0;		/* num of generated hypotheses */
  int pushctr = 0;		/* num of hypotheses actually pushed to stack */
  int finishnum = 0;		/* num of found sentence hypothesis */

  /* ワークエリア */
  /* work area */
  NEXTWORD **nextword, *nwroot;	/* buffer to store predicted words */
  int maxnwnum;			/* current allocated number of words in nextword */
  int nwnum;			/* current number of words in nextword */
  NODE *now, *new;		/* popped current hypo., expanded new hypo. */
#ifdef USE_DFA
  NODE *now_noise, *now_noise_tmp; /* for inserting/deleting noise word */
  boolean now_noise_calced;
  int t;
#endif
  int w,i,j;
  LOGPROB last_score = LOG_ZERO;

  /* copy parameter pointer beforehand for forced alignment */
  if (align_result_word_flag || align_result_phoneme_flag || align_result_state_flag) tparam = param;

  /*
   * 初期化
   * Initialize
   */
  peseqlen = backtrellis->framelen; /* (just for quick access) */
  /* 予測単語格納領域を確保 */
  /* malloc area for word prediction */
  nextword = nw_malloc(&maxnwnum, &nwroot);
  /* 前向きスコア計算用の領域を確保 */
  /* malloc are for forward viterbi (scan_word()) */
  malloc_wordtrellis();		/* scan_word用領域 */
  /* 仮説スタック初期化 */
  /* initialize hypothesis stack */
  start = bottom = NULL;
  stacknum = 0;
  /* 結果格納スタック初期化 */
  /* initialize result stack */
  if (result_reorder_flag) {
    r_start = r_bottom = NULL;
    r_stacknum = 0;
  }
  /* エンベロープ探索用の単語長別展開数カウンタを初期化 */
  /* initialize counters for envelope search */
  if (enveloped_bestfirst_width >= 0) wb_init();
  
  if (verbose_flag) j_printf("samplenum=%d\n", peseqlen);
  if (result_reorder_flag) {
    if (debug2_flag) VERMES("getting %d candidates...\n",ncan);
  }

  /* 
   * 初期仮説(1単語からなる)を得, 文仮説スタックにいれる
   * get a set of initial words from LM function and push them as initial
   * hypotheses
   */
  /* the first words will be stored in nextword[] */
#ifdef USE_NGRAM
  nwnum = ngram_firstwords(nextword, peseqlen, maxnwnum, winfo, backtrellis);
#else  /* USE_DFA */
  nwnum = dfa_firstwords(nextword, peseqlen, maxnwnum, dfa);
  /* 溢れたら、バッファを増やして再チャレンジ */
  /* If the number of nextwords can exceed the buffer size, expand the
     nextword data area */
  while (nwnum < 0) {
    nextword = nw_expand(nextword, &maxnwnum, &nwroot);
    nwnum = dfa_firstwords(nextword, peseqlen, maxnwnum, dfa);
  }
#endif
#ifdef DEBUG
  j_printf("first words=%d\n", nwnum);
#endif
  /* store them to stack */
  for (w = 0; w < nwnum; w++) {
    /* generate new hypothesis */
    new = newnode();
    start_word(new,nextword[w],param,backtrellis);
#ifdef USE_DFA
    if (new->score <= LOG_ZERO) { /* not on trellis */
      free_node(new);
      continue;
    }
#endif
    genectr++;
    /* put to stack */
    if (put_to_stack(new, &start, &bottom, &stacknum, stacksize) != -1) {
      pushctr++;
    }
  }

  /********************/
  /* main search loop */
  /********************/

  for (;;) {

    if (module_mode) {
      /* if terminate signal has been received, cancel this input */
      if (module_wants_terminate()) {
	j_printf("terminated\n");
	break;
      }
    }
    
    /* 
     * 仮説スタックから最もスコアの高い仮説を取り出す
     * pop the top hypothesis from stack
     */
#ifdef DEBUG
    VERMES("get one hypothesis\n");
#endif
    now = get_best_from_stack(&start,&stacknum);
    if (now == NULL) {  /* stack empty ---> 探索終了*/
      j_printf("stack empty\n");
      break;
    }
    /* (bogus score check) */
    if (now->score <= LOG_ZERO) continue;

    /* word envelope チェック */
    /* consult word envelope */
    if (enveloped_bestfirst_width >= 0) {
      if (!wb_ok(now)) {
	/* この仮説長における展開元仮説数の累計数は既に閾値を越えている．
	   そのため，この仮説は捨てる．*/
	/* the number of popped hypotheses at the length already
	   reaches its limit, so the current popped hypothesis should
	   be discarded here with no expansion */
	free_node(now);
	continue;
      }
    }
    
    popctr++;

    /* (for debug) 取り出した仮説とそのスコアを出力 */
    /*             output information of the popped hypothesis to stdout */
    if (debug2_flag) {
      j_printf("--- pop %d:\n", popctr);
      j_printf("  "); put_hypo_woutput(now);
      j_printf("  "); put_hypo_wname(now);
      j_printf("  %d words, f=%f, g=%f\n", now->seqnum, now->score, now->g[now->bestt]);
      j_printf("  last word on trellis: [%d-%d]\n", now->estimated_next_t + 1, now->bestt);
    }
    
    /* 取り出した仮説のスコアを元に score envelope を更新 */
    /* update score envelope using the popped hypothesis */
    envl_update(now, peseqlen);

    /* 
     * 取り出した仮説の受理フラグが既に立っていれば，
     * その仮説は探索終了とみなし，結果として出力して次のループへ．
     *
     * If the popped hypothesis already reached to the end, 
     * we can treat it as a recognition result.
     */
#ifdef DEBUG
    VERMES("endflag check\n");
#endif
    
    if (now->endflag) {
      /* quick, dirty hack */
      if (now->score == last_score) {
	free_node(now);
	continue;
      } else {
	last_score = now->score;
      }
      
      finishnum++;

      if (result_reorder_flag) {
	/* 一定数の仮説が得られたあとスコアでソートするため，
	   一時的に別のスタックに格納しておく */
	/* store the result to result stack
	   after search is finished, they will be re-ordered and output */
	put_to_stack(now, &r_start, &r_bottom, &r_stacknum, r_stacksize);
	/* 指定数の文仮説が得られたなら探索を終了する */
	/* finish search if specified number of results are found */
	if (finishnum >= ncan) {
	  if (debug2_flag) VERMES("%d\n",finishnum);
	  break;
	} else {
	  if (debug2_flag) VERMES("%d..", finishnum);
	  continue;
	}
      } else {			/* result_reorder でない場合は */
	/* 結果は見つかり次第すぐに出力 */
	/* result are directly output as soon as found */
	result_pass2(now, finishnum, winfo);
#ifdef SP_BREAK_CURRENT_FRAME
	/* ショートポーズセグメンテーション時は次のセグメント認識開始時に
	   単語履歴を伝搬させる */
	/* set the last context-aware word for next recognition */
	if (sp_break_last_nword_allow_override && finishnum == 1) sp_segment_set_last_nword(now);
#endif
	/* 必要であれば, 認識結果を用いて入力の音素セグメンテーションを行なう */
	/* do forced alignment with the result if needed */
	if (align_result_word_flag) word_rev_align(now->seq, now->seqnum, tparam);
	if (align_result_phoneme_flag) phoneme_rev_align(now->seq, now->seqnum, tparam);
	if (align_result_state_flag) state_rev_align(now->seq, now->seqnum, tparam);
	/* 仮説を解放して次の仮説へ */
	/* free the hypothesis and continue to next */
	free_node(now);
	/* これで指定数の文仮説が得られたなら, ここで探索を終了する */
	/* finish search if specified number of results are found */
	if (finishnum >= ncan) break;/* end of search */
	else continue;
      }
      
    } /* end of now->endflag */

    
    /* 
     * 探索失敗を検出する．
     * 仮説数が maxhypo 以上展開されたら, もうこれ以上は探索しない
     *
     * detecting search failure:
     * if the number of expanded hypotheses reaches maxhypo, giveup further search
     */
#ifdef DEBUG
    VERMES("loop end check\n");
#endif
    if (popctr >= maxhypo) {
      j_printf("num of hypotheses overflow\n");
      /* (for debug) 探索失敗時に、スタックに残った情報を吐き出す */
      /* (for debug) output all hypothesis remaining in the stack */
      if (debug2_flag) put_all_in_stack(&start, &stacknum);
      free_node(now);
      break;			/* end of search */
    }
    /* 仮説長が一定値を越えたとき，その仮説を破棄する */
    /* check hypothesis word length overflow */
    if (now->seqnum >= MAXSEQNUM) {
      j_printerr("sentence length exceeded ( > %d)\n",MAXSEQNUM);
      free_node(now);
      continue;
    }

    /*
     * 前向きスコアを更新する： 最後の単語の部分の前向きスコアを計算する．
     * update forward score: compute forward trellis for the last word
     */
#ifdef DEBUG
    VERMES("scan_word\n");
#endif
    scan_word(now, param);
    if (now->score < backmax + LOG_ZERO) { /* another end-of-search detecter */
      j_printf("now->score = %f?\n",now->score);
      put_hypo_woutput(now);
      free_node(now);
      break;
    }
    
    /* 
     * 取り出した仮説が文として受理可能であれば，
     * 受理フラグを立ててをスタックにいれ直しておく．
     * (次に取り出されたら解となる)
     *
     * if the current popped hypothesis is acceptable, set endflag
     * and return it to stack: it will become the recognition result
     * when popped again.
     */
#ifdef DEBUG
    VERMES("accept check\n");
#endif
    if (
#ifdef USE_NGRAM
	ngram_acceptable(now, winfo)
#else  /* USE_DFA */
	dfa_acceptable(now, dfa)
#endif
	&& now->estimated_next_t <= 5) {
      new = newnode();
      /* new に now の中身をコピーして，最終的なスコアを計算 */
      /* copy content of 'now' to 'new', and compute the final score */
      last_next_word(now, new, param);
      /* 受理フラグを立てて入れ直す */
      /* set endflag and push again  */
      new->endflag = TRUE;
      put_to_stack(new, &start, &bottom, &stacknum, stacksize);
      
      /* この仮説はここで終わらずに, ここからさらに単語展開する */
      /* continue with the 'now' hypothesis, not terminate here */
    }

    /*
     * この仮説から，次単語集合を決定する．
     * 次単語集合は, この仮説の推定始端フレーム周辺に存在した
     * 第１パスのトレリス単語集合．
     *
     * N-gramの場合は各単語の n-gram 接続確率が含まれる．
     * DFA の場合は, その中でさらに DFA 上で接続可能なもののみが返ってくる
     */
    /*
     * Determine next word set that can connect to this hypothesis.
     * They come from the trellis word that has been survived at near the
     * beginning of the last word.
     *
     * In N-gram mode, they also contain N-gram probabilities toward the
     * source hypothesis.  In DFA mode, the word set is further reduced
     * by the grammatical constraint
     */
#ifdef DEBUG
    VERMES("get next words\n");
#endif
#ifdef USE_NGRAM
    nwnum = ngram_nextwords(now, nextword, maxnwnum, ngram, winfo, backtrellis);
#else  /* USE_DFA */
    nwnum = dfa_nextwords(now, nextword, maxnwnum, dfa);
    /* nextword が溢れたら、バッファを増やして再チャレンジ */
    /* If the number of nextwords can exceed the buffer size, expand the
       nextword data area */
    while (nwnum < 0) {
      nextword = nw_expand(nextword, &maxnwnum, &nwroot);
      nwnum = dfa_nextwords(now, nextword, maxnwnum, dfa);
    }
#endif

    /* 
     * 仮説と次単語集合から新たな文仮説を生成し，スタックにいれる．
     */
    /*
     * generate new hypotheses from 'now' and 'nextword', 
     * and push them to stack
     */
#ifdef DEBUG
    VERMES("generate hypo\n");
#endif

#ifdef USE_DFA
    now_noise_calced = FALSE;	/* TRUE is noise-inserted score has been calculated */
#endif
    i = pushctr;		/* store old value */

    /* for each nextword, generate a new hypothesis */
    for (w = 0; w < nwnum; w++) {
      new = newnode();
#ifdef USE_DFA
      if (nextword[w]->can_insert_sp == TRUE) {
	/* ノイズを挟んだトレリススコアを計算し，挟まない場合との最大値を取る */
	/* compute hypothesis score with noise inserted */
	
	if (now_noise_calced == FALSE) {
	  /* now に sp をつけた仮説 now_noise を作り,そのスコアを計算 */
	  /* generate temporal hypothesis 'now_noise' which has short-pause
	     word after the original 'now' */
	  fornoise.id = dfa->sp_id;
	  now_noise = newnode();
	  cpy_node(now_noise, now);
#if 0
	  now_noise_tmp = newnode();
	  next_word(now, now_noise_tmp, &fornoise, param, backtrellis);
	  scan_word(now_noise_tmp, param);
	  for(t=0;t<peseqlen;t++) {
	    now_noise->g[t] = max(now_noise_tmp->g[t], now->g[t]);
	  }
	  free_node(now_noise_tmp);
#else
	  /* expand NOISE only if it exists in backward trellis */
	  /* begin patch by kashima */
	  if (looktrellis_flag) {
	    if(!dfa_look_around(&fornoise, now, backtrellis)){
	      free_node(now_noise);
	      free_node(new);
	      continue;
	    }
	  }
	  /* end patch by kashima */

	  /* now_nosie の スコア g[] を計算し，元の now の g[] と比較して
	     高い方を採用 */
	  /* compute trellis score g[], and adopt the maximum score
	     for each frame compared with now->g[] */
	  next_word(now, now_noise, &fornoise, param, backtrellis);
	  scan_word(now_noise, param);
	  for(t=0;t<peseqlen;t++) {
	    now_noise->g[t] = max(now_noise->g[t], now->g[t]);
	  }
	  /* ノイズを挟んだ際を考慮したスコアを計算したので，
	     ここで最後のノイズ単語を now_noise から消す */
	  /* now that score has been computed considering pause insertion,
	     we can delete the last noise word from now_noise here */
	  now_noise->seqnum--;
#endif
	  now_noise_calced = TRUE;
	}

	/* expand word only if it exists in backward trellis */
	/* begin patch by kashima */
	if (looktrellis_flag) {
	  if(!dfa_look_around(nextword[w], now_noise, backtrellis)){
	    free_node(new);
	    continue;
	  }
	}
	/* end patch by kashima */
	
	/* 新しい仮説' new' を 'now_noise' から生成 */
	/* generate a new hypothesis 'new' from 'now_noise' */
	next_word(now_noise, new, nextword[w], param, backtrellis);
	
      } else {
	
	/* expand word only if it exists in backward trellis */
	/* begin patch by kashima */
	if (looktrellis_flag) {
	  if(!dfa_look_around(nextword[w], now, backtrellis)){
	    free_node(new);
	    continue;
	  }
	}
	/* end patch by kashima */
	
	/* 新しい仮説' new' を 'now_noise' から生成 */
	/* generate a new hypothesis 'new' from 'now_noise' */
	next_word(now, new, nextword[w], param, backtrellis);
	
      }
#else  /* not USE_DFA */

      /* 新しい仮説' new' を 'now_noise' から生成
	 N-gram の場合はノイズを特別扱いしない */
      /* generate a new hypothesis 'new' from 'now'.
	 pause insertion is treated as same as normal words in N-gram mode. */
      next_word(now, new, nextword[w], param, backtrellis);

#endif /* USE_DFA */
      
      if (new->score <= LOG_ZERO) { /* not on trellis */
	free_node(new);
	continue;
      }
      genectr++;

      /* 生成した仮説 'new' をスタックに入れる */
      /* push the generated hypothesis 'new' to stack */
      if (put_to_stack(new, &start, &bottom, &stacknum, stacksize) != -1) {/* success */
	if (debug2_flag) {
	  j = new->seq[new->seqnum-1];
	  j_printf("  %15s [%15s](id=%5d)(%f) [%d-%d] pushed\n",winfo->wname[j], winfo->woutput[j], j, new->score, new->estimated_next_t + 1, new->bestt);
	}
	pushctr++;
      }
    } /* end of nextword loop */
    
    if (debug2_flag) {
      j_printf("%d pushed\n",pushctr-i);
    }
#ifdef USE_DFA
    if (now_noise_calced == TRUE) free_node(now_noise);
#endif

    /* 
     * 取り出した仮説を捨てる
     * free the source hypothesis
     */
    free_node(now);

    if (catch_intr_flag) break;
    
  }
  /***************/
  /* End of Loop */
  /***************/

  /* output */
  /* 探索失敗で一つも候補が得られなければ，第1パスの結果をそのまま出力する */
  /* If search failed and no candidate obtained in this pass, output
     the result of the previous 1st pass as a final result. */
  if (finishnum == 0) {		/* if search failed */
    if (verbose_flag) {
      j_printf("got no candidates, output 1st pass result as a final result\n",finishnum);
    }
    /* make hypothesis data from the result of previous 1st pass */
    now = newnode();
    for (i=0;i<pass1_wnum;i++) {
      now->seq[i] = pass1_wseq[pass1_wnum-1-i];
    }
    now->seqnum = pass1_wnum;
    now->score = pass1_score;
    /* output it */
    if (module_mode) {
      /* モジュールモードでは，第2パスが失敗したら何も出力しない */
      result_pass2(NULL, 0, winfo);
    } else {
      result_pass2(now, 1, winfo);
    }
#ifdef SP_BREAK_CURRENT_FRAME
    /* segment restart processing */
    if (sp_break_last_nword_allow_override) sp_segment_set_last_nword(now);
#endif
    /* do forced alignment if needed */
    if (align_result_word_flag) word_rev_align(now->seq, now->seqnum, tparam);
    if (align_result_phoneme_flag) phoneme_rev_align(now->seq, now->seqnum, tparam);
    if (align_result_state_flag) state_rev_align(now->seq, now->seqnum, tparam);
    free_node(now);
  } else {			/* if at least 1 candidate found */
    if (debug2_flag) {
      j_printf("got %d candidates\n",finishnum);
    }
    if (result_reorder_flag) {
      /* 結果はまだ出力されていないので，文候補用スタック内をソートして
	 ここで出力する */
      /* As all of the found candidate are in result stack, we sort them
	 and output them here  */
      if (debug2_flag) VERMES("done\n");
      result_reorder_and_output(&r_start, &r_bottom, &r_stacknum, output_hypo_maxnum, winfo);
    }
  }

  /* 各種カウンタを出力 */
  /* output counters */
  if (verbose_flag) {
    j_printf("%d generated, %d pushed, %d nodes popped in %d\n",
	     genectr, pushctr, popctr, backtrellis->framelen);
    j_flushprint();
  }
  
  /* 終了処理 */
  /* finalize */
  nw_free(nextword, nwroot);
  free_all_nodes(start);
  free_wordtrellis();

}
