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

/* dfa-decode.c --- for Julian: predict next word using DFA grammar for 2nd pass */

/* $Id: dfa_decode.c,v 1.6 2002/09/11 22:02:33 ri Exp $ */

/*
  スタックデコーディング(第2パス)における文法(DFA)に基づく次単語予測:
  与えられた仮説に対して，DFA 文法上接続可能な次単語の集合を決定する．
  ただし実際には, 展開元仮説の予測される始端フレーム周辺の単語トレリス
  上に残っている単語のみが展開される(dfa_look_around())．

  Next word prediction by grammar rule (DFA) in 2nd pass stack decoding:
  determine a set of words allowed to connect from grammar.
  Actually, only ones that exist in word trellis around the estimated
  word end frame will be expanded (function dfa_lookaround()).
*/

/*
  ショートポーズのスキップ処理:

  Julian ではショートポーズの出現可能位置は
  文法で指定する．実際の発話ではポーズが入らない場合があるので，それを
  考慮して次単語予測する必要がある．
  
  次単語集合にショートポーズがある場合, さらにその次の単語集合まで
  次単語集合に含める("NEXTWORD->can_insert_sp = TRUE)．実際にそこに
  ショートポーズが挿入されるかどうかは search_bestfirst_main.c で
  スコアを見て判断される．

  Short pause skipping:

  in Julian, the location where a short pause is
  possible to be inserted should be explicitly defined in grammar, just
  like other words.  This short pause word should be especially handled
  in this next word prediction, as it can be "skipped" in case user did
  not insert break.

  When a short pause word is possible to connect as the next word, the
  additional word set next to the short pause word is also included in the
  prediction set with "NEXTWORD->can_insert_sp = TRUE".  Whether short pause
  was actually inserted or not in the user input will be determined
  by score in search_bestfirst_main.c.
 */

/* Julian モードでは dfa_firstwords(), dfa_nextwords(), dfa_acceptable(),
   dfa_eosscore() が第2パスのメイン関数 wchmm_fbs() に渡され, 使用される．
   (Julius モードでは "ngram_decode.c" 内の ngram_*() が使われる */

/* In julian mode, pointers to functions dfa_firstwords(), dfa_nextwords(),
   dfa_acceptable() and dfa_eosscore() are passed to main search function
   wchmm_fbs().  (for julius, corresponding functions ngram_*() in
   "ngram_decode.c" will be passed instead) */

#include <julius.h>
#ifdef USE_DFA

/* 最初の予測単語群を返す．返り値: 単語数 (-1 on error) */
/* return initial word set.  return value: num of words (-1 on error) */
int
dfa_firstwords(NEXTWORD **nw,	/* next word set (return value) */
				/* (assume already malloced) */
	       int peseqlen,	/* end time of input */
	       int maxnw,	/* maximum length of nw[] */
	       DFA_INFO *dfa)	/* DFA info */
{
  DFA_ARC *arc;
  MULTIGRAM *m;
  int s, sb, se;
  int cate, iw, ns;
  int num = 0;

  for (m = gramlist; m; m = m->next) {
    if (m->active) {
      sb = m->state_begin;
      se = sb + m->dfa->state_num;
      for(s=sb;s<se;s++) {
	if ((dfa->st[s].status & INITIAL_S) != 0) { /* from initial state */
	  for (arc = dfa->st[s].arc; arc; arc = arc->next) {	/* for all arc */
	    cate = arc->label;	/* category ID */
	    ns = arc->to_state;	/* next DFA state ID */
	    /* all words within the category is expanded */
	    for (iw=0;iw<dfa->term.wnum[cate];iw++) {
	      nw[num]->id = dfa->term.tw[cate][iw]; /* word ID */
	      nw[num]->next_state = ns; /* next state */
	      nw[num]->can_insert_sp = FALSE; /* short pause should not inserted before this word */
	      num++;
	      if (num >= maxnw) return -1; /* buffer overflow */
	    }
	  }
	}
      }
    }
  }

  return num;
}

/* 文法に従って部文仮説の次の予測単語群を返す．帰り値: 単語数 (-1 on error) */
/* return next word set determined by DFA grammar.  return value:
   num of words (-1 on error) */
int
dfa_nextwords(NODE *hypo, NEXTWORD **nw, int maxnw, DFA_INFO *dfa) /* hypo: source hypothesis */
{
  DFA_ARC *arc, *arc2;
  int iw,cate,ns,cate2,ns2;
  int num = 0;

  /* hypo->state: current DFA state ID */
  for (arc = dfa->st[hypo->state].arc; arc; arc = arc->next) {/* for all arc */
    cate = arc->label;
    ns = arc->to_state;
    if (dfa->is_sp[cate]) {	/* short pause */
      /* expand one more next (not expand the short pause word itself) */
      for (arc2 = dfa->st[ns].arc; arc2; arc2 = arc2->next) {
	cate2 = arc2->label;
	ns2 = arc2->to_state;
	for (iw=0;iw<dfa->term.wnum[cate2];iw++) {
	  nw[num]->id = dfa->term.tw[cate2][iw];
	  nw[num]->next_state = ns2;
	  nw[num]->can_insert_sp = TRUE;
	  num++;
	  if (num >= maxnw) return -1; /* buffer overflow */
	}
      }
    } else {			/* not short pause */
      /* all words within the category is expanded */
      for (iw=0;iw<dfa->term.wnum[cate];iw++) {
	nw[num]->id = dfa->term.tw[cate][iw];
	nw[num]->next_state = ns;
	nw[num]->can_insert_sp = FALSE;
	num++;
	if (num >= maxnw) return -1; /* buffer overflow */
      }
    }
  }
  return num;
}

/* 仮説が文法上受理状態にあるかどうかを返す */
/* return if the hypothesis is in "acceptable" state */
/* NOISE: ここにはこない仕様 */
boolean
dfa_acceptable(NODE *hypo, DFA_INFO *dfa)
{
  if (dfa->st[hypo->state].status & ACCEPT_S) {
    return TRUE;
  } else {
    return FALSE;
  }
}

/* patch by kashima */
/* 次単語候補が推定された接続予測点付近のトレリス上にあるかどうかチェックする
   ある場合, nword->tre を設定(最尤の接続点はあとで決まるので最適でなくてよい)
   返り値: あれば TRUE, なければ FALSE (=仮説展開しない) */
/* Check if the given nextword 'nword' exists in the word trellis around the
   estimated connection time.  If exist, set 'nword->tre' to the corresponding
   trellis word (as best connection time will be determined later, it need not
   to be an optimal one).
   Return value: TRUE if exist,  FALSE if not exist (= not expand this word) */
boolean
dfa_look_around(NEXTWORD *nword, NODE *hypo, BACKTRELLIS *bt)
{
  int t,tm;
  int i;
  WORD_ID w;
  
  tm = hypo->estimated_next_t;	/* estimated connection time */

  /* look aound [tm-lookup_range..tm+lookup_range] frame */
  /* near the center is better:
     1. the first half (backward)   2. the second half (forward) */
  /* 1. backward */
  for(t = tm; t >= tm - lookup_range; t--) {
    if (t < 0) break;
     for (i=0;i<bt->num[t];i++) {
       w = (bt->rw[t][i])->wid;
       if(w == nword->id){	/* found */
         nword->tre = bt->rw[t][i];
         return TRUE;
       }
     }
  }
  /* 2. forward */
  for(t = tm + 1; t < tm + lookup_range; t++) {
    if (t > bt->framelen - 1) break;
    if (t >= hypo->bestt) break;
    for (i=0;i<bt->num[t];i++) {
      w = (bt->rw[t][i])->wid;
      if(w == nword->id){	/* found */
        nword->tre = bt->rw[t][i];
        return TRUE;
      }
    }
  }

  return FALSE;			/* not found */
}

#endif /* USE_DFA */
