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

/* mkwhmm.c --- make word(phrase) HMM from HTK_HMM_INFO */

/* $Id: mkwhmm.c,v 1.3 2002/09/11 22:01:50 ri Exp $ */

/* initial & accept arc will be stripped */
/* trans prob to accept state will be stored in accept_ac_a */

#include <sent/stddefs.h>
#include <sent/hmm.h>

/* calculate state length length */
static int
totalstatelen(HMM_Logical **hdseq, int hdseqlen)
{
  int i, len;

  len = 0;
  for (i=0;i<hdseqlen;i++) {
    len += hmm_logical_state_num(hdseq[i]) - 2;
  }
  return(len);
}

/* add arc with prob a to state */
static void
add_arc(HMM_STATE *state, int arc, LOGPROB a)
{
  A_CELL *atmp;

  atmp = (A_CELL *)mymalloc(sizeof(A_CELL));
  atmp->a = a;
  atmp->arc = arc;
  atmp->next = state->ac;
  state->ac = atmp;
}

/* make word(phrase) HMM from HTK_HMM_INFO */
/* LM prob will be assigned for cross-word arcs */
/* new HMM is malloced and returned */
HMM *
new_make_word_hmm_with_lm(HTK_HMM_INFO *hmminfo, HMM_Logical **hdseq, int hdseqlen, LOGPROB *lscore)
{
  HMM *new;
  int i,j,n;
  int afrom, ato;
  LOGPROB logprob;
  HTK_HMM_Data *dt;
  HTK_HMM_Trans *tr;
  int state_num;

  new = (HMM *)mymalloc(sizeof(HMM));
  new->len = totalstatelen(hdseq, hdseqlen);
  new->state = (HMM_STATE *)mymalloc(sizeof(HMM_STATE) * new->len);
  for (i=0;i<new->len;i++) {
    new->state[i].ac = NULL;
    new->state[i].is_pseudo_state = FALSE;
    new->state[i].out.state = NULL;
    new->state[i].out.cdset = NULL;
  }

  /* assign state outprob info  */
  n = 0;
  for (i = 0; i < hdseqlen; i++) {
    if (hdseq[i]->is_pseudo) {
      for (j = 1; j < hdseq[i]->body.pseudo->state_num - 1; j++) {
	new->state[n].is_pseudo_state = TRUE;
	new->state[n].out.cdset = &(hdseq[i]->body.pseudo->stateset[j]);
	n++;
      }
    } else {
      for (j = 1; j < hdseq[i]->body.defined->state_num - 1; j++) {
	new->state[n].is_pseudo_state = FALSE;
	new->state[n].out.state = hdseq[i]->body.defined->s[j];
	n++;
      }
    }
  }
  
  /* make transition arcs */
  /* initial state check */
/* 
 *   for (i=0;i<hdseq[0]->def->state_num;i++) {
 *     if (i != 1 && (hdseq[0]->def->tr->a[0][i]) != LOG_ZERO) {
 *	 j_printerr("initial state contains more than 1 arc.\n");
 *     }
 *   }
 */
  new->accept_ac_a = LOG_ZERO;
  n = 0;
  for (i = 0; i < hdseqlen; i++) {
    state_num = hmm_logical_state_num(hdseq[i]);
    tr = hmm_logical_trans(hdseq[i]);
    for (afrom = 1; afrom < state_num - 1; afrom++) {
      for (ato = 1; ato < state_num; ato++) {
	logprob = tr->a[afrom][ato];
	if (logprob != LOG_ZERO) {
	  if (ato == state_num - 1 && lscore != NULL){
	    logprob += lscore[i];
	  }
	  if (n + (ato - afrom) >= new->len) { /* arc to accept node */
	    if (new->accept_ac_a != LOG_ZERO) {
	      j_error("more than 1 arc to accept node found.\n");
	    } else {
	      new->accept_ac_a = logprob;
	    }
	  } else {
	    add_arc(&(new->state[n]), n + (ato - afrom), logprob);
	  }
	}
      }
      n++;
    }
  }

  return (new);
}

/* make word(phrase) HMM from HTK_HMM_INFO with no LM */
HMM *
new_make_word_hmm(HTK_HMM_INFO *hmminfo, HMM_Logical **hdseq, int hdseqlen)
{
  return(new_make_word_hmm_with_lm(hmminfo, hdseq, hdseqlen, NULL));
}

/* free HMM */
void
free_hmm(HMM *d)
{
  A_CELL *ac, *atmp;
  int i;

  for (i=0;i<d->len;i++) {
    ac = d->state[i].ac;
    while (ac) {
      atmp = ac->next;
      free(ac);
      ac = atmp;
    }
  }
  free(d->state);
  free(d);
}

