// file: $isip/class/pr/DecisionTreeBase/DecisionTreeBase.h
// version: $Id: DecisionTreeBase.h 9448 2004-04-11 20:37:04Z gao $
//

// make sure definitions are only made once:
//
#ifndef ISIP_DECISION_TREE_BASE
#define ISIP_DECISION_TREE_BASE

// isip include files
//
#ifndef ISIP_BI_GRAPH
#include <BiGraph.h>
#endif

#ifndef ISIP_DEBUG_LEVEL
#include <DebugLevel.h>
#endif

#ifndef ISIP_FILENAME
#include <Filename.h>
#endif

// DecisionTreeBase: a class that defines an interface contract for all
// the DecisionTrees
//
template<class TObject>
class DecisionTreeBase : public BiGraph<TObject> {

  //---------------------------------------------------------------------------
  //
  // public constants
  //
  //---------------------------------------------------------------------------
public:

  // define the class name
  //
  static const String CLASS_NAME;

  //----------------------------------------
  //
  // other important constants
  //
  //----------------------------------------
  
  // define an enumeration for all possible stopping rules for splitting:
  //
  enum STOPMODE { PURE = 0, THRESH, DEPTH, DEF_STOPMODE = PURE };

  // define an enumeration for run mode
  //
  enum RUNMODE { TRAIN = 0, TEST, DEF_RUNMODE = TRAIN };
  
  // define static NameMap objects
  //
  static const NameMap STOPMODE_MAP;
  static const NameMap RUNMODE_MAP;

  //----------------------------------------
  //
  // i/o related constants
  //
  //----------------------------------------

  static const String PARAM_STOPMODE;
  static const String PARAM_RUNMODE;
  static const String PARAM_DBGL;

  //----------------------------------------
  //
  // default values and arguments
  //
  //----------------------------------------  

  // signal processing constants
  //
  static const long DEF_DEPTH = 0;
  
  //----------------------------------------
  //
  // error codes
  //
  //----------------------------------------  

  static const long ERR = 00100200;
  
  //---------------------------------------------------------------------------
  //
  // protected data
  //
  //---------------------------------------------------------------------------
protected:

  // data structure declarations
  //
  typedef Pair<String, SingleLinkedList<String> > Attribute;
  typedef SingleLinkedList<Attribute> Attributes;

  // stopmode type
  //
  STOPMODE stopmode_d;

  // runmode type
  //
  RUNMODE runmode_d;
  
  // define parameters that are not part of the i/o for the class
  //
  long depth_d;

  // Atributes of the data
  //
  Attributes attributes_d;

  // debugging parameters
  //
  DebugLevel debug_level_d;
  
  //---------------------------------------------------------------------------
  //
  // required public methods
  //
  //---------------------------------------------------------------------------
public:
  
  // method: name
  //
  static const String& name() {
    return CLASS_NAME;
  }
  
  // other static methods
  // diagnose method is moved outside the class header file and
  // defined in the DecisionTreeBaseDiagnose.h in order to avoid issues
  // related to preprocessing of the diagnose code.
  //

  // method: setDebug
  //
  // note that this method is not virtual since
  // it operates on protected data found in this class. 
  //
  boolean setDebug(Integral::DEBUG level) {
    debug_level_d = level;
    return true;
  }

  // other debug methods
  //
  virtual boolean debug(const unichar* msg) const;

  // method: destructor
  //
  virtual ~DecisionTreeBase() {}

  // method: default constructor
  //
  DecisionTreeBase();

  // method: copy constructor
  //
  DecisionTreeBase(const DecisionTreeBase& arg) {
    assign(arg);
  }

  // assign methods:
  //  note that this method is not set to zero. this class must supply
  //  a version of this method, since it manipulates protected data.
  //
  virtual boolean assign(const DecisionTreeBase& arg);
  
  // method: sofSize
  //
  virtual long sofSize() const = 0;

  // method: read
  //
  virtual boolean read(Sof& sof, long tag, const String& name) = 0;

  // method: write
  //
  virtual boolean write(Sof& sof, long tag, const String& name) const = 0;

  // method: readData
  //
  virtual boolean readData(Sof& sof, const String& pname,
			   long size, boolean param, boolean nested) = 0;

  // method: writeData
  //
  virtual boolean writeData(Sof& sof, const String& pname) const = 0;
  
  // method: eq
  //
  virtual boolean eq(const DecisionTreeBase& arg) const;

  // memory management methods:
  //  new and delete methods are omitted since only the derived
  //  classes will be used
  //
  virtual boolean clear(Integral::CMODE ctype = Integral::DEF_CMODE);
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  set methods
  //
  // note: these functions are needed to manipulate protected data for
  // this class that must be invoked by the public interface for the
  // class. note that the set methods are virtual so a derived class
  // can override it if it needs to do more with the method, such as
  // buffer resizing.
  //
  //--------------------------------------------------------------------------

  // method: setStopMode
  //
  boolean setStopMode(STOPMODE stopmode) {
    stopmode_d = stopmode;
    return true;  
  }  

  // method: setRunMode
  //
  boolean setRunMode(RUNMODE runmode) {
    runmode_d = runmode;
    return true;  
  }  

  // method: setDepth
  //
  boolean setDepth(long& depth) {
    depth_d = depth;
    return true;  
  }  

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  get methods
  //
  //---------------------------------------------------------------------------
    
  // method: getStopMode
  //
  STOPMODE getStopMode() const {
    return stopmode_d;
  }

  // method: getRunMode
  //
  RUNMODE getRunMode() const {
    return runmode_d;
  }

  // method: getDepth
  //
  long getDepth() {
    return depth_d;  
  }

  // method: getLeafNodes
  //
  boolean getLeafNodes(BiGraphVertex<TObject>& node,
		       SingleLinkedList<BiGraphVertex<TObject> >& leaf_nodes);

  // method: getAllNodes
  //
  boolean getAllLeafNodes(BiGraphVertex<TObject>& node,
		      SingleLinkedList<BiGraphVertex<TObject> >& leaf_nodes);

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:  
  //  interface contract methods
  //
  // note: the methods below constitute the interface required by all
  // DecisionTrees that are going to be part of the generalized transform
  // software (PhoneticDecisionTree etc.).
  //
  //---------------------------------------------------------------------------

  // method: runDecisionTree
  //
  virtual boolean runDecisionTree() = 0;

  // method: setParser
  //
  virtual boolean setParser(SofParser* parser) { return false; }

  //---------------------------------------------------------------------------
  //
  // private methods
  //
  //---------------------------------------------------------------------------
private:
};

//---------------------------------------------------------------------------
//
// we define non-integral constants at the end of class definition for
// templates (for non-templates these are defined in the default constructor)
//      
//---------------------------------------------------------------------------

// constants: class name
//
template<class TObject>
const String DecisionTreeBase<TObject>::CLASS_NAME(L"DecisionTreeBase");

// constants: i/o related constants
//
template<class TObject>
const String DecisionTreeBase<TObject>::PARAM_DBGL(L"debug_level");

template<class TObject>
const String DecisionTreeBase<TObject>::PARAM_STOPMODE(L"stopmode");

template<class TObject>
const String DecisionTreeBase<TObject>::PARAM_RUNMODE(L"runmode");

// constants: name map for enum
//
template<class TObject>
const NameMap DecisionTreeBase<TObject>::STOPMODE_MAP(L"PURE, THRESH, DEPTH");

template<class TObject>
const NameMap DecisionTreeBase<TObject>::RUNMODE_MAP(L"TRAIN, CLASSIFY");

// below are all the methods for the DecisionTreeBase template class
//      

// ---------------------------------------------------------------------
//
// required static methods
//
//----------------------------------------------------------------------

// ---------------------------------------------------------------------
//
// required debug methods
//
//----------------------------------------------------------------------

// method: debug
//
// arguments:
//  const unichar* msg: (input) message to print
//
// return: a boolean value indicating status
//
template<class TObject>
boolean DecisionTreeBase<TObject>::debug(const unichar* msg_a) const {

  // declare local variables
  //
  String value;
  String output;
 
  // output an information message
  //
  output.debugStr(name(), msg_a, L":");
  Console::put(output);
  Console::increaseIndention();
  
  // display debug level
  //
  output.debugStr(name(), msg_a, L"debug_level_d", debug_level_d.getName());
  Console::put(output);

  // display the various modes
  //
  output.debugStr(name(), msg_a, L"stopmode_d",
                  STOPMODE_MAP.getName((long)stopmode_d));
  Console::put(output);

  output.debugStr(name(), msg_a, L"runmode_d",
                  RUNMODE_MAP.getName((long)runmode_d));
  Console::put(output);  

  // display common decision tree parameters
  //
  value.assign(depth_d);
  output.debugStr(name(), msg_a, L"depth_d", value);
  Console::put(output);

  // display the attributes
  //
  attributes_d.debug(L"attributes_d");
  
  // call the parent BiGraph debug method to debug the tree
  //
  BiGraph<TObject>::debug(L"BiGraph Tree");

  // decrease indention
  //
  Console::decreaseIndention();
 
  // exit gracefully
  //
  return true;
}

//------------------------------------------------------------------------
//
// required destructor/constructor(s)
//
//-----------------------------------------------------------------------

// method: constructor
//
// arguments: none
//
// return: none
//
// this is the default constructor
//
template<class TObject>
DecisionTreeBase<TObject>::DecisionTreeBase() {

  // initialize protected data
  //
  stopmode_d = DEF_STOPMODE;
  runmode_d = DEF_RUNMODE;
  depth_d = DEF_DEPTH;
}

//-------------------------------------------------------------------------
//
//  required assign methods
//
//-------------------------------------------------------------------------

// method: assign
//
// arguments:
//  const DecisionTreeBase& arg: (input) object to assign
//
// return: a boolean value indicating status
//
// this method assigns the input argument to "this".
//
template<class TObject>
boolean DecisionTreeBase<TObject>::assign(const DecisionTreeBase<TObject>&
					  arg_a) {

  // assign data related to i/o
  //
  debug_level_d = arg_a.debug_level_d;

  // assign other protected data
  //
  stopmode_d = arg_a.stopmode_d;
  runmode_d = arg_a.runmode_d;
  depth_d = arg_a.depth_d;
  attributes_d = arg_a.attributes_d;
  
  // exit gracefully
  //
  return true;
}

// method: eq
//
// arguments:
//  const DecisionTreeBase& arg: (input) input object
//
// return: a boolean value indicating status
//
// this method checks if two objects are the same
//
template<class TObject>
boolean DecisionTreeBase<TObject>::eq(const DecisionTreeBase<TObject>& arg_a)
  const {

  // check all internal data
  //
  if (((DebugLevel)debug_level_d != arg_a.debug_level_d) ||
      (stopmode_d != arg_a.stopmode_d) ||
      (runmode_d != arg_a.runmode_d) ||
      (depth_d != arg_a.depth_d) ||
      (!attributes_d.eq(arg_a.attributes_d)) ||
      !(((BiGraph<TObject>*)this)->eq((BiGraph<TObject>)arg_a))) {
    
    return false;
  }
      
  // exit gracefully
  //
  return true;
}

// method: clear
//
// arguments:
//  Integral::CMODE ctype: (input) clear mode
//
// return: a boolean value indicating status
//
template<class TObject>
boolean DecisionTreeBase<TObject>::clear(Integral::CMODE ctype_a) {

  // debug level is typically a parameter, so keep its setting in
  // retain mode.
  //
  if (ctype_a != Integral::RETAIN) {
    debug_level_d.assign(Integral::DEF_DEBUG);
  }

  // reset to initial state for all values of ctype_a
  //
  stopmode_d = DEF_STOPMODE;
  runmode_d = DEF_RUNMODE;

  // clear the attributes
  //
  attributes_d.clear(ctype_a);

  // call the clear method of the Bigraph
  //
  clear(ctype_a);
	
  // exit gracefully
  //
  return true;
}

// method: getLeafNodes
//
// arguments:
//  BiGraphVertex<TObject>& node: (input) input node
//  SingleLinkedList<BiGraphVertex<TObject> >& leaf_nodes: (output) list of
//                                                         output nodes
//
// return: a boolean value indicating status
//
// this method returns the LeafNodes at same-level or one-level below
// a node in the decisiontree
//
template<class TObject>
boolean DecisionTreeBase<TObject>::getLeafNodes(BiGraphVertex<TObject>& node_a,
       		 SingleLinkedList<BiGraphVertex<TObject> >& leaf_nodes_a) {
  
  // local variables
  //
  DoubleLinkedList< BiGraphArc <TObject> >* children;
  BiGraphArc<TObject>* child;
  BiGraphVertex<TObject>* child_node;
  BiGraphVertex<TObject>* temp_node;
  SingleLinkedList< BiGraphVertex <TObject> > nodes(DstrBase::USER);
  SingleLinkedList<BiGraphVertex<TObject> > nonleaf_nodes(DstrBase::USER);  
  boolean nonleaf = true;
  
  // 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-over all the child nodes and accumulate these nodes
    //
    for (boolean more = children->gotoFirst(); more;
	 more = children->gotoNext()) {            
      child = children->getCurr();
      child_node = child->getVertex();
      nodes.insert(child_node);
    }
  }
  
  // else this node is a leaf node, no child nodes and so exit
  //
  else {
    leaf_nodes_a.insert(&node_a);
    nonleaf = false;
  }

  // loop till all the leafnodes are found
  //
  while (nonleaf) {
    
    nonleaf = false;

    // loop over all the nodes and accumulate the leaf nodes
    //
    for (boolean more = nodes.gotoFirst(); more;
	 more = nodes.gotoNext()) {          
      
      temp_node = nodes.getCurr();
      
      // nodes that don't have children are leaf nodes
      //
      if (!temp_node->gotoFirstChild()) {
	
	// add this leaf node to the singlelinkedlist
	//
	leaf_nodes_a.insert(temp_node);
      }
      
      // store the nonleaf nodes seperately
      //
      else {
	nonleaf_nodes.insert(temp_node);      
	nonleaf = true;
      }    
    }
    
    // reset the memory
    //
    nodes.clear(Integral::RESET);

    // loop over all the nonleaf nodes and accumulate their children
    //
    for (boolean more = nonleaf_nodes.gotoFirst(); more;
	 more = nonleaf_nodes.gotoNext()) {          
      
      // get all the child nodes of this nodes
      //
      temp_node = nonleaf_nodes.getCurr();
      children = temp_node->getChildren();
      
      // loop-over all the child nodes and accumulate them
      //
      for (boolean morea = children->gotoFirst(); morea;
	   morea = children->gotoNext()) {            
	child = children->getCurr();
	child_node = child->getVertex();
	nodes.insert(child_node);
      }
    }

    // reset the memory
    //
    nonleaf_nodes.clear(Integral::RESET);    
  }
  
  // exit gracefully
  //
  return true;
}



// method: getAllLeafNodes
//
// arguments:
//  BiGraphVertex<TObject>& node: (input) input node
//  SingleLinkedList<BiGraphVertex<TObject> >& leaf_nodes: (output) list of
//                                                         output nodes
//
// return: a boolean value indicating status
//
// this method returns all the LeafNodes below a specified node in the
// decisiontree
//

template<class TObject>
boolean DecisionTreeBase<TObject>::getAllLeafNodes(BiGraphVertex<TObject>& node_a,
       		 SingleLinkedList<BiGraphVertex<TObject> >& leaf_nodes_a) {
  
  // local variables
  //
  DoubleLinkedList< BiGraphArc <TObject> >* children;
  BiGraphArc<TObject>* child;
  BiGraphVertex<TObject>* child_node;

  SingleLinkedList< BiGraphVertex <TObject> > nodes(DstrBase::USER);
  SingleLinkedList<BiGraphVertex<TObject> > nonleaf_nodes(DstrBase::USER);  
  
  // 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-over all the child nodes and accumulate these nodes
    //
    for (boolean more = children->gotoFirst(); more;
	 more = children->gotoNext()) {            
      child = children->getCurr();
      child_node = child->getVertex();
      
      getAllLeafNodes(*child_node, leaf_nodes_a);
    }
  }
  
  // else this node is a leaf node, no child nodes and so exit
  //
  else {
    leaf_nodes_a.insert(&node_a);
  }
  
  // exit gracefully
  //
  return true;
}

// end of include file
// 
#endif
