// file: bw_pdf_1.cc
//

// isip include files
//
#include "bw_train.h"
#include "bw_train_constants.h"

// method: update_pdf_cc
//
// arguments:
//  State** states : (output) the updated states
//  float_8*** means : (input) the accumulated means
//  float_8*** covars : (input) the accumulated covariance
//  float_8** mix_weights : (input) the accumulated weights
//  float_8* states_occ : (input) the accumulated state occupancy
//  int_4 num_states : (input) the number of total states
//  int_4 num_features : (input) the number of features;
//  float_4* var_floor : (input) variance floor
//  Model** models : (input) the models
//  int_4* model_access_counts : (input) the number of times each model was
//                               accessed
//  int_4 num_models : (input) the number of models
//  int_4 min_count : (input) the minimum number of times a model must occur
//                    for its states to be updated
//
// return a logical flag to indicate success
//

// this method updates the means and covariances of all the
// states in all the models if necessary
//
logical_1 update_pdf_cc(Train_State** states_a, float_8*** means_a,
			float_8*** covars_a, float_8** mix_weights_a,
			float_8* states_occ_a, int_4 num_states_a,
			int_4 num_features_a, float_4* var_floor_a,
			Train_Model** models_a, int_4* model_access_counts_a,
			int_4 num_models_a, int_4 min_count_a) {
  
  // local variables
  //
  int_4 num_mix = (int_4)0;
  float_4 tmp_covar = (float_4)0.0;
  float_4* temp_scale = (float_4*)NULL;
  float_4* old_weights = (float_4*)NULL;
  float_4* old_scale = (float_4*)NULL;
  float_4** old_mean = (float_4**)NULL;
  float_4** old_covar = (float_4**)NULL;
  
  logical_1* update_state = new logical_1[num_states_a];
  for (int_4 i = 0; i < num_states_a; i++) {
    update_state[i] = ISIP_FALSE;
  }
  
  // loop over each model's states and see if they need to be updated
  //
  for (int_4 mod_num = 0; mod_num < num_models_a; mod_num++) {

    // check if a model has been accessed required number
    // of times for it to get updated
    //
    if (model_access_counts_a[mod_num] >= min_count_a) {
      
      Train_Model* this_model = models_a[mod_num];
      
      // loop over the number of states in the model
      //
      Train_State** st_list = this_model->get_states_cc();
      for (int_4 st_num = 1; st_num < this_model->get_num_states_cc() - 1; 
	   st_num++) {
	
	// find this state in the state list
	//
	Train_State* this_state = st_list[st_num];
	
	int_4 i = 0;
	for (i = 0; i < num_states_a; i++) {
	  if (states_a[i] == this_state) {
	    break;
	  }
	}
	
	if ((i >= 0)  && (i < num_states_a)) {
	  update_state[i] = ISIP_TRUE;
	}
      }
    }
  }
  
  // loop over all the states
  //
  for (int_4 i = 0; i < num_states_a; i++) {
    
    // see if we need to update
    //
    if (update_state[i] == ISIP_TRUE) {
      
      // get the number of mixtures 
      //
      num_mix = states_a[i]->get_num_mixtures_cc();
      
      // check the state
      //
      if (num_mix > (int_4)0) {
	
	// get the old value
	//
	old_weights = states_a[i]->get_weights_cc();
	old_scale = states_a[i]->get_scale_cc();
	old_mean = states_a[i]->get_mean_cc();
	old_covar = states_a[i]->get_covar_cc();
	
	// allocate memory to scale arraies
	//
	temp_scale = new float_4[num_mix];
	
	// loop over the mixture components
	//
	for (int_4 m = 0; m < num_mix; m++) {
	  
	  // check if update is valid
	  //
	  temp_scale[m] = (float_4)0.0;
	  if (mix_weights_a[i][m] != (float_8)0.0) {
	    
	    // update current mixture component
	    //
	    for (int_4 feat = 0; feat < num_features_a; feat++) {
	      
	      means_a[i][m][feat] /= mix_weights_a[i][m];
	      old_mean[m][feat] = means_a[i][m][feat];
	      tmp_covar = covars_a[i][m][feat] / mix_weights_a[i][m];
	      tmp_covar = tmp_covar - means_a[i][m][feat] *
		means_a[i][m][feat];
	      
	      if (tmp_covar < var_floor_a[feat]) {
		covars_a[i][m][feat] = 1.0 / var_floor_a[feat];
	      }
	      else {
		covars_a[i][m][feat] = 1.0 / tmp_covar;
	      }

	      old_covar[m][feat] = covars_a[i][m][feat];
	      
	      temp_scale[m] += log(((float_8)1.0/
				    (float_8)covars_a[i][m][feat]) *
				   ISIP_TWOPI);
	    }
	    mix_weights_a[i][m] = log((float_8)mix_weights_a[i][m]/
				      (float_8)states_occ_a[i]);
	    old_weights[m] = mix_weights_a[i][m];
	    old_scale[m] = temp_scale[m];
	  }
	} // end of looping over mixtures
      }
      
      // clean up
      //
      delete [] temp_scale;
      temp_scale = (float_4*)NULL;
    } // end of a valid state updating
  }

  // clean up
  //
  delete [] update_state;
  
  // exit gracefully
  //
  return(ISIP_TRUE);
}
