// ------------------------------- // // -------- Start of File -------- // // ------------------------------- // // ----------------------------------------------------------- // // C++ Source Code File Name: testprog.cpp // Compiler Used: MSVC, BCC32, GCC, HPUX aCC, SOLARIS CC // Produced By: DataReel Software Development Team // File Creation Date: 11/07/1996 // Date Last Modified: 06/17/2016 // Copyright (c) 2001-2024 DataReel Software Development // ----------------------------------------------------------- // // ------------- Program description and details ------------- // // ----------------------------------------------------------- // /* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Test program for the generic binary search tree class. */ // ----------------------------------------------------------- // #include "gxdlcode.h" #if defined (__USE_ANSI_CPP__) // Use the ANSI Standard C++ library #include <iostream> using namespace std; // Use unqualified names for Standard C++ library #else // Use the old iostream library by default #include <iostream.h> #endif // __USE_ANSI_CPP__ #include <stdio.h> #include <time.h> #include "gxbstree.h" #include "bstreei.h" #include "ustring.h" #include "dfileb.h" #ifdef __MSVC_DEBUG__ #include "leaktest.h" #endif void InputData(UString &sbuf) // Input a line of text from the console { char buf[255]; cout << "Input Data: "; cin.getline(buf, sizeof(buf)); sbuf = buf; } void ClearInputStream(istream &s) // Used to clear istream { char c; s.clear(); while(s.get(c) && c != '\n') { ; } } int Quit() // Terminate the program { cout << "Exiting..." << "\n"; return 0; } void PausePrg() // Pause the program { cout << "\n"; cout << "Press enter to continue..." << "\n"; cin.get(); } void Menu(void) // List the program options { cout << "(A, a) Add an item to the tree" << "\n"; cout << "(B, b) Build a tree of strings" << "\n"; cout << "(C, c) Clear the tree" << "\n"; cout << "(D, d) Delete an item from the tree" << "\n"; cout << "(E, e) Extract all the node in sort order" << "\n"; cout << "(F, f) Find an item in the tree" << "\n"; cout << "(H, h, ?) Help (prints this menu)" << "\n"; cout << "(I, i) Import a dictionary file" << "\n"; cout << "(L, l) List tree items in order" << "\n"; cout << "(Q, q) Quit this program" << "\n"; cout << "(S, s) Display the tree statistics" << "\n"; cout << "(R, r) Extract all the node in reverse order" << "\n"; cout << "(T, t) Test tree insertion/deletion times" << "\n"; } unsigned TreeHeight(gxBStreeNode_t *node) // Recursive function used to calculate the tree height based // on the maximum of the height of right and left subtrees. { unsigned height = 0; if(node != 0) { unsigned left_height = TreeHeight(node->left); unsigned right_height = TreeHeight(node->right); if(left_height > right_height) { height = left_height+1; } else { height = right_height+1; } } return height; } void NumNodes(gxBStreeNode_t *node, unsigned &num_nodes) // Recursive function used to calculate the total number of nodes. { while(node) { NumNodes(node->left, num_nodes); num_nodes++; node = node->right; } } void TreeStats(gxBStree<UString> &tree) { cout << "\n"; cout << "----- Binary search tree statistics -----" << "\n"; cout << "\n"; if(tree.IsEmpty()) { cout << "The tree is empty" << "\n"; return; } unsigned num_nodes = 0; unsigned height = 0; NumNodes(tree.GetRoot(), num_nodes); height = TreeHeight(tree.GetRoot()); cout << "Number of nodes = " << num_nodes << "\n"; cout << "Height = " << height << "\n"; cout << "\n"; UString root, first, last; cout << "Root node = " << tree.GetRoot()->data << "\n"; if(tree.FindFirst(first)) cout << "First key = " << first << "\n"; if(tree.FindLast(last)) cout << "Last key = " << last << "\n"; cout << "\n"; } void ImportDictionaryFile(gxBStree<UString> &tree) // Import a list of words in sort order. This function demonstrates // how unbalanced trees are degenerated when a sort ordered list of // items are inserted into the tree. { cout << "\n"; cout << "Importing a dictionary file." << "\n"; tree.Clear(); // Clear the tree before importing the file char LineBuffer[df_MAX_LINE_LENGTH]; int status, key_count = 0; UString key; UString FileName; cout << "\n"; cout << "Enter the name of the text file to import" << "\n"; InputData(FileName); DiskFileB infile(FileName.c_str()); if(!infile) { // Could not open the istream cout << "Could not open file: " << FileName << "\n"; return; } cout << "Adding words..." << "\n"; // Get CPU clock cycles before entering loop clock_t begin = clock(); while(!infile.df_EOF()) { if(infile.df_GetLine(LineBuffer, df_MAX_LINE_LENGTH, '\n', 1) != DiskFileB::df_NO_ERROR) { break; // Error reading from the disk file } if(strcmp(LineBuffer, "") == 0) continue; key = LineBuffer; cout << key.c_str() << "\n"; status = tree.Insert(key); if(status != 1) { cout << "\n" << "Problem adding " << key.c_str() << "\n"; return; } else { key_count++; } } // Get CPU clock cycles after loop is completed clock_t end =clock(); // Calculate the elapsed time in seconds. double elapsed_time = (double)(end - begin) / CLOCKS_PER_SEC; cout.precision(3); cout << "Added " << key_count << " words in " << elapsed_time << " seconds" << "\n"; // Rewind the file infile.df_Rewind(); cout << "Verifying the entries..." << "\n"; begin = clock(); key_count = 0; double search_time = 0; while(!infile.df_EOF()) { if(infile.df_GetLine(LineBuffer, df_MAX_LINE_LENGTH, '\n', 1) != DiskFileB::df_NO_ERROR) { break; // Error reading from the disk file } if(strcmp(LineBuffer, "") == 0) continue; key = LineBuffer; clock_t begin_search = clock(); status = tree.Find(key); clock_t end_search = clock(); search_time += (double)(end_search - begin_search) / CLOCKS_PER_SEC; if (status != 1) { cout << "\n" << "Problem finding " << key << "\n"; return; } else { key_count++; } } end =clock(); elapsed_time = (double)(end - begin) / CLOCKS_PER_SEC; cout.precision(3); cout << "Verified " << key_count << " words in " << elapsed_time << " seconds" << "\n"; double avg_search_time = (search_time/(double)key_count) * 1000; cout << "Average search time = " << avg_search_time << " milliseconds" << "\n"; PausePrg(); // Rewind the file infile.df_Rewind(); cout << "Deleting the entries..." << "\n"; begin = clock(); key_count = 0; while(!infile.df_EOF()) { if(infile.df_GetLine(LineBuffer, df_MAX_LINE_LENGTH, '\n', 1) != DiskFileB::df_NO_ERROR) { break; // Error reading from the disk file } if(strcmp(LineBuffer, "") == 0) continue; key = LineBuffer; status = tree.Delete(key); if (status != 1) { cout << "\n" << "Problem deleting " << key << "\n"; return; } else { key_count++; } } end =clock(); cout.precision(3); elapsed_time = (double)(end - begin) / CLOCKS_PER_SEC; cout << "Deleted " << key_count << " words in " << elapsed_time << " seconds" << "\n"; // Rewind the file infile.df_Rewind(); cout << "Re-inserting all the words..." << "\n"; key_count = 0; begin = clock(); while(!infile.df_EOF()) { if(infile.df_GetLine(LineBuffer, df_MAX_LINE_LENGTH, '\n', 1) != DiskFileB::df_NO_ERROR) { break; // Error reading from the disk file } if(strcmp(LineBuffer, "") == 0) continue; key = LineBuffer; status = tree.Insert(key); if (status != 1) { cout << "\n" << "Problem adding " << key << "\n"; return; } else { key_count++; } } end =clock(); cout.precision(3); elapsed_time = (double)(end - begin) / CLOCKS_PER_SEC; cout << "Added " << key_count << " words in " << elapsed_time << " seconds" << "\n"; infile.df_Close(); return; } void BuildTree(gxBStree<UString> &tree) // Build a tree of strings. { char *aa1 = "dog"; char *bb1 = "cat"; char *cc1 = "fish"; char *dd1 = "mouse"; char *ee1 = "bird"; char *ff1 = "pig"; char *gg1 = "horse"; char *hh1 = "lion"; char *ii1 = "snake"; char *jj1 = "cow"; char *kk1 = "armadillo"; char *ll1 = "grouper"; char *mm1 = "rat"; char *nn1 = "monkey"; char *oo1 = "zebra"; char *pp1 = "starfish"; char *qq1 = "lizard"; char *rr1 = "crab"; char *ss1 = "snail"; char *tt1 = "gorilla"; char *uu1 = "lobster"; char *vv1 = "turkey"; char *ww1 = "beetle"; char *xx1 = "shark"; char *yy1 = "clam"; char *zz1 = "oyster"; char *keys[26] = { aa1, bb1, cc1, dd1, ee1, ff1, gg1, hh1, ii1, jj1, kk1, ll1, mm1, nn1, oo1, pp1, qq1, rr1, ss1, tt1, uu1, vv1, ww1, xx1, yy1, zz1 }; const int INSERTIONS = 26; // Number of keys to insert UString key; int i, status; tree.Clear(); cout << "Inserting " << INSERTIONS << " keys..." << "\n"; for(i = 0; i < INSERTIONS; i++) { key = keys[i]; status = tree.Insert(key); if(status != 1) { cout << "\n" << "Problem adding " << keys[i] << " - " << i << "\n"; return; } } TreeStats(tree); } void TestTree(gxBStree<UString> &tree) // Test the tree insertion and deletion times. { // Adjust this number to set the number of insertions // const unsigned long INSERTIONS = 1 * 1000; // 1K test // const unsigned long INSERTIONS = 10 * 1000; // 10K test const unsigned long INSERTIONS = 100 * 1000; // 100K test // const unsigned long INSERTIONS = 1000 * 1000; // 1MEG test // const unsigned long INSERTIONS = 10000 * 1000; // 10MEG test // const unsigned long INSERTIONS = 100000 * 1000; // 100MEG test // const unsigned long INSERTIONS = 1000000 * 1000; // 1GIG test cout << "Inserting " << INSERTIONS << " keys..." << "\n"; unsigned long i, key_count = 0; unsigned long curr_count = 0; int status; int verify_deletions = 0; // Set to ture to verify all deletions double insert_time = 0; const char *name = "Item"; char sbuf[255]; UString key; // Get CPU clock cycles before entering loop clock_t begin = clock(); for(i = 0; i < INSERTIONS; i++) { sprintf(sbuf, "%s %d", name, (int)i); key = sbuf; clock_t begin_insert = clock(); status = tree.Insert(key); clock_t end_insert = clock(); insert_time += (double)(end_insert - begin_insert) / CLOCKS_PER_SEC; key_count++; curr_count++; if(status != 1) { cout << "\n" << "Problem adding key - " << key << "\n"; return; } if(curr_count == 10000) { curr_count = 0; cout << "Inserted " << i << " keys in " << insert_time << " seconds" << "\n"; } } // Get CPU clock cycles after loop is completed clock_t end =clock(); // Calculate the elapsed time in seconds. double elapsed_time = (double)(end - begin) / CLOCKS_PER_SEC; cout.precision(3); cout << "Inserted " << key_count << " values in " << elapsed_time << " seconds" << "\n"; double avg_insert_time = (insert_time/(double)key_count) * 1000; cout << "Average insert time = " << avg_insert_time << " milliseconds" << "\n"; key_count = 0; double search_time = 0; cout << "Verifying the insertions..." << "\n"; begin = clock(); for(i = 0; i < INSERTIONS; i++) { sprintf(sbuf, "%s %d", name, (int)i); key = sbuf; clock_t begin_search = clock(); status = tree.Find(key); clock_t end_search = clock(); key_count++; search_time += (double)(end_search - begin_search) / CLOCKS_PER_SEC; if(status != 1) { cout << "Error finding key - " << key << "\n"; return; } } end =clock(); elapsed_time = (double)(end - begin) / CLOCKS_PER_SEC; cout.precision(3); cout << "Verified " << key_count << " values in " << elapsed_time << " seconds" << "\n"; double avg_search_time = (search_time/(double)key_count) * 1000; cout << "Average search time = " << avg_search_time << " milliseconds" << "\n"; cout << "Deleting all the entries..." << "\n"; key_count = 0; double delete_time = 0; begin = clock(); for(i = 0; i < INSERTIONS; i++) { sprintf(sbuf, "%s %d", name, (int)i); key = sbuf; clock_t begin_delete = clock(); status = tree.Delete(key); clock_t end_delete = clock(); delete_time += (double)(end_delete - begin_delete) / CLOCKS_PER_SEC; key_count++; if(status != 1) { cout << "Error deleting key - " << key << "\n"; return; } if(verify_deletions) { // Verify the remaining key locations for(unsigned long j = INSERTIONS-1; j != i; j--) { sprintf(sbuf, "%s %d", name, (int)j); key = sbuf; status = tree.Find(key); if(status != 1) { cout << "Error finding key - " << j << "\n"; cout << "After deleting key - " << i << "\n"; return; } } } } end =clock(); cout.precision(3); elapsed_time = (double)(end - begin) / CLOCKS_PER_SEC; cout << "Deleted " << key_count << " values in " << elapsed_time << " seconds" << "\n"; double avg_delete_time = (delete_time/(double)key_count) * 1000; cout << "Average delete time = " << avg_delete_time << " milliseconds" << "\n"; cout << "Re-inserting " << INSERTIONS << " keys..." << "\n"; key_count = 0; insert_time = 0; begin = clock(); for(i = 0; i < INSERTIONS; i++) { sprintf(sbuf, "%s %d", name, (int)i); key = sbuf; clock_t begin_insert = clock(); status = tree.Insert(key); clock_t end_insert = clock(); insert_time += (double)(end_insert - begin_insert) / CLOCKS_PER_SEC; key_count++; curr_count++; if(status != 1) { cout << "\n" << "Problem adding key - " << key << "\n"; return; } } end =clock(); elapsed_time = (double)(end - begin) / CLOCKS_PER_SEC; cout.precision(3); cout << "Inserted " << key_count << " values in " << elapsed_time << " seconds" << "\n"; avg_insert_time = (insert_time/(double)key_count) * 1000; cout << "Average insert time = " << avg_insert_time << " milliseconds" << "\n"; } void ListInOrder(gxBStree<UString> &tree) // List all the tree nodes using a tree iterator object. { gxBStreeNode<UString> *node = tree.GetRoot(); gxBStreeIterator tree_iterator(node); while((node = (gxBStreeNode<UString> *)tree_iterator.GetNext()) != 0) { cout << node->data << "\n"; } cout << "\n"; } void ExtractInOrder(gxBStree<UString> &tree) // Extract all the tree nodes in sort order. { UString key; while(!tree.IsEmpty()) { tree.ExtractFirst(key); cout << key << "\n"; } } void ExtractInReverseOrder(gxBStree<UString> &tree) // Extract all the tree nodes in reverse order. { UString key; while(!tree.IsEmpty()) { tree.ExtractLast(key); cout << key << "\n"; } } int main() // Test program's main thread of execution { #ifdef __MSVC_DEBUG__ InitLeakTest(); #endif Menu(); // Display the options menu char key; UString key_buf; gxBStree<UString> tree; int exists; int rv = 1; while(rv) { if (!cin) { ClearInputStream(cin); if (!cin) { cout << "Input stream error" << "\n"; return 0; } } cout << '>'; cin >> key; if (!cin) continue; switch(key) { case 'a' : case 'A' : ClearInputStream(cin); InputData(key_buf); tree.Insert(key_buf, &exists); if(exists) { cout << "An entry for " << key_buf << " already exists" << "\n"; } break; case 'b' : case 'B' : ClearInputStream(cin); BuildTree(tree); break; case 'c' : case 'C' : ClearInputStream(cin); tree.Clear(); break; case 'd' : case 'D' : ClearInputStream(cin); InputData(key_buf); if(tree.Delete(key_buf)) { cout << "Removed " << key_buf << "\n"; } else { cout << key_buf << " does not exists in the tree" << "\n"; } break; case 'e' : case 'E' : ClearInputStream(cin); ExtractInOrder(tree); break; case 'r' : case 'R' : ClearInputStream(cin); ExtractInReverseOrder(tree); break; case 'f' : case 'F' : ClearInputStream(cin); InputData(key_buf); if(tree.Find(key_buf)) { cout << "Found entry " << key_buf << "\n"; } else { cout << key_buf << " does not exists in the tree" << "\n"; } break; case 'l' : case 'L' : ClearInputStream(cin); ListInOrder(tree); break; case 's' : case 'S' : ClearInputStream(cin); TreeStats(tree); break; case 'i' : case 'I' : ClearInputStream(cin); ImportDictionaryFile(tree); break; case 'q' : case 'Q' : tree.Clear(); rv = Quit(); break; case 't' : case 'T' : ClearInputStream(cin); TestTree(tree); break; case '?' : case 'h' : case 'H' : ClearInputStream(cin); Menu(); break; default: cout << "Unrecognized command" << "\n"; } } return 0; } // ----------------------------------------------------------- // // ------------------------------- // // --------- End of File --------- // // ------------------------------- //