// file: $isip/class/pr/RegressionDecisionTree/rdt_05.cc
// version: $Id: rdt_05.cc 9920 2004-12-23 21:07:46Z gao $
//

// isip include files
//
#include "RegressionDecisionTree.h"

// method: initRegressionTree
//
// arguments:
//  Vector<StatisticalModel>& stat_models: (input) vector of statistical model
//  Vector <Boolean>& speech_tag: (input) speech tag
//
// return: a boolean value indicating status
//
// this method initilaize the regression tree root node using the
// statistical models and speech tag
//
boolean RegressionDecisionTree::initRegressionTree(Vector<StatisticalModel>& stat_models_a, Vector <Boolean>& speech_tag_a) {

  // keep a copy of the model in the local data field
  //
  stat_models_d.assign(stat_models_a);

  // get the number of features
  //
  long num_feat = 0;

  // if the model to adapt is Gaussian model, get feature dimension
  //
  if (stat_models_a(0).getType()
      == StatisticalModel::GAUSSIAN_MODEL) {
    
    VectorFloat mean;
    stat_models_a(0).getGaussianModel().getMean(mean);
    num_feat = mean.length();
  }

  // if the model to adapt is mixture model, get feature dimension of
  // the first mixture component
  //
  else if (stat_models_a(0).getType()
	   == StatisticalModel::MIXTURE_MODEL) {
    
    VectorFloat mean;
    StatisticalModel* sm = stat_models_a(0).getMixtureModel()
      .getModels().getFirst();
    sm->getMean(mean);
    num_feat = mean.length();
  }
  else {
    return Error::handle(name(), L"initRegressionTree",
			 ERR_ADAPT_NO_GAUSSIAN,
			 __FILE__, __LINE__);
  }

  // minimal components will be three times the number of feature
  // 
  components_threshold_d = 3 * num_feat;
  
  SingleLinkedList<RDataPoint> models_root;  
  SingleLinkedList<RDataPoint> models_speech;
  SingleLinkedList<RDataPoint> models_nonspeech;

  VectorLong stat_mixture;

  long stat_length = stat_models_a.length();
  stat_mixture.setLength(stat_length);
  mixture_offset_d.setLength(stat_length);  
  
  // loop over all statistical models
  //
  for (int i = 0; i < stat_models_a.length(); i++) {

    // if the model to adapt is a single Gaussian model, add to data
    // strucutre directly 
    //
    if (stat_models_a(i).getType() == StatisticalModel::GAUSSIAN_MODEL) {

      RDataPoint temp_point;
      long w_index = 0;

      temp_point.assign(i, 1, w_index);

      // add to the node
      //
      if (speech_tag_a(i))
	models_speech.insert(&temp_point);
      else
	models_nonspeech.insert(&temp_point);

      models_root.insert(&temp_point);
      
      stat_mixture(i) = 1;
    }
    
    // if the model to adapt is a mixture of Gaussian models, add each
    // mixture component to the data structure
    //
    else if (stat_models_a(i).getType() == StatisticalModel::MIXTURE_MODEL) {
      
      // get the linked list of mixture components
      //
      SingleLinkedList<StatisticalModel>& models =
	const_cast<StatisticalModel&>(stat_models_a(i)).getMixtureModel().
	getModels();
      
      boolean more = models.gotoFirst();
      
      // Gaussian Model index
      //
      long w_index = 0;
      
      while (more) {
	
	// get the current mixture component
	//
	StatisticalModel* model = models.getCurr();
	
	// check whether it is Gaussian
	//
	if (model->getType() == StatisticalModel::GAUSSIAN_MODEL) {
		  
	  RDataPoint temp_point;

	  temp_point.assign(i, w_index, 0);

	  if (speech_tag_a(i))
	    models_speech.insert(&temp_point);
	  else
	    models_nonspeech.insert(&temp_point);

	  models_root.insert(&temp_point);
	  
	  w_index++;
	}
	else {
	  return Error::handle(name(), L"initRegressionTree",
			       RegressionDecisionTree::ERR_ADAPT_NO_GAUSSIAN,
			       __FILE__, __LINE__);
	}
	
	// move to the next mixture component
	//
	more = models.gotoNext();
	
      } // end of loop over all mixtures

      stat_mixture(i) = w_index;
      
    } // end of else if mixture model
    
    // it is neither single Gaussian, nor mixture model
    //
    else {
      return Error::handle(name(), L"initRegressionTree",
			   RegressionDecisionTree::ERR_ADAPT_NO_GAUSSIAN,
			   __FILE__, __LINE__);

    } // end of else unsupported model

  } // end of loop over all statistical models


  mixture_offset_d(0) = 0;
  
  for (long j = 1; j < stat_length; j++) {
    mixture_offset_d(j) = mixture_offset_d(j - 1) + stat_mixture(j - 1);
  }  

  long mixture_total = mixture_offset_d(stat_length - 1) +
    stat_mixture(stat_length - 1);
  map_stat_to_trans_d.setLength(mixture_total);
    
  // construct the root node and insert it in the graph
  //
  BiGraphVertex<RegressionDecisionTreeNode>* rootnode
    = insertVertex(&rdt_rootnode_d);
  
  // connect the start node to the root node
  //
  insertArc(getStart(), rootnode, false, 0);

  // local variables
  //
  RegressionDecisionTreeNode* rdt_node = (RegressionDecisionTreeNode*)NULL;
  
  // get the data on this node
  //
  rdt_node = rootnode->getItem();

  rdt_node->setComponents(models_root);
  rdt_node->setNodeIndex(0);
  rdt_node->setParentNodeIndex(0);
  
  if (debug_level_d >= Integral::ALL) { 
    Long x_reg = stat_models_a.length();
    x_reg.debug(L"x_reg");
    Long(models_speech.length()).debug(L"models_speech");
    Long(models_nonspeech.length()).debug(L"models_nonspeech");  
  }
  
  // local variables
  //
  RegressionDecisionTreeNode child_rdt_node1;
  RegressionDecisionTreeNode child_rdt_node2;  

  child_rdt_node1.setComponents(models_speech);
  child_rdt_node1.setNodeIndex(1);
  child_rdt_node1.setParentNodeIndex(0);
  child_rdt_node1.setSpeechFlag(true);

  BiGraphVertex<RegressionDecisionTreeNode>* left_node
    = insertVertex(&child_rdt_node1);

  insertArc(rootnode, left_node, true);
  
  if (speech_tag_a.length() > 0) {
    child_rdt_node2.setComponents(models_nonspeech);
    child_rdt_node2.setNodeIndex(2);
    child_rdt_node2.setParentNodeIndex(0);
    child_rdt_node2.setSpeechFlag(false);
    
    BiGraphVertex<RegressionDecisionTreeNode>* right_node
      = insertVertex(&child_rdt_node2);  

    insertArc(rootnode, right_node, true);

    speech_flag_d = true;
  }

  // build the regression cluster
  //
  buildRegressionCluster(rootnode);

  RTreeNode* root_node = (RTreeNode*)NULL;
  SingleLinkedList<RTreeNode> leaf_nodes(DstrBase::USER);
    
  root_node = getFirst();
    
  leaf_nodes.clear();
  
  getAllLeafNodes(*root_node, leaf_nodes);

  if (debug_level_d >= Integral::ALL) {
    for (boolean more = leaf_nodes.gotoFirst(); more;
	 more = leaf_nodes.gotoNext()) {
      leaf_nodes.getCurr()->getItem()->getNodeIndex().debug(L"leaf index2");
    }
  }
  
  // exit gracefully
  //
  return true;
}


// method: findBestTerminal
//
// arguments:
//  TreeNode*& node: (input) input node
//  TreeNode*& best: (input) best node to split
//  float& score: (input/output) the best score upto now
//
// return: a TreeNode point for the best node
//
// this method return the best node to split when the input root node
// is given by using the best score
//
BiGraphVertex <RegressionDecisionTreeNode>* RegressionDecisionTree::findBestTerminal(RTreeNode*& node_a, RTreeNode*& best_a, float& score_a) {
  
  // local variables
  //
  float score = (float)0;
  DoubleLinkedList<BiGraphArc<RegressionDecisionTreeNode> >* children;
  BiGraphVertex<RegressionDecisionTreeNode>* child_node;
  
  // check the node
  //
  if (node_a != (RTreeNode*)NULL) {
    // get all the child nodes of this node
    //
    children = node_a->getChildren();

    // if this node has child nodes, accumulate the child nodes
    //
    if (node_a->gotoFirstChild()) {
      
      // loop for the left child
      //
      children->gotoFirst();
      child_node = children->getCurr()->getVertex();
      
      best_a = findBestTerminal(child_node, best_a, score_a);
    }

    else {

      // local variables
      //
      RegressionDecisionTreeNode* rdt_node = (RegressionDecisionTreeNode*)NULL;
      
      // get the data on this node
      //
      rdt_node = node_a->getItem();
      
      rdt_node->updateDistribution(stat_models_d);
      
      score = rdt_node->getClusterScore();

      long num_component = rdt_node->getNumComponents();

      // if the node's score is greater, record the score and set the
      // best node to this node
      // 
      if (score_a < score && num_component > components_threshold_d) {
	score_a = score;
	best_a = node_a;
      }
      return best_a;
    }

    if( children->gotoNext()) {
      // loop for the right child
      //
      child_node = children->getCurr()->getVertex();
      
      best_a = findBestTerminal(child_node, best_a, score_a);
    }
  }

  // return the best node
  //
  return best_a;
}


// method: buildRegressionClusters
//
// arguments:
//  (RTreeNode*& root_node: (input) root node
//
// return: a boolean value indicating status
//
// this method creates (train) the decision-tree on the basis of
// algorithm and implementation and the root node
//
boolean RegressionDecisionTree::buildRegressionCluster(RTreeNode*& root_node_a) {
  
  long k, index;
  float score;
  long components;
  
  VectorFloat average_mean;
  VectorFloat left_average_mean, right_average_mean;
  MatrixFloat average_covariance;   

  // local variables
  //
  RegressionDecisionTreeNode* left_child_node;
  RegressionDecisionTreeNode* right_child_node;

  left_child_node = new RegressionDecisionTreeNode();
  right_child_node = new RegressionDecisionTreeNode();
  
  RTreeNode* best_node; 

  // the root includes all the data, if first split the non-speech and
  // speech data. This will create 3 nodes, index will be 0, 1, 2
  // 
  // So the next split index starts from 3.
  //
  // if no split, the index start from 1;

  if (speech_flag_d)   // exculde non speech symbol
    index = 3;
  else
    index = 1;    // include non speech symbol

  k = index / 2;
  
  for (; k < num_terminals_d; k++) {

    score = 0.0;
    components = 0;
    
    best_node = root_node_a;
    
    // find the best (worst scoring) terminal to split
    // based on the score and number of the components
    //
    best_node = findBestTerminal(root_node_a, best_node, score);
    //best_node = findBestTerminal(root_node_a, best_node, components);

    if (debug_level_d >= Integral::ALL) {
      best_node->getItem()->getNodeIndex().debug(L"split node");
    }
 
    // check to see if there are no more nodes to split
    //
    if ((k > 1 || score == 0.0) && best_node == root_node_a) {
      break;
    }

    // local variables
    //
    RegressionDecisionTreeNode* rdt_node = (RegressionDecisionTreeNode*)NULL;
  
    // get the data on this node
    //
    rdt_node = best_node->getItem();

    rdt_node->updateDistribution(stat_models_d);
    
    // get the parent node distribution
    // such as average mean and average covar
    //
    rdt_node->getAverageMean(average_mean);
    rdt_node->getAverageCov(average_covariance);

    average_mean.debug(L"average_mean--best node point");
    average_covariance.debug(L"average_covariance--first check point");
    
    // copy parent node distribution to children
    //
    left_child_node->setAverageMean(average_mean);
    right_child_node->setAverageMean(average_mean);

    left_child_node->setAverageCov(average_covariance);
    right_child_node->setAverageCov(average_covariance);    

    // perturb mean of the children
    //
    perturbMean(left_child_node, 0.05);
    perturbMean(right_child_node, -0.05);
  
    left_child_node->getAverageMean(left_average_mean);
    right_child_node->getAverageMean(right_average_mean);

    // cluster node information to childre
    //
    clusterChildren(rdt_node, left_child_node, right_child_node);

    if ((long)left_child_node->getNumComponents() == 0 ||
	(long)right_child_node->getNumComponents() == 0) {
      best_node->getItem()->setSplitFlag(false);
      k--;           // ignore this loop and select another node to split
      continue;
    }

    long parent_index = best_node->getItem()->getNodeIndex();
    
    left_child_node->setParentNodeIndex(parent_index);
    right_child_node->setParentNodeIndex(parent_index);
    
    left_child_node->setNodeIndex(index++);
    right_child_node->setNodeIndex(index++);
    
    // create new children to link to
    //
    BiGraphVertex<RegressionDecisionTreeNode>* left_node
      = insertVertex(left_child_node);
    BiGraphVertex<RegressionDecisionTreeNode>* right_node
      = insertVertex(right_child_node);  

    insertArc(best_node, left_node, true);
    insertArc(best_node, right_node, true);
  
  }

  // exit gracefully
  //
  return true;
}


// method: perturbMean
//
// arguments:
//  RegressionDecisionTreeNode*& child_node: (input) node to perturb the mean
//  double perturb_depth: (input) perturb depth
//
// return: a boolean value indicating status
//
// this method perturb mean using given perturb depth
//
boolean RegressionDecisionTree::perturbMean(RegressionDecisionTreeNode*&
					    child_node_a,
					    double perturb_depth_a) {
  VectorFloat mean;
  MatrixFloat covariance;

  child_node_a->getAverageMean(mean);
  child_node_a->getAverageCov(covariance);

  for (long i = 0; i < mean.length(); i++) {
    double x = perturb_depth_a * covariance(i, i);
    mean(i) += (float)x;
  }

  child_node_a->setAverageMean(mean);
  
  // exit gracefully
  //
  return true;
  
}


// method: clusterChildren
//
// arguments:
//  RegressionDecisionTreeNode*& root_node: (input) parent node 
//  RegressionDecisionTreeNode*& left_child_node: (input) left child node
//  RegressionDecisionTreeNode*& right_child_node: (input) right child node
//
// return: a boolean value indicating status
//
// this method cluster the components in the parent node to its left
// and right child nodes
//
boolean RegressionDecisionTree::clusterChildren(RegressionDecisionTreeNode*&
						root_node_a,
						RegressionDecisionTreeNode*&
						left_child_node_a,
						RegressionDecisionTreeNode*&
						right_child_node_a) {
  const double threshold = 1.0e-12;
  long iteration = 0;
  double oldDistance, newDistance = 0.0;

  long MAX_ITERATION = 10000;

  do {
    iteration += 1;
    if (iteration >= MAX_ITERATION)
      break;
    calculateDistance(root_node_a, left_child_node_a, right_child_node_a);
    if (iteration == 1)
      oldDistance = left_child_node_a->getClusterScore() +
	right_child_node_a->getClusterScore() + threshold;
    else
      oldDistance = newDistance;
    newDistance = (left_child_node_a->getClusterScore() *
		   left_child_node_a->getClusterAccumulate() +
		   right_child_node_a->getClusterScore() *
		   right_child_node_a->getClusterAccumulate()) /
      (left_child_node_a->getClusterAccumulate() +
       right_child_node_a->getClusterAccumulate());
    
  } while ((oldDistance - newDistance) > threshold);
  
  createChildNode(root_node_a, left_child_node_a, right_child_node_a);
  
  // exit gracefully
  //
  return true;
}

// method: calculateDistance
//
// arguments:
//  RegressionDecisionTreeNode*& root_node: (input) parent node 
//  RegressionDecisionTreeNode*& left_child_node: (input) left child node
//  RegressionDecisionTreeNode*& right_child_node: (input) right child node
//
// return: a boolean value indicating status
//
// this method calculates the distance for all the components with
// respect to its average mean
//
boolean RegressionDecisionTree::calculateDistance(RegressionDecisionTreeNode*&
						  root_node_a,
						  RegressionDecisionTreeNode*&
						  left_child_node_a,
						  RegressionDecisionTreeNode*&
						  right_child_node_a) {

  
  double score1, score2;
  VectorFloat mean;
  VectorFloat left_mean_sum;
  VectorFloat right_mean_sum;  

  VectorFloat left_average_mean, right_average_mean;
  Double occ;
    
  double left_accu = 0.0, right_accu = 0.0;
  double left_score = 0.0, right_score = 0.0;
  long temp_left_index = 0, temp_right_index = 0;

  left_child_node_a->getAverageMean(left_average_mean);
  right_child_node_a->getAverageMean(right_average_mean);
  
  
  // loop all the models in the root node and calculate the mean value
  // under the current classification condition
  //
  RData gaussian_models;
   
  gaussian_models = root_node_a->getComponents();

  // loop over all gaussian model index
  //
  for (boolean more_index = gaussian_models.gotoFirst(); more_index;
       more_index = gaussian_models.gotoNext()) {

    // get the first datapoint in triple
    //
    RDataPoint* datapoint = gaussian_models.getCurr();
    
    // get the statistical model index
    //
    long sm_index = (long)datapoint->first();
    
    // get the gaussian model index
    //
    long gm_index = (long)datapoint->second();

    // if the model to adapt is a single Gaussian model, compute its
    // contribution to the global adaptation
    //
    if (stat_models_d(sm_index).getType() == StatisticalModel::GAUSSIAN_MODEL) {
      GaussianModel& gm = stat_models_d(sm_index).getGaussianModel();
      
      // compute the contribution of this Gaussian model to the global
      // adaptation
      //
      gm.getMean(mean);
      occ = gm.getOccupancy();
    }
    
    // if the model to adapt is a mixture of Gaussian models, compute
    // the contribution of mixture component match the index to the
    // global adaptation
    //
    else if (stat_models_d(sm_index).getType() == StatisticalModel::MIXTURE_MODEL) {

      // get the linked list of mixture components
      //
      SingleLinkedList<StatisticalModel>& models =
	const_cast<StatisticalModel&>(stat_models_d(sm_index)).
	getMixtureModel().getModels();

      // check whether each mixture component is Gaussian model and
      // compute its contribution to the global adaptation
      //
      boolean more = models.gotoFirst();
      long local_gaussian_index = 0;
      
      while (more) {

	// get the current mixture component
	//
	StatisticalModel* model = models.getCurr();

	// check whether it is Gaussian
	//
	if (model->getType() == StatisticalModel::GAUSSIAN_MODEL) {
	  GaussianModel& gm = model->getGaussianModel();

	  // compute the contribution of this Gaussian model to the
	  // global adaptation
	  //
	  if (local_gaussian_index == gm_index) {
	    gm.getMean(mean);
	    occ = gm.getOccupancy();
	    break;
	  }
	  else
	    local_gaussian_index++;
	}
	else {
	  return Error::handle(name(), L"calculateDistance",
			       RegressionDecisionTree::ERR_ADAPT_NO_GAUSSIAN,
			       __FILE__, __LINE__);
	}
	
	// move to the next mixture component
	//
	more = models.gotoNext();
	  
      } // end of loop over all mixtures
    } // end of else if mixture model

    // Calculate the distance between this mean and average mean of
    // each child node

    score1 = mean.distance(left_average_mean);
    score2 = mean.distance(right_average_mean);

    VectorFloat temp_mean;
    temp_mean.assign(mean);
    temp_mean.mult(occ);
    
    if (score1 < score2) {
      // accumulate the occupancy count to left node
      //
      left_accu += occ;
      left_score += (occ*score1);
      
      if (temp_left_index == 0) {
	left_mean_sum.assign(temp_mean);
	temp_left_index++;
      }
      else {
	left_mean_sum.add(temp_mean);
      	temp_left_index++;
      }
    }
    else {
      // accumulate the occupancy count to the right node
      //
      right_accu += occ;
      right_score += (occ*score2);
      
      if (temp_right_index == 0) {
	right_mean_sum.assign(temp_mean);
	temp_right_index++;
      }
      else {
	temp_right_index++;
	right_mean_sum.add(temp_mean);
      }
    }
    
  } // end of all model loop

  // recalculate the average mean for each node
  //
  left_average_mean.assign(left_mean_sum);
  right_average_mean.assign(right_mean_sum);

  left_average_mean.div(left_accu);
  right_average_mean.div(right_accu);    

  left_child_node_a->setAverageMean(left_average_mean);
  right_child_node_a->setAverageMean(right_average_mean);
    
  left_child_node_a->setClusterAccumulate(left_accu);
  right_child_node_a->setClusterAccumulate(right_accu);

  left_child_node_a->setClusterScore(left_score);
  right_child_node_a->setClusterScore(right_score);

  left_child_node_a->setNumComponents(temp_left_index);
  right_child_node_a->setNumComponents(temp_right_index);
  
  // exit gracefully
  //
  return true;
}


// method: createChildNodes
//
// arguments:
//  RegressionDecisionTreeNode*& root_node: (input) parent node 
//  RegressionDecisionTreeNode*& left_child_node: (input) left child node
//  RegressionDecisionTreeNode*& right_child_node: (input) right child node
//
// return: a boolean value indicating status
//
// this method classifies the components in parent node to its child
// nodes
//
boolean RegressionDecisionTree::createChildNode(RegressionDecisionTreeNode*&
						root_node_a,
						RegressionDecisionTreeNode*&
						left_child_node_a,
						RegressionDecisionTreeNode*&
						right_child_node_a) {
  
  double score_left, score_right;
  long num_left = 0, num_right = 0;
  VectorFloat mean, left_average_mean, right_average_mean;
  RData left, right;

  // loop all the models in the root node and calculate the mean value
  // under the current classification condition
  //
  RData gaussian_models;
   
  gaussian_models = root_node_a->getComponents();

  if ((long)left_child_node_a->getNumComponents() == 0 ||
      (long)right_child_node_a->getNumComponents() == 0) {
    return false;
  }
    
  // loop over all gaussian model index
  //
  for (boolean more_index = gaussian_models.gotoFirst(); more_index;
       more_index = gaussian_models.gotoNext()) {

    // get the first datapoint in triple
    //
    RDataPoint* datapoint = gaussian_models.getCurr();
    
    // get the statistical model index
    //
    long sm_index = (long)datapoint->first();
    
    // get the gaussian model index
    //
    long gm_index = (long)datapoint->second();
    
    GaussianModel gm;
    
    getGaussianModel(sm_index, gm_index, gm);

    gm.getMean(mean);
    
    left_child_node_a->getAverageMean(left_average_mean);
    right_child_node_a->getAverageMean(right_average_mean);
    
    // Calculate the distance between this mean and average mean of
    // each child node

    score_left = mean.distance(left_average_mean);
    score_right = mean.distance(right_average_mean);

    if (score_left < score_right) {
      // insert to left node
      //
      left.insert(datapoint);
      num_left++;
    }
    else {
      // insert to right node
      //
      right.insert(datapoint);
      num_right++;
    }

  } // end of all model loop

  if (num_left == 0 || num_right == 0) {
    return false;
  }
  
  left_child_node_a->setNumComponents(num_left);
  right_child_node_a->setNumComponents(num_right);

  left_child_node_a->setComponents(left);
  right_child_node_a->setComponents(right);  

  // calculate the average mean and variance of the cluster
  //
  left_child_node_a->updateDistribution(stat_models_d);
  right_child_node_a->updateDistribution(stat_models_d);
  
  // exit gracefully
  //
  return true;
}


// method: runDecisionTree
//
// arguments:
//  none
//
// return: a boolean value indicating status
//
// this method runs the decisiontree using the specified runmode and
// stopmode.
//
boolean RegressionDecisionTree::runDecisionTree() {

  // exit gracefully
  //
  return true;
}


// method: getMean
//
// arguments:
//  long sm_index: (input) statistical model index
//  long gm_index: (input) Gaussian model index
//  VectorFloat& mean: (output) mean value for this model specified
//                      by the index of the statistical model index
//                      and Gaussian model index
//
// return: a boolean value indicating status
//
// this method get the mean value for the specified modle using the
// index for statistical model and Gaussian model
//
boolean RegressionDecisionTree::getMean(long sm_index_a,
					long gm_index_a,
					VectorFloat& mean_a) {

  // if the model to adapt is a single Gaussian model, directly get
  // the mean
  //
  if (stat_models_d(sm_index_a).getType() == StatisticalModel::GAUSSIAN_MODEL) {
    GaussianModel& gm = stat_models_d(sm_index_a).getGaussianModel();
    
    // get the mean value of this model
    //
    gm.getMean(mean_a);
  }
    
  // if the model to adapt is a mixture of Gaussian models, compute
  // the contribution of mixture component match the index to the
  // mean
  //
  else if (stat_models_d(sm_index_a).getType() == StatisticalModel::MIXTURE_MODEL) {
    
    // get the linked list of mixture components
    //
    SingleLinkedList<StatisticalModel>& models =
      const_cast<StatisticalModel&>(stat_models_d(sm_index_a)).
      getMixtureModel().getModels();
    
    // check whether each mixture component is Gaussian model and
    // compute its contribution to the global adaptation
    //
    boolean more = models.gotoFirst();
    long local_gaussian_index = 0;
    
    while (more) {
      
      // get the current mixture component
      //
      StatisticalModel* model = models.getCurr();
      
      // check whether it is Gaussian
      //
      if (model->getType() == StatisticalModel::GAUSSIAN_MODEL) {
	GaussianModel& gm = model->getGaussianModel();
	
	//Long(gm.getAccessCount()).debug(L"access count in createTrans is");
	
	// compute the contribution of this Gaussian model to the
	// global adaptation
	//
	if (local_gaussian_index == gm_index_a) {
	  gm.getMean(mean_a);
	  break;
	}
	else
	  local_gaussian_index++;
      }
      else {
	return Error::handle(name(), L"getMean",
			     RegressionDecisionTree::ERR_ADAPT_NO_GAUSSIAN,
			     __FILE__, __LINE__);
      }
      
      // move to the next mixture component
      //
      more = models.gotoNext();
      
    } // end of loop over all mixtures
  } // end of else if mixture model
  
  // exit gracefully
  //
  return true;
}


// method: getGaussianModel
//
// arguments
//  long sm_index: (input) statistical model index
//  long gm_index: (input) Gaussian model index
//  GaussianModel& gm: (output) Gaussian model specified
//                        by the index of the statistical model index
//                        and Gaussian model index
//
// return: a boolean value indicating status
//
// this method get the Gaussian model by specified model using the
// index for statistical model and Gaussian model
//
boolean RegressionDecisionTree::getGaussianModel(long sm_index_a,
						 long gm_index_a,
						 GaussianModel& gm_a) {

  // local variable
  //
  boolean res = false;
  
  // if the model is a single Gaussian model, get the model directly
  //
  if (stat_models_d(sm_index_a).getType() ==
      StatisticalModel::GAUSSIAN_MODEL) {

    gm_a = stat_models_d(sm_index_a).getGaussianModel();
    res = true;
  }
    
  // if the model is a mixture of Gaussian models, loop to find the
  // model match the index
  //
  else if (stat_models_d(sm_index_a).getType() ==
	   StatisticalModel::MIXTURE_MODEL) {
    
    // get the linked list of mixture components
    //
    SingleLinkedList<StatisticalModel>& models =
      const_cast<StatisticalModel&>(stat_models_d(sm_index_a)).
      getMixtureModel().getModels();
    
    // check whether each mixture component is Gaussian model and
    // compute its contribution to the global adaptation
    //
    boolean more = models.gotoFirst();
    long local_gaussian_index = 0;
    
    while (more) {
      
      // get the current mixture component
      //
      StatisticalModel* model = models.getCurr();
      
      // check whether it is Gaussian
      //
      if (model->getType() == StatisticalModel::GAUSSIAN_MODEL) {
	
	// compute the contribution of this Gaussian model to the
	// global adaptation
	//
	if (local_gaussian_index == gm_index_a) {

	  gm_a = model->getGaussianModel();
	  res = true;
	  break;
	}
	else
	  local_gaussian_index++;
      }
      else {
	return Error::handle(name(), L"getGaussianModel",
			     RegressionDecisionTree::ERR_ADAPT_NO_GAUSSIAN,
			     __FILE__, __LINE__);
      }
      
      // move to the next mixture component
      //
      more = models.gotoNext();
      
    } // end of loop over all mixtures
  } // end of else if mixture model
  
  // exit gracefully
  //
  return res;
}


// method: findBestTerminal
//
// arguments:
//  TreeNode* node: (input) input node
//  TreeNode* best: (input) best node to split
//  long& component: (input/output) the maximum components upto now
//
// return: a TreeNode point for the best node
//
// this method return the best node to split when the input root node
// is given by using the maximum components
//
BiGraphVertex<RegressionDecisionTreeNode>* RegressionDecisionTree::findBestTerminal(RTreeNode*& node_a, RTreeNode*& best_a, long& components_a) {
  
  // local variables
  //
  long component = 0;

  DoubleLinkedList< BiGraphArc <RegressionDecisionTreeNode> >* children;
  BiGraphVertex<RegressionDecisionTreeNode>* child_node;
  
  // check the node
  //
  if (node_a != (RTreeNode*)NULL) {
    // get all the child nodes of this node
    //
    children = node_a->getChildren();

    // if this node has child nodes, accumulate the child nodes
    //
    if (node_a->gotoFirstChild()) {
      
      // loop for the left child
      //
      children->gotoFirst();
      child_node = children->getCurr()->getVertex();
      
      best_a = findBestTerminal(child_node, best_a, components_a);
    }

    else {

      // local variables
      //
      RegressionDecisionTreeNode* rdt_node = (RegressionDecisionTreeNode*)NULL;
      
      // get the data on this node
      //
      rdt_node = node_a->getItem();
      
      rdt_node->updateDistribution(stat_models_d);

      // if this node is unsplitable, return the previous best node
      //
      if (!(rdt_node -> getSplitFlag())) {
	return best_a;
      }
      
      component = rdt_node->getNumComponents();
      
      // if the node's score is greater, record the score and set the
      // best node to this node
      // 
      if (components_a < component && component > components_threshold_d) {
	components_a = component;
	best_a = node_a;
      }
      return best_a;
    }

    if( children->gotoNext()) {
      // loop for the right child
      //
      child_node = children->getCurr()->getVertex();
      
      best_a = findBestTerminal(child_node, best_a, components_a);
    }
  }

  // return the best node
  //
  return best_a;
}
