// file: $isip/class/dstr/GraphVertex/GraphVertexDiagnose.h
// version: $Id: GraphVertexDiagnose.h 10636 2007-01-26 22:18:09Z tm334 $
//

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

// isip include files
//
#ifndef ISIP_GRAPH_VERTEX
#include <GraphVertex.h>
#endif

// GraphVertexDiagnose: a class that contains the diagnose method of
// GraphVertex class.
//
template<class TObject>
class GraphVertexDiagnose : public GraphVertex<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:

  // method: name
  //
  static const String& name() {
    return GraphVertex<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 GraphVertexDiagnose 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 GraphVertexDiagnose<TObject>::diagnose(Integral::DEBUG level_a) {

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

  // output the class name
  //
  if (level_a > Integral::NONE) {
    SysString output(L"diagnosing class ");
    output.concat(GraphVertex<TObject>::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
  //
  
  if (level_a > Integral::BRIEF) {
    Integral::debug(L"debug");
  }

  // default constructor
  //
  GraphVertex<TObject> def_vertex;
  TObject item;
  def_vertex.setItem(&item);

  // copy constructor
  //
  GraphVertex<TObject> copy_vertex(def_vertex);

  // all of the constructed vertices should contain the same item now
  //
  if (def_vertex.item_d->ne(*copy_vertex.item_d)) {
    return Error::handle(name(), L"constructors", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  // check the constructors and destructors for allocating on the dynamic
  // memory heap
  //
  GraphVertex<TObject>* def_dyn_vert = new GraphVertex<TObject>();
  def_dyn_vert->setItem(&item);

  // copy constructor
  //
  GraphVertex<TObject>* copy_dyn_vert =
    new GraphVertex<TObject>(*def_dyn_vert);
  
  // all of the constructed nodes should contain the same item now
  //
  if (def_dyn_vert->item_d->ne(*copy_dyn_vert->item_d)) {
    return Error::handle(name(), L"constructors", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  // clear some vertices
  //
  def_dyn_vert->clear();
  def_vertex.clear();
  copy_dyn_vert->clear();
  copy_vertex.clear();
  
  // see if we can dynamically delete
  //
  delete def_dyn_vert;
  delete copy_dyn_vert;

  // check the constructors for allocating on the stack
  //
  GraphVertex<TObject> def_vert_2;
  def_vert_2.setItem(&item);
  
  GraphVertex<TObject> copy_vert_2(def_vert_2);            // copy constructor

  // all of the constructed nodes should contain the exact same item now
  //
  if (def_vert_2.item_d != copy_vert_2.item_d) {
    return Error::handle(name(), L"constructors", Error::TEST, __FILE__,
                         __LINE__);
  }

  // default constructor
  //
  GraphVertex<TObject>* def_dyn_vert_2 =
    new GraphVertex<TObject>(); 
  def_dyn_vert_2->setItem(&item);

  // copy constructor
  //
  GraphVertex<TObject>* copy_dyn_vert_2 =
    new GraphVertex<TObject>(*def_dyn_vert_2);

  // all of the constructed nodes should contain the same item now
  //
  if (def_dyn_vert_2->item_d != copy_dyn_vert_2->item_d) {
    return Error::handle(name(), L"constructors", Error::TEST, __FILE__,
                         __LINE__);
  }

  // clear some vertices
  //
  def_dyn_vert_2->clear();
  copy_vert_2.clear();
  
  // see if we can dynamically delete
  //
  delete def_dyn_vert_2;
  delete copy_dyn_vert_2;

  // test large allocation construction and deletion
  //
  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
    //
    GraphVertex<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++) {
      GraphVertex<TObject>** self_verts = new GraphVertex<TObject>*[j * 100];
      GraphVertex<TObject>** verts = new GraphVertex<TObject>*[j * 100];
      
      // create the vertices
      //
      for (long i = 0; i < j * 100; i++) {
	self_verts[i] = new GraphVertex<TObject>();
	verts[i] = new GraphVertex<TObject>();
      }
      
      // delete nodes
      //
      for (long i = (j * 100) - 1; i >= 0; i--) {
	delete self_verts[i];
	delete verts[i];
      }
      
      delete [] self_verts;
      delete [] verts;
    }

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

      // clean up memory
      //
      delete [] self_verts;
      delete [] verts;
    }
  }

  // test assign methods
  //
  TObject item0;
  TObject item1;
  
  GraphVertex<TObject> tmp_vert;
  GraphVertex<TObject>* tmp_dyn_vert = new GraphVertex<TObject>();
  
  // set the items
  //
  tmp_vert.setItem(&item0);
  tmp_dyn_vert->setItem(&item1);

  // try the copy assign
  //
  tmp_vert.assign(*tmp_dyn_vert);
  
  if ((tmp_vert.item_d->ne(item1)) || (tmp_dyn_vert->item_d->ne(item1))) {
    return Error::handle(name(), L"vertex assign", Error::TEST,
			 __FILE__, __LINE__);
  }
  
  // clean up
  //
  delete tmp_dyn_vert;
  
  // testing remaining required methods
  //
  Char chr_item_0(L'A');
  GraphVertex<Char> dbg_vert;
  dbg_vert.setItem(&chr_item_0);
  if (level_a >= Integral::ALL) {
    dbg_vert.debug(L"testing debug method");
  }

  // create two vertices
  //
  GraphVertex<Char> eq_vert_00;
  GraphVertex<Char> eq_vert_10;
  
  // create a character
  //
  Char eq_char(L'a');
  
  // set the first vertex
  //
  eq_vert_00.setItem(&eq_char);
  
  // assign the second item
  //
  eq_vert_10.assign(eq_vert_00);
  
  // call the eq method
  //
  if (!eq_vert_00.eq(eq_vert_10)) {
    return Error::handle(name(), L"eq", Error::TEST, __FILE__, __LINE__);
  }
  
  // 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(); 
  }
    
  // test item, arc, and parent graph manipulations
  //
  Char* chars[128];
  for (long i = 0; i < 128; i++) {
    chars[i] = new Char((unichar)i);
  }
  
  // create two graphs to manipulate
  //
  DiGraph<Char> graph1(DstrBase::USER);
  DiGraph<Char> graph2(DstrBase::USER);

  // create an array of vertices to test
  //
  GraphVertex<Char>* vertices[128];
  for (long i = 0; i < 128; i++) {

    vertices[i] = new GraphVertex<Char>();

    // set the item in each vertex
    //
    vertices[i]->setItem(chars[i]);
  }

  // make sure the setParentGraph and getParentGraph methods work
  //
  // assign one of the two graphs to each node
  //
  for (long i = 0; i < 64; i++) {
    if (!vertices[i]->setParentGraph(&graph1)) {
      return Error::handle(name(), L"setParentGraph", Error::TEST,
			   __FILE__, __LINE__);
    }
  }
  for (long i = 64; i < 128; i++) {
    if (!vertices[i]->setParentGraph(&graph2)) {
      return Error::handle(name(), L"setParentGraph", Error::TEST,
			   __FILE__, __LINE__);
    }
  }

  // make sure that the proper graph was assigned
  //
  for (long i = 0; i < 64; i++) {
    if ((vertices[i]->getParentGraph() != &graph1) ||
	(vertices[128 - i - 1]->getParentGraph() != &graph2) ||
	(vertices[i]->getParentGraph() ==
	 vertices[128 - i - 1]->getParentGraph())) {

      return Error::handle(name(), L"getParentGraph", Error::TEST,
			   __FILE__, __LINE__);
    }
  }

  // start building a binary tree graph where the first (left) child of each
  // vertex is the (2*i + 1) element of the character array and the
  // second (right) child is the (2*i + 2) element of the character array
  //
  // create the first vertex
  //
  vertices[0]->setItem(chars[0]);

  // set the graph characteristics
  //
  graph1.setWeighted();

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

  // --------------------------------------------------------------------
  //
  // 3. class-specific public methods:
  //     vertex manipuation methods
  //
  // --------------------------------------------------------------------

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

  // attach this as the first node in the graph
  //
  graph1.getStart()->insertArc(vertices[0], 0.0, false);

  // now loop over the remaining vertices - the tree will be built when we
  // get to 63. the 63rd vertex will only have one child (the root node
  // takes one vertex)
  //
  for (long i = 0; i < 63; i++) {
    vertices[i]->insertArc(vertices[2 * i + 1], (float)i, false);
    vertices[i]->insertArc(vertices[2 * i + 2], (float)i, false);    
  }
  vertices[63]->insertArc(vertices[127], (float)127, false);

  // reverse engineer the binary tree to make sure it is correct
  //
  graph1.getStart()->gotoFirst();

  // get the first element and make sure it is the proper root vertex
  //
  if (!(graph1.getStart()->isAdjacent(vertices[0])) ||
      (graph1.getStart()->getCurr()->getVertex() != vertices[0])) {
    return Error::handle(name(), L"insertArc", Error::TEST,
			 __FILE__, __LINE__);
  }

  // the leaf vertices should be empty
  //
  for (long i = 127; i >= 64; --i) {
    if (!vertices[i]->isEmpty()) {
      return Error::handle(name(), L"insertArc", Error::TEST,
			   __FILE__, __LINE__);
    }
  }

  // handle the 63rd item that has only one child vertex
  //
  if (vertices[63]->getCurr()->getVertex() != vertices[2 * 63 + 1]) {
    return Error::handle(name(), L"insertArc", Error::TEST,
			 __FILE__, __LINE__);
  }

  // remove the first arc
  //
  if (!vertices[63]->removeArc()) {
    return Error::handle(name(), L"removeArc", Error::TEST,
			 __FILE__, __LINE__);
  }

  // the remaining vertices should have the appropriate child vertices in the
  // appropriate order and with the appropriate weight
  //
  for (long i = 62; i >= 0; --i) {

    // go to the front of the list
    //
    vertices[i]->gotoFirst();

    // the first vertex should point to the (2*i + 1) element
    //
    if (vertices[i]->getCurr()->getVertex() != vertices[2 * i + 1]) {

      return Error::handle(name(), L"insertArc", Error::TEST,
			   __FILE__, __LINE__);
    }

    // remove the first arc
    //
    if (!vertices[i]->removeArc()) {
      return Error::handle(name(), L"removeArc", Error::TEST,
			   __FILE__, __LINE__);
    }
    
    // the second vertex should point to the (2*i + 2) element
    //
    if (vertices[i]->getCurr()->getVertex() != vertices[2 * i + 2]) {

      return Error::handle(name(), L"insertArc", Error::TEST,
			   __FILE__, __LINE__);
    }

    // remove the second arc
    //
    if (!vertices[i]->removeArc()) {
      return Error::handle(name(), L"removeArc", Error::TEST,
			   __FILE__, __LINE__);
    }

    // make sure the list is now empty
    //
    if (!vertices[i]->isEmpty()) {
      return Error::handle(name(), L"removeArc", Error::TEST,
			   __FILE__, __LINE__);
    }
  }

  // the root vertex should still be attached to the start vertex of the
  // graph
  //
  if (graph1.getStart()->getCurr()->getVertex() != vertices[0]) {
      return Error::handle(name(), L"insertArc/removeArc", Error::TEST,
			   __FILE__, __LINE__);
  }

  // disconnect the root vertex
  //
  if (!graph1.getStart()->removeArc()) {
    return Error::handle(name(), L"removeArc", Error::TEST,
			 __FILE__, __LINE__);
  }

  // the graph start vertex should now be empty
  //
  if (!graph1.getStart()->isEmpty()) {
    return Error::handle(name(), L"removeArc", Error::TEST,
			 __FILE__, __LINE__);
  }

  // now rebuild the same binary tree so we can remove arcs a different
  // way
  //

  // attach this as the first node in the graph
  //
  graph1.getStart()->insertArc(vertices[0], 0.0, false);

  // now loop over the remaining vertices - the tree will be built when we
  // get to 63. the 63rd vertex will only have one child (the root node
  // takes one vertex)
  //
  for (long i = 0; i < 63; i++) {
    vertices[i]->insertArc(vertices[2 * i + 1], (float)i, false);
    vertices[i]->insertArc(vertices[2 * i + 2], (float)i, false);    
  }
  vertices[63]->insertArc(vertices[127], (float)127, false);

  // reverse engineer the binary tree to make sure it is correct
  //
  graph1.getStart()->gotoFirst();

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

  // --------------------------------------------------------------------
  //
  // 4. class-specific public methods:
  //     item manipulation methods
  //
  // --------------------------------------------------------------------

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

  // get the first element and make sure it is the proper root vertex
  //
  if (!(graph1.getStart()->isAdjacent(vertices[0])) ||
      (graph1.getStart()->getCurr()->getVertex() != vertices[0]) ||
      (graph1.getStart()->getCurr()->
       getVertex()->getItem()->ne(*chars[0]))) {
    return Error::handle(name(), L"insertArc", Error::TEST,
			 __FILE__, __LINE__);
  }

  // test whether the start vertex is start
  //
  if (!(graph1.getStart()->isStart())) {
    return Error::handle(name(), L"isStart", Error::TEST,
			 __FILE__, __LINE__);
  }

  // test whether the terminal vertex is terminal
  //
  if (!(graph1.getTerm()->isTerm())) {
    return Error::handle(name(), L"isTerm", Error::TEST,
			 __FILE__, __LINE__);
  }

  // test whether all other vertices are not start or terminal
  //
  for (long i = 0; i < 128; i++) {
    if (vertices[i]->isStart() || vertices[i]->isTerm()) {
      return Error::handle(name(), L"isStart or isTerm", Error::TEST,
			   __FILE__, __LINE__);
    }
  }

  // the leaf vertices should be empty
  //
  for (long i = 127; i >= 64; --i) {
    if (!vertices[i]->isEmpty()) {
      return Error::handle(name(), L"insertArc", Error::TEST,
			   __FILE__, __LINE__);
    }
  }

  // handle the 63rd item that has only one child vertex
  //
  if (vertices[63]->getCurr()->getVertex() != vertices[2 * 63 + 1]) {
    return Error::handle(name(), L"insertArc", Error::TEST,
			 __FILE__, __LINE__);
  }
  
  // remove the first arc
  //
  if (!vertices[63]->removeArc()) {
    return Error::handle(name(), L"removeArc", Error::TEST,
			 __FILE__, __LINE__);
  }

  // the remaining vertices should have the appropriate child vertices in the
  // appropriate order and with the appropriate weight
  //
  for (long i = 62; i >= 32; --i) {

    // go to the front of the list
    //
    vertices[i]->gotoFirst();

    // the first vertex should point to the (2*i + 1) element
    //
    if (vertices[i]->getCurr()->getVertex() != vertices[2 * i + 1]) {

      return Error::handle(name(), L"insertArc", Error::TEST,
			   __FILE__, __LINE__);
    }

    // move to the next arc
    //
    vertices[i]->gotoNext();

    // the second vertex should point to the (2*i + 2) element
    //
    if (vertices[i]->getCurr()->getVertex() != vertices[2 * i + 2]) {

      return Error::handle(name(), L"insertArc", Error::TEST,
			   __FILE__, __LINE__);
    }

    // remove all arcs from this vertex
    //
    if (!vertices[i]->removeAllArcs()) {
      return Error::handle(name(), L"removeAllArcs", Error::TEST,
			   __FILE__, __LINE__);
    }
    if (!vertices[i]->isEmpty()) {
      return Error::handle(name(), L"removeAllArcs", Error::TEST,
			   __FILE__, __LINE__);
    }
  }

  // the remaining vertices should have the appropriate child vertices in the
  // appropriate order and with the appropriate weight
  //
  for (long i = 31; i >= 0; --i) {

    // go to the front of the list
    //
    vertices[i]->gotoFirst();

    // the first vertex should point to the (2*i + 1) element
    //
    if (vertices[i]->getCurr()->getVertex() != vertices[2 * i + 1]) {

      return Error::handle(name(), L"insertArc", Error::TEST,
			   __FILE__, __LINE__);
    }

    // move to the next vertex
    //
    vertices[i]->gotoNext();
    
    // the second vertex should point to the (2*i + 2) element
    //
    if (vertices[i]->getCurr()->getVertex() != vertices[2 * i + 2]) {

      return Error::handle(name(), L"insertArc", Error::TEST,
			   __FILE__, __LINE__);
    }

    // remove the arcs in turn
    //
    if (!vertices[i]->removeArc(vertices[2 * i + 2])) {
      return Error::handle(name(), L"removeArc", Error::TEST,
			   __FILE__, __LINE__);
    }
    
    if (!vertices[i]->removeArc(vertices[2 * i + 1])) {
      return Error::handle(name(), L"removeArc", Error::TEST,
			   __FILE__, __LINE__);
    }
    
    // make sure the list is now empty
    //
    if (!vertices[i]->isEmpty()) {
      return Error::handle(name(), L"removeArc", Error::TEST,
			   __FILE__, __LINE__);
    }
  }

  // the root vertex should still be attached to the start vertex of the
  // graph
  //
  if (graph1.getStart()->getCurr()->getVertex() != vertices[0]) {
      return Error::handle(name(), L"insertArc/removeArc", Error::TEST,
			   __FILE__, __LINE__);
  }

  // disconnect the root vertex
  //
  if (!graph1.getStart()->removeArc()) {
    return Error::handle(name(), L"removeArc", Error::TEST,
			 __FILE__, __LINE__);
  }

  // the graph start vertex should now be empty
  //
  if (!graph1.getStart()->isEmpty()) {
    return Error::handle(name(), L"removeArc", Error::TEST,
			 __FILE__, __LINE__);
  }

  // clean up the memory
  //
  for (long i = 0; i < 128; i++) {
    delete chars[i];
    delete vertices[i];
  }

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

  //---------------------------------------------------------------------
  //
  // 7. 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
