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

/* calc_tied_mix.c --- calculate outprob of tied mixture */
/*                     codebook-level cache is enabled */

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

#include <sent/stddefs.h>
#include <sent/htk_hmm.h>
#include <sent/htk_param.h>
#include <sent/hmm.h>
#include <sent/gprune.h>
#include "globalvars.h"

/* control */
static int allocframenum;

/* book cache */
static MIXCACHE ***mixture_cache = NULL;/* [time][bookid][#] */
static MIXCACHE **tcache;		/* [bookid][#] of current time */
static MIXCACHE *ttcache;		/* [#] of current time and book */
static MIXCACHE **last_tcache;
static MIXCACHE *last_ttcache;		/* [#] of current time and book */
static int *last_id;

/* results */
static LOGPROB **fallback_score = NULL; /* [t][gssetid], LOG_ZERO if selected*/

/* initialize cache area (should be called once on startup) */
boolean
calc_tied_mix_init()
{
  mixture_cache = NULL;
  allocframenum = -1;
  last_id = (int *)mybmalloc(sizeof(int) * OP_hmminfo->maxmixturenum);
  return TRUE;
}

/* prepare cache area (should be called for each sample) */
boolean
calc_tied_mix_prepare(int framenum)
{
  int bid, t, size;

  /* (re)-allocate */
  if (allocframenum < framenum) {
    if (mixture_cache != NULL) {
      for(t=0;t<allocframenum;t++) {
	free(mixture_cache[t][0]);
	free(mixture_cache[t]);
      }
      free(mixture_cache);
    }
    size = OP_gprune_num * OP_hmminfo->codebooknum;
  
    mixture_cache = (MIXCACHE ***)mymalloc(sizeof(MIXCACHE **) * framenum);
    for(t=0;t<framenum;t++) {
      mixture_cache[t] = (MIXCACHE **)mymalloc(sizeof(MIXCACHE *) * OP_hmminfo->codebooknum);
      mixture_cache[t][0] = (MIXCACHE *)mymalloc(sizeof(MIXCACHE) * size);
      for(bid=1;bid<OP_hmminfo->codebooknum;bid++) {
	mixture_cache[t][bid] = &(mixture_cache[t][0][OP_gprune_num * bid]);
      }
    }
    allocframenum = framenum;
  }
  /* clear */
  for(t=0;t<framenum;t++) {
    for(bid=0;bid<OP_hmminfo->codebooknum;bid++) {
      mixture_cache[t][bid][0].score = LOG_ZERO;
    }
  }

  return TRUE;
}



LOGPROB
calc_tied_mix()
{
  GCODEBOOK *book = (GCODEBOOK *)(OP_state->b);
  LOGPROB logprob;
  int i;

  if (OP_last_time != OP_time) { /* different frame */
    tcache = mixture_cache[OP_time];
    if (OP_time >= 1) {
      last_tcache = mixture_cache[OP_time-1];
    } else {
      last_tcache = NULL;
    }
  }
  ttcache = tcache[book->id];
  if (tcache[book->id][0].score != LOG_ZERO) { /* already calced */
    /* calculate using cache and weight */
    for (i=0;i<OP_calced_num;i++) {
      OP_calced_score[i] = ttcache[i].score + OP_state->bweight[ttcache[i].id];
    }
  } else { /* not calced yet */
    /* compute Gaussian set */
    if (OP_time >= 1) {
      last_ttcache = last_tcache[book->id];
      if (last_ttcache[0].score != LOG_ZERO) {
	for(i=0;i<OP_gprune_num;i++) last_id[i] = last_ttcache[i].id;
	/* tell last calced best */
	compute_gaussset(book->d, book->num, last_id);
      } else {
	compute_gaussset(book->d, book->num, NULL);
      }
    } else {
      compute_gaussset(book->d, book->num, NULL);
    }
    /* computed Gaussians will be set in:
       score ... OP_calced_score[0..OP_calced_num]
       id    ... OP_calced_id[0..OP_calced_num] */
    /* OP_gprune_num = required, OP_calced_num = actually calced */
    /* store to cache */
    for (i=0;i<OP_calced_num;i++) {
      ttcache[i].id = OP_calced_id[i];
      ttcache[i].score = OP_calced_score[i];
      /* now OP_calced_{id|score} can be used for work area */
      OP_calced_score[i] += OP_state->bweight[OP_calced_id[i]];
    }
  }
  logprob = addlog_array(OP_calced_score, OP_calced_num);
  if (logprob <= LOG_ZERO) return LOG_ZERO;
  return (logprob / LOGTEN);
}  
