// file: $isip/class/dstr/UGraph/UGraph.h
// version: $Id: UGraphDiagnose.h 6197 2001-01-17 17:47:00Z alphonso $
//

// make sure definitions are only made once
//
#ifndef ISIP_U_GRAPH_DIAGNOSE
#define ISIP_U_GRAPH_DIAGNOSE

// isip include files
//
#ifndef ISIP_U_GRAPH
#include "UGraph.h"
#endif

// UGraphDiagnose: a class that contains the diagnose method of UGraph class.
//
template<class TObject>
class UGraphDiagnose : public UGraph<TObject> {

  //---------------------------------------------------------------------------
  //
  // public constants
  //
  //---------------------------------------------------------------------------
public:
  
  // define the class name
  //

  //----------------------------------------
  //
  // i/o related constants
  //
  //----------------------------------------  
  
  //----------------------------------------
  //
  // default values and arguments
  //
  //----------------------------------------

  // default values
  //
    
  // default arguments to methods
  //
  
  //----------------------------------------
  //
  // error codes
  //
  //----------------------------------------  
  
  //---------------------------------------------------------------------------
  //
  // protected data
  //
  //---------------------------------------------------------------------------
protected:

  //---------------------------------------------------------------------------
  //
  // required public methods
  //
  //---------------------------------------------------------------------------
public:

  // methods: name
  //
  static const String& name() {
    return UGraph<TObject>::name();
  }

  // other static methods
  //
  static boolean diagnose(Integral::DEBUG debug_level);

  // debug methods
  //  these methods are omitted since this class does not have data
  //  members and operations  
  //
  
  // destructor/constructor(s):
  //  these methods are omitted since this class does not have data
  //  members and operations
  //

  // assign methods:
  //  these methods are omitted since this class does not have data
  //  members and operations
  //

  // operator= methods:
  //  these methods are omitted since this class does not have data
  //  members and operations
  //  

  // i/o methods:
  //  these methods are omitted since this class does not have data
  //  members and operations
  //

  // equality methods:
  //  these methods are omitted since this class does not have data
  //  members and operations
  //

  // memory-management methods:
  //  these methods are omitted since this class does not have data
  //  members and operations
  //
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods
  //
  //---------------------------------------------------------------------------

  //  these methods are omitted since this class does not have data
  //  members and operations
  //
  
  //---------------------------------------------------------------------------
  //
  // private methods
  //
  //---------------------------------------------------------------------------
private:

}; 

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

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

// method: diagnose
//
// arguments:
//  Integral::DEBUG level: (input) debug level for diagnostics
//
// return: a boolean value indicating status
//
template<class TObject>
boolean UGraphDiagnose<TObject>::diagnose(Integral::DEBUG level_a) {

  //----------------------------------------------------------------------
  //
  // 0. preliminaries
  //
  //----------------------------------------------------------------------

  // output the class name
  //
  if (level_a > Integral::NONE) {
    SysString output(L"diagnosing class ");
    output.concat(CLASS_NAME);
    output.concat(L": ");
    Console::put(output);
    Console::increaseIndention();
  }

  //---------------------------------------------------------------------
  //
  // 1. required public methods
  //
  //---------------------------------------------------------------------

  // set indentation
  //
  if (level_a > Integral::NONE) {
    Console::put(L"testing required public methods...\n");
    Console::increaseIndention();
  }

  // test the debug methods
  //
  setDebug(debug_level_d);

  // test constructors and memory management methods
  //
  UGraph<Char> def_graph;

  // create an arc from the first to the last node
  //
  def_graph.insertArc(def_graph.getStart(), def_graph.getTerm(), false, 0.75);

  // copy constructor
  //
  UGraph<Char> copy_graph(def_graph);

  // the copy graph should now have an arc from its start node to its terminal
  // node with a weight of 0.75
  //
  GraphArc<Char>* cstr_arc = (GraphArc<Char>*)NULL;
  if ((cstr_arc = copy_graph.getStart()->getFirst()) ==
      (GraphArc<Char>*)NULL) {
    return Error::handle(name(), L"constructors", Error::TEST, __FILE__,
			 __LINE__);
  }
  if ((cstr_arc->getVertex() != copy_graph.getTerm()) ||
      cstr_arc->getEpsilon() ||
      !Integral::almostEqual(cstr_arc->getWeight(), 0.75)) {
    return Error::handle(name(), L"constructors", Error::TEST, __FILE__,
			 __LINE__);
  }

  // check the constructors and destructors for allocating on the dynamic
  // memory heap
  //
  UGraph<Char>* def_dyn_graph = new UGraph<Char>;

  // create an arc from the first to the last node
  //
  def_dyn_graph->insertArc(def_dyn_graph->getStart(), def_dyn_graph->getTerm(),
			false, 0.75);

  // copy constructor
  //
  UGraph<Char>* copy_dyn_graph = new UGraph<Char>(*def_dyn_graph);

  // the copy graph should now have an arc from its start node to its terminal
  // node with a weight of 0.75
  //
  cstr_arc = (GraphArc<Char>*)NULL;
  if ((cstr_arc = copy_dyn_graph->getStart()->getFirst())
      == (GraphArc<Char>*)NULL) {
    return Error::handle(name(), L"constructors", Error::TEST, __FILE__,
			 __LINE__);
  }
  if ((cstr_arc->getVertex() != copy_dyn_graph->getTerm()) ||
      cstr_arc->getEpsilon() ||
      !Integral::almostEqual(cstr_arc->getWeight(), 0.75)) {
    return Error::handle(name(), L"constructors", Error::TEST, __FILE__,
			 __LINE__);
  }

  // clear the respective graphs
  //
  copy_graph.clear(Integral::FREE);
  def_dyn_graph->clear(Integral::FREE);
  def_graph.clear(Integral::FREE);
  copy_dyn_graph->clear(Integral::FREE);
  
  // see if we can dynamically delete
  //
  delete def_dyn_graph;
  delete copy_dyn_graph;

  {
    // test constructors and memory management methods in USER-allocation
    //
    UGraph<Char> def_graph(USER);
    
    // create an arc from the first to the last node
    //
    def_graph.insertArc(def_graph.getStart(), def_graph.getTerm(),
			false, 0.75);
    
    // copy constructor
    //
    UGraph<Char> copy_graph(def_graph);
    
    // the copy graph should now have an arc from its start node to its
    // terminal node with a weight of 0.75
    //
    GraphArc<Char>* cstr_arc = (GraphArc<Char>*)NULL;
    if ((cstr_arc = copy_graph.getStart()->getFirst()) ==
	(GraphArc<Char>*)NULL) {
      return Error::handle(name(), L"constructors", Error::TEST, __FILE__,
			   __LINE__);
    }
    if ((cstr_arc->getVertex() != copy_graph.getTerm()) ||
	cstr_arc->getEpsilon() ||
	!Integral::almostEqual(cstr_arc->getWeight(), 0.75)) {
      return Error::handle(name(), L"constructors", Error::TEST, __FILE__,
			   __LINE__);
    }
    
    // check the constructors and destructors for allocating on the dynamic
    // memory heap in USER-allocation mode
    //
    UGraph<Char>* def_dyn_graph = new UGraph<Char>(USER);
    
    // create an arc from the first to the last node
    //
    def_dyn_graph->insertArc(def_dyn_graph->getStart(),
			     def_dyn_graph->getTerm(), false, 0.75);
    
    // copy constructor
    //
    UGraph<Char>* copy_dyn_graph = new UGraph<Char>(*def_dyn_graph);
    
    // the copy graph should now have an arc from its start node to its
    // terminal node with a weight of 0.75
    //
    cstr_arc = (GraphArc<Char>*)NULL;
    if ((cstr_arc = copy_dyn_graph->getStart()->getFirst())
	== (GraphArc<Char>*)NULL) {
      return Error::handle(name(), L"constructors", Error::TEST, __FILE__,
			   __LINE__);
    }
    if ((cstr_arc->getVertex() != copy_dyn_graph->getTerm()) ||
	cstr_arc->getEpsilon() ||
	!Integral::almostEqual(cstr_arc->getWeight(), 0.75)) {
      return Error::handle(name(), L"constructors", Error::TEST, __FILE__,
			   __LINE__);
    }

    // clear the respective graphs
    //
    copy_graph.clear(Integral::FREE);
    def_dyn_graph->clear(Integral::FREE);
    def_graph.clear(Integral::FREE);
    copy_dyn_graph->clear(Integral::FREE);
    
    // see if we can dynamically delete
    //
    delete def_dyn_graph;
    delete copy_dyn_graph;    
  }

  // test large allocation construction and deletion in both modes
  //
  if (level_a >= Integral::ALL) {

    // output an informative message
    //
    Console::put(L"\ntesting large chunk memory allocation and deletion:\n");
    
    // set the memory to a strange block size so we can hopefully catch any
    // frame overrun errors
    //
    UGraph<TObject>::setGrowSize((long)731);
    
    // loop for a large number of times creating and deleting a large number
    // of vertices at each loop
    //
    for (long j = 1; j <= 100; j++) {
      UGraph<TObject>** self_graphs = new UGraph<TObject>*[j * 50];
      UGraph<TObject>** graphs = new UGraph<TObject>*[j * 50];
      
      // create the vertices
      //
      for (long i = 0; i < j * 50; i++) {
	self_graphs[i] = new UGraph<TObject>();
	graphs[i] = new UGraph<TObject>(USER);
      }
      
      // delete nodes
      //
      for (long i = (j * 50) - 1; i >= 0; i--) {
	delete self_graphs[i];
	delete graphs[i];
      }
      
      delete [] self_graphs;
      delete [] graphs;
    }

    // perform the same test using the new[] and delete [] operators
    //
    for (long j = 1; j <= 100; j++) {
      
      // allocate a large number of nodes
      //
      UGraph<TObject>* self_graphs = new UGraph<TObject>[j * 50];
      UGraph<TObject>* graphs = new UGraph<TObject>[j * 50](USER);

      // clean up memory
      //
      delete [] self_graphs;
      delete [] graphs;
    }
  }

  // test assign method in SYSTEM-allocation mode
  //
  UGraph<Char> assn_graph_0;

  // create an arc from the first to a common node
  //
  Char* char1 = new Char('k');
  assn_graph_0.insertArc(assn_graph_0.getStart(),
			 assn_graph_0.insertVertex(char1),
			 false, 0.75);

  GraphVertex<Char>* tmp_vertex;
  if ((tmp_vertex = assn_graph_0.getFirst()) == (GraphVertex<Char>*)NULL) {
    return Error::handle(name(), L"insertArc", Error::TEST, __FILE__,
			 __LINE__);
  }
  assn_graph_0.insertArc(tmp_vertex, assn_graph_0.getTerm(),
			 false, -0.75);

  UGraph<Char> assn_graph_1;
  assn_graph_1.assign(assn_graph_0);
  
  // the assigned graph should now have an arc from its start node to the
  // char1 with a weight of 0.75 and an arc from the char1 to the terminal
  // node with a weight of -0.75
  //
  cstr_arc = (GraphArc<Char>*)NULL;

  if ((cstr_arc = assn_graph_1.getStart()->getFirst())
      == (GraphArc<Char>*)NULL) {
    return Error::handle(name(), L"assign", Error::TEST, __FILE__,
			 __LINE__);
  }

  if ((cstr_arc->getVertex()->getItem()->ne(*char1)) ||
      cstr_arc->getEpsilon() ||
      !Integral::almostEqual(cstr_arc->getWeight(), 0.75)) {
    return Error::handle(name(), L"assign", Error::TEST, __FILE__,
			 __LINE__);
  }

  assn_graph_1.gotoFirst();
  assn_graph_1.getCurr()->gotoFirst();
  if ((cstr_arc = assn_graph_1.getCurr()->getCurr())
      == (GraphArc<Char>*)NULL) {
    return Error::handle(name(), L"assign", Error::TEST, __FILE__,
			 __LINE__);
  }

  if ((cstr_arc->getVertex() != assn_graph_1.getStart()) ||
      cstr_arc->getEpsilon() ||
      !Integral::almostEqual(cstr_arc->getWeight(), 0.75)) {
    return Error::handle(name(), L"assign", Error::TEST, __FILE__,
			 __LINE__);
  }

  if ((cstr_arc = assn_graph_1.getCurr()->getNext())
      == (GraphArc<Char>*)NULL) {
    return Error::handle(name(), L"assign", Error::TEST, __FILE__,
			 __LINE__);
  }

  if ((cstr_arc->getVertex() != assn_graph_1.getTerm()) ||
      cstr_arc->getEpsilon() ||
      !Integral::almostEqual(cstr_arc->getWeight(), -0.75)) {
    return Error::handle(name(), L"assign", Error::TEST, __FILE__,
			 __LINE__);
  }  

  if ((cstr_arc = assn_graph_1.getTerm()->getFirst())
      == (GraphArc<Char>*)NULL) {
    return Error::handle(name(), L"assign", Error::TEST, __FILE__,
			 __LINE__);
  }

  if ((cstr_arc->getVertex()->getItem()->ne(*char1)) ||
      cstr_arc->getEpsilon() ||
      !Integral::almostEqual(cstr_arc->getWeight(), -0.75)) {
    return Error::handle(name(), L"assign", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  // clean up memory
  //
  delete char1;

  {
    // test assign method in USER-allocation mode
    //
    UGraph<Char> assn_graph_0(USER);
    
    // create an arc from the first to a common node
    //
    Char* char1 = new Char('k');
    assn_graph_0.insertArc(assn_graph_0.getStart(),
			   assn_graph_0.insertVertex(char1),
			   false, 0.75);
    
    GraphVertex<Char>* tmp_vertex;
    if ((tmp_vertex = assn_graph_0.getFirst()) == (GraphVertex<Char>*)NULL) {
      return Error::handle(name(), L"insertArc", Error::TEST, __FILE__,
			   __LINE__);
    }
    assn_graph_0.insertArc(tmp_vertex, assn_graph_0.getTerm(),
			   false, -0.75);
    
    UGraph<Char> assn_graph_1(USER);
    assn_graph_1.assign(assn_graph_0);
    
    // the assigned graph should now have an arc from its start node to the
    // char1 with a weight of 0.75 and an arc from the char1 to the terminal
    // node with a weight of -0.75
    //
    cstr_arc = (GraphArc<Char>*)NULL;
    
    if ((cstr_arc = assn_graph_1.getStart()->getFirst())
	== (GraphArc<Char>*)NULL) {
      return Error::handle(name(), L"assign", Error::TEST, __FILE__,
			   __LINE__);
    }
    
    if ((cstr_arc->getVertex()->getItem()->ne(*char1)) ||
	cstr_arc->getEpsilon() ||
	!Integral::almostEqual(cstr_arc->getWeight(), 0.75)) {
      return Error::handle(name(), L"assign", Error::TEST, __FILE__,
			   __LINE__);
    }
    
    assn_graph_1.gotoFirst();
    assn_graph_1.getCurr()->gotoFirst();
    if ((cstr_arc = assn_graph_1.getCurr()->getCurr())
	== (GraphArc<Char>*)NULL) {
      return Error::handle(name(), L"assign", Error::TEST, __FILE__,
			   __LINE__);
    }
    
    if ((cstr_arc->getVertex() != assn_graph_1.getStart()) ||
	cstr_arc->getEpsilon() ||
	!Integral::almostEqual(cstr_arc->getWeight(), 0.75)) {
      return Error::handle(name(), L"assign", Error::TEST, __FILE__,
			   __LINE__);
    }
    
    if ((cstr_arc = assn_graph_1.getCurr()->getNext())
	== (GraphArc<Char>*)NULL) {
      return Error::handle(name(), L"assign", Error::TEST, __FILE__,
			 __LINE__);
    }
    
    if ((cstr_arc->getVertex() != assn_graph_1.getTerm()) ||
	cstr_arc->getEpsilon() ||
	!Integral::almostEqual(cstr_arc->getWeight(), -0.75)) {
      return Error::handle(name(), L"assign", Error::TEST, __FILE__,
			   __LINE__);
    }  
    
    if ((cstr_arc = assn_graph_1.getTerm()->getFirst())
	== (GraphArc<Char>*)NULL) {
      return Error::handle(name(), L"assign", Error::TEST, __FILE__,
			   __LINE__);
    }
    
    if ((cstr_arc->getVertex()->getItem()->ne(*char1)) ||
	cstr_arc->getEpsilon() ||
	!Integral::almostEqual(cstr_arc->getWeight(), -0.75)) {
      return Error::handle(name(), L"assign", Error::TEST, __FILE__,
			   __LINE__);
    }
    
    // clean up memory
    //
    delete char1;    
  }

  // test the equality method in SYSTEM-allocated mode
  //

  // -------------------------------------------------
  // START    - A 
  // A -(0.3) - A
  //   -(0.3) - R
  //   -(epi) - B
  //   -(epi) - C
  // B -(0.2) - L
  // C -(3.6) - K
  // M -(0.6) - K
  // M -(1.2) - A
  // L -(-0.7)- R
  // L -(0.4) - C
  // S -(-0.2)- K
  // S -(-0.6)- S
  // R        - TERM 
  // -------------------------------------------------  

  // create the graph items
  //
  Char* A = new Char('A');
  Char* B = new Char('B');
  Char* C = new Char('C');
  Char* K = new Char('K');
  Char* L = new Char('L');
  Char* M = new Char('M');
  Char* S = new Char('S');  
  Char* R = new Char('R');

  // create the graph
  //
  UGraph<Char>* graph_00 = new UGraph<Char>();
  
  // create the verteices
  //
  GraphVertex<Char>* VA = graph_00->insertVertex(A);
  GraphVertex<Char>* VB = graph_00->insertVertex(B);
  GraphVertex<Char>* VC = graph_00->insertVertex(C);    
  GraphVertex<Char>* VK = graph_00->insertVertex(K);
  GraphVertex<Char>* VL = graph_00->insertVertex(L);
  GraphVertex<Char>* VM = graph_00->insertVertex(M);    
  GraphVertex<Char>* VS = graph_00->insertVertex(S);
  GraphVertex<Char>* VR = graph_00->insertVertex(R);

  // connect the graph
  //
  graph_00->insertArc(graph_00->getStart(), VA, true);
  graph_00->insertArc(VA, VA, false, 0.3);
  graph_00->insertArc(VA, VR, false, 0.3);
  graph_00->insertArc(VA, VB, true);
  graph_00->insertArc(VA, VC, true);  
  graph_00->insertArc(VB, VL, false, 0.2);
  graph_00->insertArc(VC, VK, false, 3.6);
  graph_00->insertArc(VM, VK, false, 0.6);
  graph_00->insertArc(VM, VA, false, 1.2);
  graph_00->insertArc(VL, VR, false, -0.7);
  graph_00->insertArc(VL, VC, false, 0.4);
  graph_00->insertArc(VS, VK, false, -0.2);
  graph_00->insertArc(VS, VS, false, -0.6);
  graph_00->insertArc(VR, graph_00->getTerm(), true);  

  // create the graph
  //
  UGraph<Char>* graph_01 = new UGraph<Char>(*graph_00);
  
  // create the graph
  //
  UGraph<Char>* graph_02 = new UGraph<Char>();
  
  VA = graph_02->insertVertex(A);
  VB = graph_02->insertVertex(B);
  VC = graph_02->insertVertex(C);    
  VK = graph_02->insertVertex(K);
  VL = graph_02->insertVertex(L);
  VM = graph_02->insertVertex(M);    
  VS = graph_02->insertVertex(S);
  VR = graph_02->insertVertex(R);

  // connect the graph
  //
  graph_02->insertArc(graph_02->getStart(), VA, true);  
  graph_02->insertArc(VA, VA, false, 0.3);
  graph_02->insertArc(VA, VR, false, 0.3);
  graph_02->insertArc(VA, VB, true);
  graph_02->insertArc(VA, VC, true);  
  graph_02->insertArc(VB, VL, false, 0.2);
  graph_02->insertArc(VC, VK, false, 3.6);
  graph_02->insertArc(VM, VK, false, 0.6);
  graph_02->insertArc(VM, VA, false, -1.2);
  graph_02->insertArc(VL, VR, false, -0.7);
  graph_02->insertArc(VL, VC, false, 0.4);
  graph_02->insertArc(VS, VK, false, -0.2);
  graph_02->insertArc(VS, VS, false, -0.6);    
  graph_02->insertArc(VR, graph_02->getTerm(), true);
  
  // graph_00 and graph_01 should be equal
  //
  if (!graph_00->eq(*graph_01)) {
    return Error::handle(name(), L"eq", Error::TEST, __FILE__, __LINE__);
  }

  // graph_00 and graph_01 should not be equal
  //
  if (graph_00->eq(*graph_02)) {
    return Error::handle(name(), L"eq", Error::TEST, __FILE__, __LINE__);
  }  

  // delete the character objects
  //
  delete A;
  delete B;
  delete C;
  delete K;
  delete L;
  delete M;
  delete S;
  delete R;
  
  // clean up memory
  //
  graph_00->clear(Integral::FREE);
  graph_01->clear(Integral::FREE);
  graph_02->clear(Integral::FREE);

  delete graph_00;
  delete graph_01;
  delete graph_02;

  {
    // test the equality method in USER-allocated mode
    //
    
    // -------------------------------------------------
    // START    - A 
    // A -(0.3) - A
    //   -(0.3) - R
    //   -(epi) - B
    //   -(epi) - C
    // B -(0.2) - L
    // C -(3.6) - K
    // M -(0.6) - K
    // M -(1.2) - A
    // L -(-0.7)- R
    // L -(0.4) - C
    // S -(-0.2)- K
    // S -(-0.6)- S
    // R        - TERM 
    // -------------------------------------------------  
    
    // create the graph items
    //
    Char* A = new Char('A');
    Char* B = new Char('B');
    Char* C = new Char('C');
    Char* K = new Char('K');
    Char* L = new Char('L');
    Char* M = new Char('M');
    Char* S = new Char('S');  
    Char* R = new Char('R');
    
    // create the graph
    //
    UGraph<Char>* graph_00 = new UGraph<Char>(USER);
    
    // create the verteices
    //
    GraphVertex<Char>* VA = graph_00->insertVertex(A);
    GraphVertex<Char>* VB = graph_00->insertVertex(B);
    GraphVertex<Char>* VC = graph_00->insertVertex(C);    
    GraphVertex<Char>* VK = graph_00->insertVertex(K);
    GraphVertex<Char>* VL = graph_00->insertVertex(L);
    GraphVertex<Char>* VM = graph_00->insertVertex(M);    
    GraphVertex<Char>* VS = graph_00->insertVertex(S);
    GraphVertex<Char>* VR = graph_00->insertVertex(R);
    
    // connect the graph
    //
    graph_00->insertArc(graph_00->getStart(), VA, true);    
    graph_00->insertArc(VA, VA, false, 0.3);
    graph_00->insertArc(VA, VR, false, 0.3);
    graph_00->insertArc(VA, VB, true);
    graph_00->insertArc(VA, VC, true);  
    graph_00->insertArc(VB, VL, false, 0.2);
    graph_00->insertArc(VC, VK, false, 3.6);
    graph_00->insertArc(VM, VK, false, 0.6);
    graph_00->insertArc(VM, VA, false, 1.2);
    graph_00->insertArc(VL, VR, false, -0.7);
    graph_00->insertArc(VL, VC, false, 0.4);
    graph_00->insertArc(VS, VK, false, -0.2);
    graph_00->insertArc(VS, VS, false, -0.6);
    graph_00->insertArc(VR, graph_00->getTerm(), true);  
    
    // create the graph
    //
    UGraph<Char>* graph_01 = new UGraph<Char>(*graph_00);
    
    // create the graph
    //
    UGraph<Char>* graph_02 = new UGraph<Char>(USER);
    
    VA = graph_02->insertVertex(A);
    VB = graph_02->insertVertex(B);
    VC = graph_02->insertVertex(C);    
    VK = graph_02->insertVertex(K);
    VL = graph_02->insertVertex(L);
    VM = graph_02->insertVertex(M);    
    VS = graph_02->insertVertex(S);
    VR = graph_02->insertVertex(R);
    
    // connect the graph
    //
    graph_02->insertArc(graph_02->getStart(), VA, true);  
    graph_02->insertArc(VA, VA, false, 0.3);
    graph_02->insertArc(VA, VR, false, 0.3);
    graph_02->insertArc(VA, VB, true);
    graph_02->insertArc(VA, VC, true);  
    graph_02->insertArc(VB, VL, false, 0.2);
    graph_02->insertArc(VC, VK, false, 3.6);
    graph_02->insertArc(VM, VK, false, 0.6);
    graph_02->insertArc(VM, VA, false, -1.2);
    graph_02->insertArc(VL, VR, false, -0.7);
    graph_02->insertArc(VL, VC, false, 0.4);
    graph_02->insertArc(VS, VK, false, -0.2);
    graph_02->insertArc(VS, VS, false, -0.6);
    graph_02->insertArc(VR, graph_02->getTerm(), true);    
    
    // graph_00 and graph_01 should be equal
    //
    if (!graph_00->eq(*graph_01)) {
      return Error::handle(name(), L"eq", Error::TEST, __FILE__, __LINE__);
    }
    
    // graph_00 and graph_01 should not be equal
    //
    if (graph_00->eq(*graph_02)) {
      return Error::handle(name(), L"eq", Error::TEST, __FILE__, __LINE__);
    }  
    
    // clean up memory
    //
    graph_00->clear(Integral::FREE);
    graph_01->clear();
    graph_02->clear();

    delete graph_00;
    delete graph_01;
    delete graph_02;    
  }
  
  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }

  // --------------------------------------------------------------------
  //
  // 2. class-specific public methods:
  //     graph manipulation methods
  //
  // --------------------------------------------------------------------

  // set indentation
  //
  if (level_a > Integral::NONE) {
    Console::put(L"testing class-specific public methods: graph manipulation methods...\n");
    Console::increaseIndention(); 
  }

  // -------------------------------------------------
  // START -(epi)- T 
  // T     -(0.3)- W
  // W     -(epi)- TERM 
  // -------------------------------------------------
  
  // test insert arc methods in SYSTEM-allocation mode
  //
  UGraph<Char>* graph_03 = new UGraph<Char>();

  // create some vertices
  //
  Char* T = new Char('T');
  Char* W = new Char('W');  
  GraphVertex<Char>* VT = graph_03->insertVertex(T);
  GraphVertex<Char>* VW = graph_03->insertVertex(W);  

  // insert an arc for the start vertex to 'T'
  //
  graph_03->insertArc(graph_03->getStart(), VT, true);

  // insert an arc from 'T' to 'W' with a weight of -0.3
  //
  graph_03->insertArc(VT, VW, false, -0.3);

  // insert an arc from 'W' to the term vertex
  //
  graph_03->insertArc(VW, graph_03->getTerm(), true);

  // the start vertex should have an epsilon transition to VT
  //
  GraphArc<TObject>* my_arc = graph_03->getStart()->getFirst();    
  if ((my_arc->getVertex()) != VT && (!my_arc->getEpsilon())) {
    return Error::handle(name(), L"insertArc", Error::TEST,
			 __FILE__, __LINE__);
  }

  // the vertex VT should have two arcs one to the start vertex and
  // the other to the terminal vertex
  //
  boolean more_nodes = graph_03->gotoFirst();
  
  while (more_nodes) {

    // VT should have a weighted arc to the term vertex
    //
    GraphVertex<TObject>* my_vertex = graph_03->getCurr();
    my_vertex->gotoFirst();

    if (my_vertex == VT) {

      // there must be two out-going arcs from the vertex
      //
      if (my_vertex->length() != (long)2) {
	return Error::handle(name(), L"insertArc", Error::TEST,
			     __FILE__, __LINE__);
      }

      // retrieve the first arc
      //
      GraphArc<TObject>* my_arc = my_vertex->getCurr();

      // if the arc is to the start vertex
      //
      if (my_arc->getVertex() == graph_03->getStart()) {

	if (!my_arc->getEpsilon()) {
	  return Error::handle(name(), L"insertArc", Error::TEST,
			       __FILE__, __LINE__);
	}

	// retrieve the second arc
	//	
	my_arc = my_vertex->getNext();
	
	if ((my_arc->getVertex() != VW) &&
	    (!Integral::almostEqual(my_arc->getWeight(), -0.3))) {
	  return Error::handle(name(), L"insertArc", Error::TEST,
			       __FILE__, __LINE__);
	}
      }
      
      // the arc must be to the VW vertex
      //      
      else {
	
	if ((my_arc->getVertex() != VW) &&
	    (!Integral::almostEqual(my_arc->getWeight(), -0.3))) {
	  return Error::handle(name(), L"insertArc", Error::TEST,
			       __FILE__, __LINE__);
	}

	// retrieve the second arc
	//	
	my_arc = my_vertex->getNext();

	if ((my_arc->getVertex() != graph_03->getStart()) &&
	    (!my_arc->getEpsilon())) {
	  return Error::handle(name(), L"insertArc", Error::TEST,
			       __FILE__, __LINE__);
	}
      }
    }

    if (my_vertex == VW) {

      // there must be two out-going arcs from the vertex
      //
      if (my_vertex->length() != (long)2) {
	return Error::handle(name(), L"insertArc", Error::TEST,
			     __FILE__, __LINE__);
      }

      // retrieve the first arc
      //
      GraphArc<TObject>* my_arc = my_vertex->getCurr();
      
      // if the arc is to the term vertex
      //
      if (my_arc->getVertex() == graph_03->getTerm()) {
	
	if (!my_arc->getEpsilon()) {
	  return Error::handle(name(), L"insertArc", Error::TEST,
			       __FILE__, __LINE__);
	}
	
	// retrieve the second arc
	//		
	my_arc = my_vertex->getNext();
	
	if ((my_arc->getVertex() != VT) &&
	    (!Integral::almostEqual(my_arc->getWeight(), -0.3))) {
	  return Error::handle(name(), L"insertArc", Error::TEST,
			       __FILE__, __LINE__);
	}
      }

      // the arc must be to the VT vertex
      //
      else {

	if ((my_arc->getVertex() != VT) &&
	    (!Integral::almostEqual(my_arc->getWeight(), -0.3))) {
	  return Error::handle(name(), L"insertArc", Error::TEST,
			       __FILE__, __LINE__);
	}

	// retrieve the second arc
	//		
	my_arc = my_vertex->getNext();

	if ((my_arc->getVertex() != graph_03->getTerm()) &&
	    (!my_arc->getEpsilon())) {
	  return Error::handle(name(), L"insertArc", Error::TEST,
			       __FILE__, __LINE__);
	}
      }
    }
    
    more_nodes = graph_03->gotoNext();
  }

  // clean up
  //
  delete T;
  delete W;
  graph_03->clear(Integral::FREE);
  delete graph_03;
  
  {
    // -------------------------------------------------
    // START -(epi)- T 
    // T     -(0.3)- W
    // W     -(epi)- TERM 
    // -------------------------------------------------
    
    // test insert arc methods in USER-allocation mode
    //
    UGraph<Char>* graph_03 = new UGraph<Char>(USER);
    
    // create some vertices
    //
    Char* T = new Char('T');
    Char* W = new Char('W');  
    GraphVertex<Char>* VT = graph_03->insertVertex(T);
    GraphVertex<Char>* VW = graph_03->insertVertex(W);  
    
    // insert an arc for the start vertex to 'T'
    //
    graph_03->insertArc(graph_03->getStart(), VT, true);
    
    // insert an arc from 'T' to 'W' with a weight of -0.3
    //
    graph_03->insertArc(VT, VW, false, -0.3);
    
    // insert an arc from 'W' to the term vertex
    //
    graph_03->insertArc(VW, graph_03->getTerm(), true);
    
    // the start vertex should have an epsilon transition to VT
    //
    GraphArc<TObject>* my_arc = graph_03->getStart()->getFirst();    
    if ((my_arc->getVertex()) != VT && (!my_arc->getEpsilon())) {
      return Error::handle(name(), L"insertArc", Error::TEST,
			   __FILE__, __LINE__);
    }
    
    // the vertex VT should have two arcs one to the start vertex and
    // the other to the terminal vertex
    //
    boolean more_nodes = graph_03->gotoFirst();
    
    while (more_nodes) {
      
      // VT should have a weighted arc to the term vertex
      //
      GraphVertex<TObject>* my_vertex = graph_03->getCurr();
      my_vertex->gotoFirst();
      
      if (my_vertex == VT) {

	// there must be two out-going arcs from the vertex
	//
	if (my_vertex->length() != (long)2) {
	  return Error::handle(name(), L"insertArc", Error::TEST,
			       __FILE__, __LINE__);
	}
	
	// retrieve the first arc
	//
	GraphArc<TObject>* my_arc = my_vertex->getCurr();
	
	// if the arc is to the start vertex
	//
	if (my_arc->getVertex() == graph_03->getStart()) {
	  
	  if (!my_arc->getEpsilon()) {
	    return Error::handle(name(), L"insertArc", Error::TEST,
				 __FILE__, __LINE__);
	  }
	  
	  // retrieve the second arc
	  //	
	  my_arc = my_vertex->getNext();
	  
	  if ((my_arc->getVertex() != VW) &&
	      (!Integral::almostEqual(my_arc->getWeight(), -0.3))) {
	    return Error::handle(name(), L"insertArc", Error::TEST,
				 __FILE__, __LINE__);
	  }
	}
	
	// the arc must be to the VW vertex
	//      
	else {
	  
	  if ((my_arc->getVertex() != VW) &&
	      (!Integral::almostEqual(my_arc->getWeight(), -0.3))) {
	    return Error::handle(name(), L"insertArc", Error::TEST,
				 __FILE__, __LINE__);
	  }
	  
	  // retrieve the second arc
	  //	
	  my_arc = my_vertex->getNext();
	  
	  if ((my_arc->getVertex() != graph_03->getStart()) &&
	      (!my_arc->getEpsilon())) {
	    return Error::handle(name(), L"insertArc", Error::TEST,
				 __FILE__, __LINE__);
	  }
	}
      }
      
      if (my_vertex == VW) {
	
	// there must be two out-going arcs from the vertex
	//
	if (my_vertex->length() != (long)2) {
	  return Error::handle(name(), L"insertArc", Error::TEST,
			     __FILE__, __LINE__);
	}
	
	// retrieve the first arc
	//
	GraphArc<TObject>* my_arc = my_vertex->getCurr();
	
	// if the arc is to the term vertex
	//
	if (my_arc->getVertex() == graph_03->getTerm()) {
	  
	  if (!my_arc->getEpsilon()) {
	    return Error::handle(name(), L"insertArc", Error::TEST,
				 __FILE__, __LINE__);
	  }
	  
	  // retrieve the second arc
	  //		
	  my_arc = my_vertex->getNext();
	  
	  if ((my_arc->getVertex() != VT) &&
	      (!Integral::almostEqual(my_arc->getWeight(), -0.3))) {
	    return Error::handle(name(), L"insertArc", Error::TEST,
				 __FILE__, __LINE__);
	  }
	}
	
	// the arc must be to the VT vertex
	//
	else {
	  
	  if ((my_arc->getVertex() != VT) &&
	      (!Integral::almostEqual(my_arc->getWeight(), -0.3))) {
	    return Error::handle(name(), L"insertArc", Error::TEST,
				 __FILE__, __LINE__);
	  }
	  
	  // retrieve the second arc
	  //		
	  my_arc = my_vertex->getNext();
	  
	  if ((my_arc->getVertex() != graph_03->getTerm()) &&
	      (!my_arc->getEpsilon())) {
	    return Error::handle(name(), L"insertArc", Error::TEST,
				 __FILE__, __LINE__);
	  }
	}
      }
      
      more_nodes = graph_03->gotoNext();
    }
    
    // clean up
    //
    graph_03->clear(Integral::FREE);
    delete graph_03;
  }

  // test the remove arc methods in SYSTEM-allocation mode
  //
  
  // -------------------------------------------------
  // START    - X 
  // X -(0.5) - Y
  // X        - TERM 
  // -------------------------------------------------  
  
  UGraph<Char>* graph_04 = new UGraph<Char>();

  // create some vertices
  //
  Char* X = new Char('X');
  Char* Y = new Char('Y');  

  GraphVertex<Char>* VX = graph_04->insertVertex(X);
  GraphVertex<Char>* VY = graph_04->insertVertex(Y);

  // insert an arc from the start to 'X' and from 'X' to term
  //
  graph_04->insertArc(graph_04->getStart(), VX, true);  
  graph_04->insertArc(VX, graph_04->getTerm(), true);

  // insert an arc from 'X' to 'Y'
  //
  graph_04->insertArc(VX, VY, false, 0.5);

  // 'X' should have three out going arcs to start, term and 'Y'
  //
  graph_04->gotoFirst();

  // the first vertex should be 'X'
  //
  if (graph_04->getCurr() != VX) {
    return Error::handle(name(), L"insertArc", Error::TEST,
			 __FILE__, __LINE__);
  }

  // the first arc should goto the start vertex with an epsilon transition
  //
  GraphArc<TObject>* test_arc;
  graph_04->getCurr()->gotoFirst();  
  test_arc = graph_04->getCurr()->getCurr();
  if ((test_arc->getVertex() != graph_04->getStart()) &&
      (!test_arc->getEpsilon())) {
    return Error::handle(name(), L"insertArc", Error::TEST,
			 __FILE__, __LINE__);
  }
  
  // the second arc should goto the term vertex with an epsilon transition
  //  
  graph_04->getCurr()->gotoNext();
  test_arc = graph_04->getCurr()->getCurr();
  if ((test_arc->getVertex() != graph_04->getTerm()) &&
      (!test_arc->getEpsilon())) {
    return Error::handle(name(), L"insertArc", Error::TEST,
			 __FILE__, __LINE__);
  }
  
  // the third arc should goto 'Y' with a weight of 0.5
  //
  graph_04->getCurr()->gotoNext();
  test_arc = graph_04->getCurr()->getCurr();    
  if ((test_arc->getVertex() != VY) &&
      (!Integral::almostEqual(test_arc->getWeight(), 0.5))) {
    return Error::handle(name(), L"insertArc", Error::TEST,
			 __FILE__, __LINE__);
  }

  // vertex 'Y' should have an arc to 'X' with a weight of 0.5
  //
  graph_04->gotoNext();
  graph_04->getCurr()->gotoFirst();
  test_arc = graph_04->getCurr()->getCurr();
  if ((test_arc->getVertex() != VX) &&
      (!Integral::almostEqual(test_arc->getWeight(), 0.5))) {
    return Error::handle(name(), L"insertArc", Error::TEST,
			 __FILE__, __LINE__);
  }
      
  // remove the arc from 'X' to 'Y'
  //
  graph_04->removeArc(VX, VY);

  // vertex 'X' should have two arcs to the start and term vertices
  //
  graph_04->gotoFirst();  
  if (graph_04->getCurr()->length() != 2) {
    return Error::handle(name(), L"removeArc", Error::TEST,
			 __FILE__, __LINE__);
  }

  // vertex 'Y' should have no arcs
  //  
  graph_04->gotoNext();  
  if (graph_04->getCurr()->length() != 0) {
    return Error::handle(name(), L"removeArc", Error::TEST,
			 __FILE__, __LINE__);
  }  

  // 'X' should only have arcs to the star and term vertices
  //
  graph_04->gotoFirst();
  graph_04->getCurr()->gotoFirst();

  test_arc = graph_04->getCurr()->getCurr();
  if ((test_arc->getVertex() != graph_04->getStart()) &&
      (!test_arc->getEpsilon())) {
    return Error::handle(name(), L"removeArc", Error::TEST,
			 __FILE__, __LINE__);
  }

  graph_04->getCurr()->gotoNext();
  test_arc = graph_04->getCurr()->getCurr();
  if ((test_arc->getVertex() != graph_04->getTerm()) &&
      (!test_arc->getEpsilon())) {
    return Error::handle(name(), L"removeArc", Error::TEST,
			 __FILE__, __LINE__);
  }  
    
  // clean up
  //
  delete X;
  delete Y;
  graph_04->clear(Integral::FREE);
  delete graph_04;  

  {
    // test the remove arc methods in USER-allocation mode
    //

    // -------------------------------------------------
    // START    - X 
    // X -(0.5) - Y
    // X        - TERM 
    // -------------------------------------------------  
    
    UGraph<Char>* graph_04 = new UGraph<Char>(USER);
    
    // create some vertices
    //
    Char* X = new Char('X');
    Char* Y = new Char('Y');  
    
    GraphVertex<Char>* VX = graph_04->insertVertex(X);
    GraphVertex<Char>* VY = graph_04->insertVertex(Y);
    
    // insert an arc from the start to 'X' and from 'X' to term
    //
    graph_04->insertArc(graph_04->getStart(), VX, true);  
    graph_04->insertArc(VX, graph_04->getTerm(), true);
    
    // insert an arc from 'X' to 'Y'
    //
    graph_04->insertArc(VX, VY, false, 0.5);
    
    // 'X' should have three out going arcs to start, term and 'Y'
    //
    graph_04->gotoFirst();
    
    // the first vertex should be 'X'
    //
    if (graph_04->getCurr() != VX) {
      return Error::handle(name(), L"insertArc", Error::TEST,
			 __FILE__, __LINE__);
    }
    
    // the first arc should goto the start vertex with an epsilon transition
    //
    GraphArc<TObject>* test_arc;
    graph_04->getCurr()->gotoFirst();  
    test_arc = graph_04->getCurr()->getCurr();
    if ((test_arc->getVertex() != graph_04->getStart()) &&
	(!test_arc->getEpsilon())) {
      return Error::handle(name(), L"insertArc", Error::TEST,
			   __FILE__, __LINE__);
    }
    
    // the second arc should goto the term vertex with an epsilon transition
    //  
    graph_04->getCurr()->gotoNext();
    test_arc = graph_04->getCurr()->getCurr();
    if ((test_arc->getVertex() != graph_04->getTerm()) &&
	(!test_arc->getEpsilon())) {
      return Error::handle(name(), L"insertArc", Error::TEST,
			   __FILE__, __LINE__);
    }
    
    // the third arc should goto 'Y' with a weight of 0.5
    //
    graph_04->getCurr()->gotoNext();
    test_arc = graph_04->getCurr()->getCurr();    
    if ((test_arc->getVertex() != VY) &&
	(!Integral::almostEqual(test_arc->getWeight(), 0.5))) {
      return Error::handle(name(), L"insertArc", Error::TEST,
			   __FILE__, __LINE__);
    }
    
    // vertex 'Y' should have an arc to 'X' with a weight of 0.5
    //
    graph_04->gotoNext();
    graph_04->getCurr()->gotoFirst();
    test_arc = graph_04->getCurr()->getCurr();
    if ((test_arc->getVertex() != VX) &&
	(!Integral::almostEqual(test_arc->getWeight(), 0.5))) {
      return Error::handle(name(), L"insertArc", Error::TEST,
			   __FILE__, __LINE__);
    }
    
    // remove the arc from 'X' to 'Y'
    //
    graph_04->removeArc(VX, VY);
    
    // vertex 'X' should have two arcs to the start and term vertices
    //
    graph_04->gotoFirst();  
    if (graph_04->getCurr()->length() != 2) {
      return Error::handle(name(), L"removeArc", Error::TEST,
			   __FILE__, __LINE__);
    }
    
    // vertex 'Y' should have no arcs
    //  
    graph_04->gotoNext();  
    if (graph_04->getCurr()->length() != 0) {
      return Error::handle(name(), L"removeArc", Error::TEST,
			   __FILE__, __LINE__);
    }  
    
    // 'X' should only have arcs to the star and term vertices
    //
    graph_04->gotoFirst();
    graph_04->getCurr()->gotoFirst();
    
    test_arc = graph_04->getCurr()->getCurr();
    if ((test_arc->getVertex() != graph_04->getStart()) &&
	(!test_arc->getEpsilon())) {
      return Error::handle(name(), L"removeArc", Error::TEST,
			   __FILE__, __LINE__);
    }
    
    graph_04->getCurr()->gotoNext();
    test_arc = graph_04->getCurr()->getCurr();
    if ((test_arc->getVertex() != graph_04->getTerm()) &&
	(!test_arc->getEpsilon())) {
      return Error::handle(name(), L"removeArc", Error::TEST,
			 __FILE__, __LINE__);
    }  
    
    // clean up
    //
    graph_04->clear(Integral::FREE);
    delete graph_04;
  }

  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }

  //---------------------------------------------------------------------
  //
  // 3. print completion message
  //
  //---------------------------------------------------------------------  

  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
  
  if (level_a > Integral::NONE) {
    SysString output(L"diagnostics passed for class ");
    output.concat(name());
    output.concat(L"\n");
    Console::put(output);
  }  
  
  // exit gracefully
  //
  return true;  
 }

// end of include file
//
#endif
