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

/* backtrellis.c --- keep trellis at 1st pass and provide access to 2nd pass */
/* 第1パスの結果を BACKTRELLIS として保存し第2パスで参照する */

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

#include <julius.h>

/* backtrellis を初期化する(起動時に1回だけ実行) */
/* initialize backtrellis (once on startup) */
void
bt_init(BACKTRELLIS *bt)
{
  bt->num  = NULL;
  bt->rw   = NULL;
  bt->list = NULL;
  bt->root = NULL;
}

/* backtrellis を準備する (認識開始時ごとに実行) */
/* prepare backtrellis (beginning of each speech input) */
void
bt_prepare(BACKTRELLIS *bt)
{
  TRELLIS_ATOM *tre, *tmp;
  int t;

  /* free previous data */
  if (bt->root != NULL) free(bt->root);
  if (bt->rw != NULL) free(bt->rw);
  if (bt->num != NULL) free(bt->num);
  bt->num = NULL;
  bt->rw = NULL;
  bt->root = NULL;

  tre = bt->list;
  while (tre != NULL) {
    tmp = tre->next;
    free(tre);
    tre = tmp;
  }

  /* reset entry point */
  bt->list = NULL;
}  


/* 第1パスで出現した単語終端のトレリス情報 (tatom) をバックトレリスbtに格納 */
/* store trellis atom (= word end) to backtrellis */
/* ここでは格納だけして，第1パス終了後に bt_relocate_rw() でフレーム順に再配置する */
/* this function just store the new atom into list.
   they will be re-located per frame after 1st pass for quicker access in 2nd pass */
void
bt_store(BACKTRELLIS *bt, TRELLIS_ATOM *tatom)
{
#ifdef WORD_GRAPH
  tatom->within_wordgraph = FALSE;
#endif
  tatom->next = bt->list;
  bt->list = tatom;
  /*j_printf("  %3d: %15s %f < %3d %15s\n", tatom->endtime,
	 winfo->woutput[tatom->wid], tatom->backscore,
	 tatom->begintime, winfo->woutput[tatom->last_tre->wid]);*/
}

/* 第1パス終了後, 格納された単語トレリス情報をフレーム順に再配置する */
/* re-locate the stored atom lists per frame (will be called after 1st pass) */
void
bt_relocate_rw(BACKTRELLIS *bt)
{
  TRELLIS_ATOM *tre;
  int t;
  int totalnum, n;

  bt->num = (int *)mymalloc(sizeof(int) * bt->framelen);

  /* count number of trellis atom (= survived word end) for each frame */
  for (t=0;t<bt->framelen;t++) bt->num[t] = 0;
  totalnum = 0;
  for (tre=bt->list;tre;tre=tre->next) {
#ifdef SP_BREAK_CURRENT_FRAME
    /* the last frame (when triggered from sp to non-sp) should be discarded */
    if (tre->endtime >= bt->framelen) continue;
#endif      
    bt->num[tre->endtime]++;
    totalnum++;
  }
  /* if no atom found, return here with all bt->num[t] set to 0 */
  if (totalnum <= 0) return;
  
  /* allocate area */
  bt->root = (TRELLIS_ATOM **)mymalloc(sizeof(TRELLIS_ATOM *) * totalnum);
  bt->rw  = (TRELLIS_ATOM ***)mymalloc(sizeof(TRELLIS_ATOM **) * bt->framelen);
  n = 0;
  for (t=0;t<bt->framelen;t++) {
    if (bt->num[t] > 0) {
      bt->rw[t] = (TRELLIS_ATOM **)&(bt->root[n]);
      n += bt->num[t];
    }
  }
  /* then store the atoms */
  for (t=0;t<bt->framelen;t++) bt->num[t] = 0;
  for (tre=bt->list;tre;tre=tre->next) {
#ifdef SP_BREAK_CURRENT_FRAME
    /* the last frame (when triggered from sp to non-sp) should be discarded */
    if (tre->endtime >= bt->framelen) continue;
#endif      
    t = tre->endtime;
    bt->rw[t][bt->num[t]] = tre;
    bt->num[t]++;
  }
}


/* 以下の関数は bt_relocate_rw 実行後にのみ使用可能となる．*/
/* functions below this line should be called after bt_relocate_rw() */

#ifdef SP_BREAK_CURRENT_FRAME
/* 入力セグメントの両端に残った最尤単語仮説を取り出し, それらを
 第2パスにおける初期/最終仮説としてセットする */
/* extract the best word hypothesis on each end of input segment, and
   store them as start/end word in the 2nd pass */
void
set_terminal_words(BACKTRELLIS *bt)
{
  LOGPROB maxscore;
  int i,t;

  maxscore = LOG_ZERO;
  for(t=bt->framelen-1;t>=0;t--) {
    if (bt->num[t] > 0) break;
  }
  for(i=0;i<bt->num[t];i++) {
    if (maxscore < (bt->rw[t][i])->backscore) {
      maxscore = (bt->rw[t][i])->backscore;
      sp_break_2_begin_word = (bt->rw[t][i])->wid;
    }
  }
  maxscore = LOG_ZERO;
  for(t=0;t<bt->framelen;t++) {
    if (bt->num[t] > 0) break;
  }
  for(i=0;i<bt->num[t];i++) {
    if (maxscore < (bt->rw[t][i])->backscore) {
      maxscore = (bt->rw[t][i])->backscore;
      sp_break_2_end_word = (bt->rw[t][i])->wid;
    }
  }
#ifdef SP_BREAK_DEBUG
  printf("2nd pass begin word: %s\n",
	 (sp_break_2_begin_word == WORD_INVALID) ? "WORD_INVALID" : winfo->wname[sp_break_2_begin_word]);
  printf("2nd pass end word: %s\n",
	 (sp_break_2_end_word == WORD_INVALID) ? "WORD_INVALID" : winfo->wname[sp_break_2_end_word]);
#endif
}
#endif /* SP_BREAK_CURRENT_FRAME */

/* bt_relocate_rw 終了後, 単語IDでソートしておく */
/* sort trellis atoms on each frame by word ID */
static int
compare_wid(TRELLIS_ATOM **a, TRELLIS_ATOM **b)
{
  if ((*a)->wid > (*b)->wid) return 1;
  if ((*a)->wid < (*b)->wid) return -1;
  return 0;
}
void
bt_sort_rw(BACKTRELLIS *bt)
{
  int t;

  for (t=0;t<bt->framelen;t++) {
    qsort(bt->rw[t], bt->num[t], sizeof(TRELLIS_ATOM *),
	  (int (*)(const void *,const void *))compare_wid);
  }
}


/* 以下の関数は事前にbt_sort_rw() が呼ばれていること(第2パス用) */
/* functions below should be called after bt_sort_rw() */

/* 時刻 t 上の単語終端トレリス上で単語 wkey を検索して結果を返す */
/* あればそのトレリス単語情報へのポインタ, なければ NULL を返す */
/* search trellis atom (= word end) with word ID 'wkey' on frame 't' */
/* return pointer to the found trellis atom, and NULL on failure */
TRELLIS_ATOM *
bt_binsearch_atom(BACKTRELLIS *bt, int t, WORD_ID wkey)
{
  /* do binary search */
  /* assume rw are ordered by wid */
  int left, right, mid;
  TRELLIS_ATOM *tmp;
#ifdef WPAIR
  int i;
  LOGPROB maxscore;
  TRELLIS_ATOM *maxtre;
#endif
  
  if (bt->num[t] == 0) return(NULL);
  
  left = 0;
  right = bt->num[t] - 1;
  while (left < right) {
    mid = (left + right) / 2;
    if ((bt->rw[t][mid])->wid < wkey) {
      left = mid + 1;
    } else {
      right = mid;
    }
  }
  tmp = bt->rw[t][left];
  if (tmp->wid == wkey) {
#ifdef WPAIR
    /* same word with different context will be found:
       most likely one will be returned */
    maxscore = LOG_ZERO;
    i = left;
    while (i >= 0 && (tmp = bt->rw[t][i])->wid == wkey) {
      if (maxscore < tmp->backscore) {
	maxscore = tmp->backscore;
	maxtre = tmp;
      }
      i--;
    }
    i = left;
    while (i < bt->num[t] && (tmp = bt->rw[t][i])->wid == wkey) {
      if (maxscore < tmp->backscore) {
	maxscore = tmp->backscore;
	maxtre = tmp;
      }
      i++;
    }

    tmp = maxtre;
#endif /* WORD_GRAPH */

    return(tmp);
  } else {
    return(NULL);
  }
}
