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

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

// isip include files
//
#ifndef ISIP_DOUBLE_LINKED_LIST
#include "Set.h"
#endif

// SetDiagnose: a class that contains the diagnose method
// of Set class.
//
template<class TObject>
class SetDiagnose : public Set<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 Set<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 SetDiagnose 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 SetDiagnose<TObject>::diagnose(Integral::DEBUG level_a) {

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

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

  // prepare items for all the lists used in this diagnose 
  //
  long num_elem = 9;
  Char** items = new Char*[num_elem];
  
  for (long i = 0; i < 3; i++) {
    for (long j = 0; j < 3; j++) {
      items[i * 3 + j] = new Char((unichar)((long)'a' + j * 3 + (2 - i)));
    }
  }

  // test constructors and memory management
  //
  Set<Char> def_list;
  for (long i = 0; i < 9; i++) {
    def_list.insert(items[i]);
  }

  // copy constructor
  //
  Set<Char> copy_list(def_list);     
  
  // the two constructed lists should have the same items in the nodes now
  //
  if (def_list.ne(copy_list)) {
    return Error::handle(name(), L"copy constructor", Error::TEST, __FILE__,
			 __LINE__);
  }

  // print out the list
  //
  if (level_a >= Integral::ALL) {
    def_list.debug(L"def_list");
  }
  
  // check the constructors and destructors for allocating on the dynamic
  // memory heap
  //
  Set<Char>* def_dyn_list = new Set<Char>;
  def_dyn_list->assign(def_list);
  
  Set<Char>* copy_dyn_list =
    new Set<Char>(*def_dyn_list);
  
  // the two constructed lists should have the same items in their nodes now
  //
  if (def_dyn_list->ne(copy_list)) {
    return Error::handle(name(), L"copy constructor", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  // see if we can dynamically delete
  //
  delete def_dyn_list;
  delete copy_dyn_list;
  
  // when memory is USER-allocated
  //
  
  // check the constructors for allocating on the stack
  //
  Set<Char> def_list_2;
  def_list_2.setAllocationMode(DstrBase::USER);

  for (long i = 0; i < 9; i++) {
    def_list_2.insert(items[i]);
  }

  Set<Char> copy_list_2(def_list_2);
  
  // the two constructed lists should have the same items in the nodes now
  //
  if (def_list_2.ne(copy_list_2)) {
    return Error::handle(name(), L"copy constructor", Error::TEST, __FILE__,
			 __LINE__);
  }

  if (def_list_2.getAllocationMode() != copy_list_2.getAllocationMode()) {
    return Error::handle(name(), L"copy constructor", Error::TEST, __FILE__, __LINE__);
  }
  
  // check the constructors and destructors for allocating on the dynamic
  // memory heap
  //
  Set<Char>* def_dyn_list_2 =
    new Set<Char>(DstrBase::USER);
  
  for (long i = 0; i < 9; i++) {
    def_dyn_list_2->insert(items[i]);
  }
  
  Set<Char>* copy_dyn_list_2 =
    new Set<Char>(*def_dyn_list_2);
  
  // the two constructed lists should have the same items in their nodes now
  //
  if (def_dyn_list_2->ne(copy_list_2)) {
    return Error::handle(name(), L"copy constructor", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  // see if we can dynamically delete
  //
  delete def_dyn_list_2;
  delete copy_dyn_list_2;
  
  // test assign methods
  //  
  Set<Char> tmp_list;         
  Set<Char>* tmp_dyn_list = new Set<Char>();

  Set<Char> tmp_list_1(DstrBase::USER); 
  Set<Char>* tmp_dyn_list_1 = new Set<Char>(DstrBase::USER);

  // insert an item into the list
  //
  for (long i = 0; i < 9; i++) {
    tmp_list.insert(items[i]);
    tmp_list_1.insert(items[i]);
  }

  // try the list assign method
  //
  tmp_dyn_list->assign(tmp_list);
  tmp_dyn_list_1->assign(tmp_list_1);
  
  if (tmp_dyn_list->ne(tmp_list) || tmp_dyn_list->ne(tmp_list_1)) {
    return Error::handle(name(), L"list assign", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  if (tmp_dyn_list_1->ne(tmp_list) || tmp_dyn_list_1->ne(tmp_list_1)) {
    return Error::handle(name(), L"list assign", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  // clean up
  //
  delete tmp_dyn_list;
  delete tmp_dyn_list_1;
  
  // testing i/o methods
  //
  String text_filename;
  Integral::makeTemp(text_filename);
  String bin_filename;
  Integral::makeTemp(bin_filename);
  
  // open files in write mode
  //
  Sof text_file;
  text_file.open(text_filename, File::WRITE_ONLY, File::TEXT);
  Sof bin_file;
  bin_file.open(bin_filename, File::WRITE_ONLY, File::BINARY);
  
  // prepare items for the lists
  //
  Char** write_chars = new Char*[5];
  String** write_strings = new String*[5];
  
  unichar tmp_char = L'a';
  
  for (long j = 0; j < 5; j++) {
    write_chars[j] = new Char(tmp_char);
    write_strings[j] = new String();
    tmp_char++;
  }
  
  write_strings[0]->assign(L"this ");
  write_strings[1]->assign(L"is");
  write_strings[2]->assign(L"a");
  write_strings[3]->assign(L"Set");
  write_strings[4]->assign(L"String");
  
  // create lists to write
  //
  Set<Char> write_null_list;
  Set<Char> write_char_list(DstrBase::USER);

  write_char_list.insert(write_chars[0]);
  
  Set<String> write_str_list(DstrBase::USER);

  for (long i = 0; i < 5; i++) {
    write_str_list.insert(write_strings[i]);
  }
  
  // create objects for reading in, all read lists are SYSTEM-allocated
  //
  Set<Char> read_null_list_text;
  Set<Char> read_null_list_bin;
  Set<Char> read_char_list_text;
  Set<Char> read_char_list_bin;
  Set<String> read_str_list_text;
  Set<String> read_str_list_bin;
  
  
  // create items for writing
  //
  
  // write the values
  //
  write_null_list.write(text_file, (long)11);
  write_null_list.write(bin_file, (long)11);

  write_char_list.write(text_file, (long)0);
  write_char_list.write(bin_file, (long)0);  
  
  write_str_list.write(text_file, (long)0);
  write_str_list.write(bin_file, (long)0); 
  
  // close the files
  //
  text_file.close();
  bin_file.close();
  
  // open the files in read mode
  //
  text_file.open(text_filename);
  bin_file.open(bin_filename);
  
  // read in the lists and test for equivalence
  // if there is error, print out the lists
  //
  if (!read_null_list_text.read(text_file, (long)11) ||
      (read_null_list_text.ne(write_null_list))) {
    read_null_list_text.debug(L"read_null_list_text");
    return Error::handle(name(), L"read null text", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  if (!read_null_list_bin.read(bin_file, (long)11) ||
      (read_null_list_bin.ne(write_null_list))) {
    read_null_list_bin.debug(L"read_null_list_bin");
    return Error::handle(name(), L"read null bin", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  if (!read_char_list_text.read(text_file, (long)0) ||
      (read_char_list_text.ne(write_char_list))) {

    read_char_list_text.debug(L"read_char_list_text");
    return Error::handle(name(), L"read char text", Error::TEST, __FILE__,
			 __LINE__);
  }

  if (!read_char_list_bin.read(bin_file, (long)0) ||
      (read_char_list_bin.ne(write_char_list))) {
    read_char_list_bin.debug(L"read_char_list_bin");
    return Error::handle(name(), L"write char bin", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  if (!read_str_list_text.read(text_file, (long)0) ||
      (read_str_list_text.ne(write_str_list))) {
    read_str_list_text.debug(L"read_str_list_text");
    return Error::handle(name(), L"read str text", Error::TEST, __FILE__, 
			 __LINE__);
  }
  
  if (!read_str_list_bin.read(bin_file, (long)0) ||
      (read_str_list_bin.ne(write_str_list))) {
    read_str_list_bin.debug(L"read_str_list_bin");
    return Error::handle(name(), L"write str bin", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  // close and delete the temporary files
  //
  text_file.close();
  bin_file.close();
  File::remove(text_filename);
  File::remove(bin_filename);
  
  // clean the memory that was created by the Set read and write
  //
  read_char_list_text.clear();
  read_char_list_bin.clear();
  read_str_list_text.clear();
  read_str_list_bin.clear();

  for (long i = 0; i < 5; i++) {
    delete write_chars[i];
    delete write_strings[i];
  }
  delete [] write_chars;
  delete [] write_strings;
  
  // testing equality methods
  //
  Set<Char>* char_list = new Set<Char>;
  
  Char* characters = new Char[500];
  unichar temp_char = L'a';
  
  // initialize the characters and store them in the lists
  //
  for (long i = 0; i < 500; i++) {
    characters[i].assign(temp_char);
    
    // insert the character to the list
    //
    char_list->insert(&characters[i]);
    
    // increment the character value
    //
    temp_char++;
  }
  
  // self-allocated
  //
  Set<Char>* char_copy_list_1 = new Set<Char>();
  char_copy_list_1->assign(*char_list);
  
  // not self-allocated
  //
  Set<Char>* char_copy_list_2 =
    new Set<Char>(DstrBase::USER);
  char_copy_list_2->assign(*char_list);
  
  // char_copy_list_1 and char_copy_list_2 should be the same as the
  // char_list
  //
  if (!char_list->eq(*char_copy_list_1)) {
    return Error::handle(name(), L"eq", Error::TEST, __FILE__, __LINE__);
  }
  
  if (char_list->ne(*char_copy_list_1)) {
    return Error::handle(name(), L"ne", Error::TEST, __FILE__, __LINE__);
  }
  
  if (!char_list->eq(*char_copy_list_2)) {
    return Error::handle(name(), L"eq", Error::TEST, __FILE__, __LINE__);
  }
  
  if (char_list->ne(*char_copy_list_2)) {
    return Error::handle(name(), L"ne", Error::TEST, __FILE__, __LINE__);
  }
  
  // clean up
  //
  delete char_list;
  delete char_copy_list_1;
  delete char_copy_list_2;
  delete [] characters;

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

  // --------------------------------------------------------------------
  //
  // 2. class-specific public methods:
  //     positioning methods
  //
  // --------------------------------------------------------------------

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

  // test positioning, node and data accessing methods
  //
  Set<Char> tmp_list_2;
  
  for (long i = 0; i < 9; i++) {
    tmp_list_2.insert(items[i]);
  }
  
  // goto the first node
  //
  if (!tmp_list_2.gotoFirst()) {
    return Error::handle(name(), L"gotoFirst", Error::TEST, __FILE__,
			 __LINE__);
  }

  // make sure this is the first node
  //
  if (!tmp_list_2.isFirst()) {
    return Error::handle(name(), L"isFirst", Error::TEST, __FILE__, __LINE__);
  }

  // make sure this is not the last node
  //
  if (tmp_list_2.isLast()) {
    return Error::handle(name(), L"isLast", Error::TEST, __FILE__, __LINE__);
  }

  if (tmp_list_2.first_d->getItem()->ne(*items[6]) ||
      tmp_list_2.curr_d->getItem()->ne(*items[6])) {
    return Error::handle(name(), L"gotoFirst", Error::TEST, __FILE__,
			 __LINE__);
  }

  // goto the next node
  //
  if (!tmp_list_2.gotoNext()) {
    return Error::handle(name(), L"gotoNext", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  if (tmp_list_2.curr_d->getItem()->ne(*items[3])) {
    return Error::handle(name(), L"gotoNext", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  // is mark_d set?
  //
  if (tmp_list_2.markIsSet()) {
    return Error::handle(name(), L"markIsSet", Error::TEST, __FILE__,
			 __LINE__);
  }

  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
  
  //--------------------------------------------------------------------------
  //
  // 3. class-specific public methods:
  //     marking methods
  //
  //--------------------------------------------------------------------------

  // set indentation
  //
  if (level_a > Integral::NONE) {
    Console::put(L"testing class-specific public methods: marking methods...\n");
    Console::increaseIndention();
  }
  
  // set a mark here - the second node 
  //
  tmp_list_2.setMark();

  
  if (tmp_list_2.mark_d->getItem()->ne(*items[3])) {
    return Error::handle(name(), L"setMark", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  
  if (!tmp_list_2.isMarkedElement()) {
    return Error::handle(name(), L"isMarkedElement", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  if (!tmp_list_2.markIsSet()) {
    return Error::handle(name(), L"markIsSet", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  // goto the last node
  //
  if (!tmp_list_2.gotoLast()) {
    return Error::handle(name(), L"gotoLast", Error::TEST, __FILE__,
			 __LINE__);
  }

  // make sure this is not the first node
  //
  if (tmp_list_2.isFirst()) {
    return Error::handle(name(), L"isFirst", Error::TEST, __FILE__, __LINE__);
  }

  // make sure this is the last node
  //
  if (!tmp_list_2.isLast()) {
    return Error::handle(name(), L"isLast", Error::TEST, __FILE__, __LINE__);
  }


  if (tmp_list_2.last_d->getItem()->ne(*items[2]) ||
      tmp_list_2.curr_d->getItem()->ne(*items[2])) {
    return Error::handle(name(), L"gotoLast", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  if (tmp_list_2.isMarkedElement()) {
    return Error::handle(name(), L"isMarkedElement", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  // when there is no next node, gotoNext should stay at the current position
  // and return false
  //
  tmp_list_2.gotoLast();
  if (tmp_list_2.gotoNext()) {
    return Error::handle(name(), L"gotoNext", Error::TEST, __FILE__,
			 __LINE__);
  }
    
  if (tmp_list_2.curr_d != tmp_list_2.last_d) {
    return Error::handle(name(), L"gotoNext", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  // goto the previous node 
  //
  if (!tmp_list_2.gotoPrev()) {
    return Error::handle(name(), L"gotoPrev", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  if (tmp_list_2.curr_d->getItem()->ne(*items[5])) {
    return Error::handle(name(), L"gotoPrev", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  // when there is no previous node, gotoPrev should stay at the current
  // position and return false
  //
  tmp_list_2.gotoFirst();
  
  if (tmp_list_2.gotoPrev()) {
    return Error::handle(name(), L"gotoPrev", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  if (tmp_list_2.curr_d != tmp_list_2.first_d) {
    return Error::handle(name(), L"gotoPrev", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  // goto the marked node 
  //
  if (!tmp_list_2.gotoMark()) {
    return Error::handle(name(), L"gotoMark", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  if (tmp_list_2.curr_d->getItem()->ne(*items[3]) ||
      tmp_list_2.mark_d->getItem()->ne(*items[3])) {
    return Error::handle(name(), L"gotoMark", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }

  //--------------------------------------------------------------------------
  //
  // 4. class-specific public methods:
  //     element access methods
  //
  //--------------------------------------------------------------------------

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

  // get the element of the current node
  //
  if (tmp_list_2.getCurr()->ne(*items[3])) {
    return Error::handle(name(), L"getCurr", Error::TEST, __FILE__,
			 __LINE__);
  }


  for (long i = 0; i < 9; i++) {
    Char x((long)L'a' + i);
    if (tmp_list_2(i).ne(x)) {
      return Error::handle(name(), L"operator", Error::TEST,
			   __FILE__, __LINE__);
    }
  }

  // test getPosition
  //
  if (tmp_list_2.getPosition() != 1) {
    return Error::handle(name(), L"getPosition", Error::TEST, __FILE__,
			 __LINE__);
  }

  // test gotoPosition for all cases
  //
  for (long i = 0; i < 9; i++) {
    tmp_list_2.gotoPosition(i);
    if (tmp_list_2.getPosition() != i) {
      return Error::handle(name(), L"getPosition", Error::TEST, __FILE__,
			   __LINE__);
    }
  }

  // test the contains method
  //
  tmp_list_2.gotoLast();
  for (long i = 0; i < 9; i++) {
    if (!tmp_list_2.contains(items[i])) {
      return Error::handle(name(), L"contains", Error::TEST, __FILE__,
			   __LINE__);
    }
  }

  tmp_list_2.gotoLast();
  for (long i = 0; i < 9; i++) {
    if (!tmp_list_2.find(items[i])) {
      return Error::handle(name(), L"find", Error::TEST, __FILE__,
			   __LINE__);
    }
    if (tmp_list_2.getCurr()->ne(*items[i])) {
      return Error::handle(name(), L"getPosition", Error::TEST, __FILE__,
			   __LINE__);
    }
  }

  // restore the position
  //
  tmp_list_2.gotoPosition(1);

  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
  
  //--------------------------------------------------------------------------
  //
  // 5. class-specific public methods:
  //     insert and remove methods
  //
  //--------------------------------------------------------------------------

  // set indentation
  //
  if (level_a > Integral::NONE) {
    Console::put(L"testing class-specific public methods: insert and remove methods...\n");
    Console::increaseIndention();
  }
  
  // build a simple set
  //
  Set<Char> set_system(DstrBase::SYSTEM);
  Set<Char> set_user(DstrBase::USER);
  
  for (long i = 0; i < 9; i++) {
    if (!set_system.insert(items[i])) {
      return Error::handle(name(), L"insert", Error::TEST, __FILE__, __LINE__);
    }
    if (!set_user.insert(items[i])) {
      return Error::handle(name(), L"insert", Error::TEST, __FILE__, __LINE__);
    }
  }


  // now try to insert items again, it should fail
  //
  for (long i = 0; i < 9; i++) {
    if (set_system.insert(items[i])) {
      return Error::handle(name(), L"insert", Error::TEST, __FILE__, __LINE__);
    }
    if (set_user.insert(items[i])) {
      return Error::handle(name(), L"insert", Error::TEST, __FILE__, __LINE__);
    }
  }

  // find item[4]
  //
  if (!set_system.find(items[4])) {
    return Error::handle(name(), L"find", Error::TEST, __FILE__, __LINE__);
  }
  if (!set_user.find(items[4])) {
    return Error::handle(name(), L"find", Error::TEST, __FILE__, __LINE__);
  }

  // remove it
  //
  if (!set_system.remove()) {
    return Error::handle(name(), L"remove", Error::TEST, __FILE__, __LINE__);
  }
  if (!set_user.remove()) {
    return Error::handle(name(), L"remove", Error::TEST, __FILE__, __LINE__);
  }

  // make sure it is gone
  //
  if (set_system.contains(items[4])) {
    return Error::handle(name(), L"contains", Error::TEST, __FILE__, __LINE__);
  }
  if (set_user.contains(items[4])) {
    return Error::handle(name(), L"contains", Error::TEST, __FILE__, __LINE__);
  }

  Char* x;
  
  // find item[2]
  //
  if (!set_system.find(items[2])) {
    return Error::handle(name(), L"find", Error::TEST, __FILE__, __LINE__);
  }
  if (!set_user.find(items[2])) {
    return Error::handle(name(), L"find", Error::TEST, __FILE__, __LINE__);
  }

  // remove it: for system mode it should copy the object
  //
  x = new Char;
  if (!set_system.remove(x) || (x == items[2]) || x->ne(*items[2])) {
    return Error::handle(name(), L"remove", Error::TEST, __FILE__, __LINE__);
  }
  delete x;
  if (!set_user.remove(x) || (x != items[2])) {
    return Error::handle(name(), L"remove", Error::TEST, __FILE__, __LINE__);
  }
  
  // make sure it is gone
  //
  if (set_system.contains(items[2])) {
    return Error::handle(name(), L"contains", Error::TEST, __FILE__, __LINE__);
  }
  if (set_user.contains(items[2])) {
    return Error::handle(name(), L"contains", Error::TEST, __FILE__, __LINE__);
  }

  // make sure everything else is still there
  //
  for (long i = 0; i < 9; i++) {
    if ((i != 2) && (i != 4) && !set_system.contains(items[i])) {
      return Error::handle(name(), L"contains", Error::TEST, __FILE__,
			   __LINE__);
    }
    if ((i != 2) && (i != 4) && !set_user.contains(items[i])) {
      return Error::handle(name(), L"contains", Error::TEST, __FILE__,
			   __LINE__);
    }
  }

  // delete working data
  //
  for (long i = 0; i < 3; i++) {
    for (long j = 0; j < 3; j++) {
      delete items[i * 3 + j];
    }
  }
  delete [] items;

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

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

