Question Description
main.cppmain.cppmain.cppmain.cpp
In this task, you will implement a program that creates an English dictionary for users to search through for definitions of words. You will implement a hashtable to store the dictionary and a user interface for managing/using the dictionary. The hash table will be indexed via the words in the English language. The dictionary will store the definition of the words for doing lookups after the dictionary is built.
Here are some of the key components of your implementation:
●Word class
oholds a word (string)
oholds the word’s definition (string)
oAlready implemented for testing, but it’s a tiny thing anyway.
●Hashtable class
oImplements a hash table (a vector)
oThe hash table will start with size 101
oThe Hashtable class will keep track of how many Words it holds (which is size N)
oThe main table will be a C++ STL vector (see note in private: section of the class)
oThe hash table uses separate chaining to resolve collisions (Chapter 5.3)
▪Each chain is a linked list holding Words
▪You’re welcome to decide how you want to do the linked list (STL list is fine)
oThe hash table needs to implement:
▪int hash_function(string key)
●returns an integer based upon converting the string key to an int
●See Figure 5.4 from the book for this algorithm
▪int hash_function(int key)
●returns hashkey integer based upon table size
●The function from the book using mod TableSize would work just fine
▪void insert(key, value)
●Hashes the Word’s key and adds a Word object to the hash table if it isn’t already there
●Updates (overwrites) old entry if there’s a already the same Word in the hash table
▪bool contains(string key)
●Searches the hash table by a string.
●Will need to convert the string to a key, then hash it and search the vector of Words to see if it’s there
▪Word remove(string key)
●Finds an entry using the passed in word, erases it (list.erase()) from the hash table, then returns how many elements it removed [0,1]
▪void rehash()
-
-
-
- Is called if the table gets above a load factor (N / Table Size) of 1.0 during an insert of a new Word
- At least doubles the table size then finds the next prime number for the new table size (See figure 5.22 for an example)
- Example code for finding the next prime above a given integer can be found at:
https://gist.github.com/alabombarda/f3944cd68dda390d25cb
-
-
- Dictionary class:
- This class is the user interface for your dictionary.
- I’ve stubbed in the basics for you, but you’ll need to handle parsing the user’s input they type in and handle JSON files with dictionaries in them.
- I provided the canonical loop for C++ to handle user input that ends when the file ends (EOF) or ctrl-D because you should just know that one by heart.
- This class needs to allow the user to enter these commands via STDIN:
help -> print out command help
add “word” “definition” -> Add (or update!) a word and its definition. Must handle quotes
remove “word” -> Remove a given word. Must handle quotes
define “word” -> Define a word by printing out its definition or “unknown word”
load “filename” -> Load in a JSON file of dictionary words
unload “filename” -> Remove words from a given JSON file of dictionary words
size -> Print out current number of words in the dictionary
clear -> Remove ALL words from the dictionary
print [#words] -> Print out all words, unless user gives a maximum number
random -> Print out a single word chosen randomly from the dictionary
quit -> Quit the user interface and shut down
●The key parts include:
●Words can be mixed case, but should still match!
○Hello == hello == HELLO == hElL0
●Words might have quotes around them if they have special characters like spaces, so make sure to strip those off when you parse
●Filenames for JSON files *can* have directory paths. C++ fopen and istream will take those just fine. i.e. configs/fullDict.json
●print has an optional number of words to limit printing
○If the user doesn’t specify, they get *everything* – test with care
○print isn’t in sorted order, just start from the first word you find
- unload will read in the JSON file and call remove on every word it finds
A note about JSON files:
JSON is a very common format for web servers. It’s used to pass data structures between programs and for storing configurations. The dictionaries I’ve provided are in JSON format. They live in the config/ directory. You may either use a C++ JSON library to load the files or write your own parser, which doesn’t have to handle the full JSON spec, but only our dictionary files. The lines are simple enough that it should be reasonably easy to parse the files without a full on library for handling the full JSON specification, but you’re welcome to approach it either way.
Main JSON page: http://www.json.org/
A couple of JSON libraries if you want to use those:
https://github.com/miloyip/rapidjson
http://uscilab.github.io/cereal/
Don’t underestimate how much it will take to get a working parser for the dictionary files! Handling the various pieces of texts, quotes, and other string handling could be interestingdepending on how you approach things. You can do most of it by reading in each line of the file, deciding if it’s a line with a word and definition, then using string iterators to find the start and stop of the word and definition to copy out.
Testing
As per normal, the Makefile has test cases. In this case there are separate tests for the hash table and the dictionary. The hash table tests are very similar to the ones we’ve seen for trees. The tests for the dictionary are more UI-style where they pass in a series of user commands to view the output. Please have a look at how these tests are implemented to make sure you have a sense of what is expected of the program. They pass in the kinds of strings you would expect based on the documentation here, but the ones in the tests are the canonical ones if there’s a discrepancy.
Deliverables
You must upload your program by checking it into blackboard on the PA3 branch. Please do not merge it into the master branch so the graders can find it more easily. Also, ensure your code stays in the PA3 directory so we know which code base to check. Grading should follow this pattern:
1) git clone your repository (or git pull to sync your local repo with the online version)
2) cd into your repository’s directory
3) git checkout PA3
4) cd PA3
5) make
6) make test
7) make bigtest
8) inspect your output, errors, and code for style and quality
Grading Criteria
Your assignment will be judged by the following criteria:
- [80] Your code compiles on the EECS servers using g++ -std=c++11 *.cpp (as is defined in the Makefile), passes inspection with a set of test cases (dictionary json files, Makefile, and general poking/prodding via make test and make bigtest.
- [10] Data structure usage. Your program uses a vector for the hashtable and separate chaining with linked lists for the collision resolution.
- [10] Your code is well documented and generally easy to read.
the given files
Makefile
| # Variables | |
| GPP = g++ | |
| CFLAGS = -g -Wall -std=c++11 | |
| RM = rm -f | |
| BINNAME = HashingDict | |
| # Shell gives make a full user environment | |
| # Adding this to PATH will find the newer g++ compiler on the EECS servers. | |
| SHELL := /bin/bash | |
| PATH := /opt/rh/devtoolset-3/root/usr/bin/:$(PATH) | |
| # Default is what happenes when you call make with no options | |
| # In this case, it requires that 'all' is completed | |
| default: all | |
| # All is the normal default for most Makefiles | |
| # In this case, it requires that build is completed | |
| all: build | |
| # build depends upon *.cpp, then runs the command: | |
| # g++ -g -std=c++0x -o bigFiveList | |
| build: main.cpp | |
| $(GPP) $(CFLAGS) -o $(BINNAME) main.cpp | |
| run: build | |
| ./$(BINNAME) | |
| test: build | |
| ./$(BINNAME) –test | |
| bigtest: build | |
| ./$(BINNAME) –test –withFuzzing | |
| # If you call "make clean" it will remove the built program | |
| # rm -f HelloWorld | |
| clean veryclean: | |
| $(RM) $(BINNAME) |
dictionary.h
| #ifndef __DICT_H | |
| #define __DICT_H | |
| #include "hashtable.h" | |
| #include "word.h" | |
| #include <string> | |
| #include <iostream> | |
| class Dictionary | |
| { | |
| private: | |
| Hashtable<string, Word> _dict; // Primary dictionary store | |
| void parseline( string line ) { | |
| //cout << " [d] Parsing line: " << line << endl; | |
| cout << endl; | |
| } | |
| public: | |
| Dictionary()// Default constructor | |
| { } | |
| /** | |
| * Run the main dictionary user interface | |
| */ | |
| void run_ui() { | |
| // print out header | |
| cout << "+——————————————+" << endl; | |
| cout << "|– Welcome to the best dictionary evar! –|" << endl; | |
| cout << "+——————————————+" << endl; | |
| string instr; | |
| cout << " Enter command (^D or EOF quits): "; | |
| // read in user input until eof | |
| while (getline(cin, instr)) { | |
| cout << endl << " –debug– Entered command: " << instr << endl; | |
| parseline(instr); | |
| // call function based on line contents | |
| // print results | |
| cout << "Enter command: "; | |
| } | |
| cout << endl; | |
| } | |
| }; | |
| #endif |
hashtable.h
| #ifndef __HASH_H | |
| #define __HASH_H | |
| #include <unordered_map> | |
| #include <string> | |
| #include <iostream> | |
| using namespace std; | |
| /* | |
| private: | |
| void rehash(); | |
| int hash_function(KEYTYPE key); | |
| public: | |
| bool insert(KEYTYPE key, VALTYPE val); | |
| bool contains(KEYTYPE key); | |
| int remove(KEYTYPE key); | |
| VALTYPE & find(KEYTYPE key); | |
| int size(); // Elements currently in table | |
| bool empty(); // Is the hash empty? | |
| float load_factor(); // Return current load factor | |
| void clear(); // Empty out the table | |
| int bucket_count(); // Total number of buckets in table | |
| */ | |
| template <typename KEYTYPE, typename VALTYPE> | |
| class Hashtable | |
| { | |
| private: | |
| /** | |
| * Rehash the table into a larger table when the load factor is too large | |
| */ | |
| void rehash() { | |
| } | |
| /** | |
| * Function that takes the key (a string or int) and returns the hash key | |
| * This function needs to be implemented for several types it could be used with! | |
| */ | |
| int hash_function(int key) { | |
| cout << " Hashing with int type keys." << endl; | |
| return –1; | |
| } | |
| int hash_function(string key) { | |
| cout << " Hashing with string type keys." << endl; | |
| return –1; | |
| } | |
| public: | |
| /** | |
| * Basic constructor | |
| */ | |
| Hashtable( int startingSize = 101 ) | |
| { | |
| } | |
| /** | |
| * Add an element to the hash table | |
| */ | |
| bool insert(KEYTYPE key, VALTYPE val) { | |
| // Currently unimplemented | |
| return false; | |
| } | |
| /** | |
| * Return whether a given key is present in the hash table | |
| */ | |
| bool contains(KEYTYPE key) { | |
| return false; | |
| } | |
| /** | |
| * Completely remove key from hash table | |
| * Returns number of elements removed | |
| */ | |
| int remove(KEYTYPE key) { | |
| // Doesn't actually remove anything yet | |
| return 0; | |
| } | |
| /** | |
| * Searches the hash and returns a pointer | |
| * Pointer to Word if found, or nullptr if nothing matches | |
| */ | |
| VALTYPE *find(KEYTYPE key) { | |
| return nullptr; | |
| } | |
| /** | |
| * Return current number of elements in hash table | |
| */ | |
| int size() { | |
| return(-1); | |
| } | |
| /** | |
| * Return true if hash table is empty, false otherwise | |
| */ | |
| bool empty() { | |
| return(false); | |
| } | |
| /** | |
| * Calculates the current load factor for the hash | |
| */ | |
| float load_factor() { | |
| //return _hash.load_factor(); | |
| return (-1.0); | |
| } | |
| /** | |
| * Returns current number of buckets (elements in vector) | |
| */ | |
| int bucket_count() { | |
| return (-1); | |
| } | |
| /** | |
| * Deletes all elements in the hash | |
| */ | |
| void clear() { | |
| // Does nothing yet | |
| } | |
| }; | |
| #endif |
main.cpp
| #include <iostream> | |
| #include <cstdlib> | |
| #include <string.h> | |
| #include "word.h" | |
| #include "hashtable.h" | |
| #include "dictionary.h" | |
| #include "testinghash.h" | |
| #include "testingdictionary.h" | |
| using namespace std; | |
| /** | |
| * Test mode operations | |
| */ | |
| void run_test_mode( bool bigtest = false ) { | |
| cout << " [t] Running in test mode. " << endl; | |
| run_hashtable_tests(); // See testinghash.h | |
| cout << endl << " ———————————————————– " << endl << endl;; | |
| cout << " [t] Testing the dictionary class. " << endl; | |
| run_dictionary_tests( bigtest ); // See testingdictionary.h | |
| } | |
| /** | |
| * Normal mode execution for general user control | |
| */ | |
| void run_normal_mode() { | |
| cout << " [x] Running in normal mode. " << endl; | |
| Dictionary dict; | |
| dict.run_ui(); | |
| } | |
| /** | |
| * Main function for test or use | |
| */ | |
| int main( int argc, char* argv[] ) | |
| { | |
| // Note: If you call this program like this: ./HashingDict –test | |
| // it will call the test function and –withFuzzing will test and load a big dictionary file | |
| bool do_test = false; | |
| bool do_big_test = false; | |
| for( int i = 0; i < argc; ++i ) { | |
| if( !strcmp(argv[i], "–test" ) ) { | |
| cout << " [x] Enabling test mode. " << endl; | |
| do_test = true; | |
| } | |
| else if( !strcmp(argv[i], "–withFuzzing" ) ) { | |
| cout << " [x] Enabling test and BIG test mode. " << endl; | |
| do_test = true; | |
| do_big_test = true; | |
| } | |
| } | |
| if( do_test ) { | |
| run_test_mode( do_big_test ); | |
| cout << " [x] Testing program complete. " << endl; | |
| if( do_big_test ) | |
| cout << " [x] BIGTEST testing program complete. " << endl; | |
| } | |
| else | |
| { | |
| cout << " [x] Running in normal mode. " << endl; | |
| cout << " [!] Nothing to do in normal mode so here's a helicopter: " << endl; | |
| cout << " .—-.-.n / ( o \n '| __ ` ||n ||| ||| -'" << endl << endl; | |
| cout << " Since that's not a helicopter, you should probably replace this code with something like "Welcome to my dictionary program" and go into normal running mode." << endl << endl; | |
| cout << endl << " Oh, and you should probably run 'make test' to test your program. " << endl; | |
| run_normal_mode(); | |
| } | |
| return(0); | |
| } |
testingdictionary.h
| #ifndef __DICT_TESTS_H | |
| #define __DICT_TESTS_H | |
| #include <iostream> | |
| #include <string> | |
| #include <fstream> | |
| #include "dictionary.h" | |
| using namespace std; | |
| //********************************************************************** | |
| void do_test( string testname, string filename ) { | |
| cout << "***************************************************************************" << endl; | |
| cout << " [t] Testing '" << testname << "' command" << endl; | |
| string instr; | |
| cout << " [t] Commands being run on the dictionary are: " << endl; | |
| ifstream in(filename); // Open up commands to test for tester to see | |
| while(getline(in, instr)) { | |
| cout << instr << endl; | |
| } | |
| cout << endl << " [t] Running those commands to dict: " << endl << endl; | |
| ifstream in2(filename); // Create file stream for file of commands to test | |
| cin.rdbuf(in2.rdbuf()); // Redirect std::cin to filename! | |
| Dictionary dict; // Create a new dictionary to test | |
| dict.run_ui(); // Run the dictionary, reading from filename | |
| cout << endl << " [t] Done testing '" << testname << "' command" << endl << endl; | |
| } | |
| //********************************************************************** | |
| void run_dictionary_tests( bool bigtest = false ) { | |
| streambuf *cinbuf = cin.rdbuf(); //save default STDIN | |
| cout << " [t] Dictionary tests begin." << endl; | |
| cout << " [x] These tests redirect STDIN to other files. " << endl; | |
| cout << " [x] This *should* work on Windows, but no promises! " << endl; | |
| cout << endl; | |
| do_test("help", "UITests/help.txt"); | |
| do_test("add", "UITests/add.txt"); | |
| do_test("remove", "UITests/remove.txt"); | |
| do_test("define", "UITests/define.txt"); | |
| do_test("load", "UITests/load.txt"); | |
| do_test("unload", "UITests/unload.txt"); | |
| do_test("size", "UITests/size.txt"); | |
| do_test("print", "UITests/print.txt"); | |
| do_test("random", "UITests/random.txt"); | |
| do_test("quit", "UITests/quit.txt"); | |
| if( bigtest ) | |
| do_test("bigtest","UITests/bigtest.txt"); | |
| cin.rdbuf(cinbuf); //reset cin to STDIN again | |
| cout << endl << " [t] Dictionary tests end." << endl; | |
| } | |
| #endif |
testinghash.h
| #include "hashtable.h" | |
| #include "word.h" | |
| //************************************************************** | |
| void test_hash_empty() { | |
| cout << " [t] Testing empty()" << endl;; | |
| Hashtable<string, Word> ht; | |
| cout << " [t] If is empty – "; | |
| ( ht.empty() ) ? cout << "pass" : cout << "fail"; | |
| cout << endl; | |
| ht.insert( "test", Word{ "test", "def" } ); | |
| cout << " [t] If is not empty – "; | |
| ( ht.empty() == false ) ? cout << "pass" : cout << "fail"; | |
| cout << endl; | |
| } | |
| //************************************************************** | |
| void test_hash_size() { | |
| cout << " [t] Testing size()" << endl;; | |
| Hashtable<string, Word> ht; | |
| for( int i = 0; i < 4; i++ ) | |
| { | |
| cout << " [t] Size " << i << " : " << ht.size(); | |
| ( i == ht.size() ) ? cout << " – pass" : cout << " – fail"; | |
| cout << endl; | |
| ht.insert( to_string(i), Word(to_string(i), "number") ); | |
| } | |
| for( int i = 3; i > –1; i– ) | |
| { | |
| ht.remove( to_string(i) ); | |
| cout << " [t] Size " << i << " : " << ht.size(); | |
| ( i == ht.size() ) ? cout << " – pass" : cout << " – fail"; | |
| cout << endl; | |
| } | |
| } | |
| //************************************************************** | |
| void test_hash_contains() { | |
| cout << " [t] Testing contains()" << endl;; | |
| Hashtable<string, Word> ht; | |
| ht.insert( "wordA", Word("wordA", "It's a word!") ); | |
| cout << " [t] Contains wordA (yes)"; | |
| ( ht.contains( "wordA" ) ) ? cout << " – pass" : cout << " – fail"; | |
| cout << endl; | |
| cout << " [t] Contains wordNO (no)"; | |
| ( !ht.contains( "wordNO" ) ) ? cout << " – pass" : cout << " – fail"; | |
| cout << endl; | |
| } | |
| //************************************************************** | |
| void test_hash_remove() { | |
| cout << " [t] Testing remove()" << endl;; | |
| Hashtable<string, Word> ht; | |
| ht.insert( "wordA", Word("WordA", "It's a word!") ); | |
| cout << " [t] Contains wordA (yes)"; | |
| ( ht.contains( "wordA" ) ) ? cout << " – pass" : cout << " – fail"; | |
| cout << endl; | |
| cout << " [t] Removing WordA: " << endl; | |
| int count = ht.remove( "wordA" ); | |
| cout << " Count (1): " << count; | |
| ( count == 1 ) ? cout << " – pass" : cout << " – fail"; cout << endl; | |
| cout << " Contains (no): "; | |
| ( !ht.contains( "wordA" ) ) ? cout << " – pass" : cout << " – fail"; | |
| cout << endl; | |
| } | |
| //************************************************************** | |
| void test_hash_find() { | |
| cout << " [t] Testing find()" << endl;; | |
| Hashtable<string, Word> ht; | |
| ht.insert( "myWord", Word("myWord", "It's a word!") ); | |
| cout << " [t] Contains myWord (yes)"; | |
| ( ht.contains( "myWord" ) ) ? cout << " – pass" : cout << " – fail"; | |
| cout << endl; | |
| Word * found = ht.find( "myWord" ); | |
| cout << " [t] myWord should be : myWord : It's a word!" << endl; | |
| cout << " [t] myWord instead is: "; | |
| if( found != nullptr ) { | |
| cout << found->to_string(); | |
| if( "myWord : It's a word!" == found->to_string() ) | |
| cout << " – pass"; | |
| else | |
| cout << " – fail"; | |
| } | |
| else | |
| cout << " – fail"; | |
| cout << endl; | |
| } | |
| //************************************************************** | |
| // Test load factor (force initial size, insert a couple, output) | |
| void test_hash_loadfactor() { | |
| cout << " [t] Testing loadfactor()" << endl;; | |
| Hashtable<string, Word> ht; | |
| for( int i = 0; i < 500; i++ ) { | |
| ht.insert( to_string(i), Word( to_string(i), "isa word" ) ); | |
| if( i % 33 == 0 ) { | |
| cout << " [t] load factor (" << ht.size() << " / " | |
| << ht.bucket_count() << ") -> "; | |
| float roundedTarget = (float)((int)(100*(float)ht.size() / (float)ht.bucket_count())) /100; | |
| float foundTarget = (float)((int)(100*ht.load_factor())) / 100.0; | |
| cout << roundedTarget << " vs " << foundTarget; | |
| (roundedTarget == foundTarget) ? cout << " – pass" : cout << " – fail"; | |
| cout << endl; | |
| } | |
| } | |
| } | |
| //************************************************************** | |
| // Test clearing out whole hash table | |
| void test_hash_clear() { | |
| cout << " [t] Testing clear()" << endl;; | |
| Hashtable<string, Word> ht; | |
| int tbsize = 500; | |
| for( int i = 0; i < tbsize; i++ ) { | |
| ht.insert( to_string(i), Word( to_string(i), "isa word" ) ); | |
| } | |
| cout << " [t] Table should be " << tbsize << " elements and is: " << ht.size(); | |
| ( tbsize == ht.size() ) ? cout << " – pass" : cout << " – fail"; | |
| cout << endl; | |
| cout << " [t] Calling clear() on table now." << endl;; | |
| ht.clear(); | |
| cout << " [t] Table should be " << 0 << " elements and is: " << ht.size(); | |
| ( 0 == ht.size() ) ? cout << " – pass" : cout << " – fail"; | |
| cout << endl; | |
| cout << " [t] Testing empty() one more time. "; | |
| ( ht.empty() ) ? cout << " – pass" : cout << " – fail"; | |
| cout << endl; | |
| } | |
| //************************************************************** | |
| void run_hashtable_tests() { | |
| cout << " [t] Testing the hash class itself. " << endl; | |
| // Create object | |
| Hashtable<string, Word> h1; | |
| // NOTE: insert() tested by size() and contains() (at least) | |
| test_hash_empty(); // Test if empty | |
| test_hash_size(); // Test size | |
Tutor AnswerReview
Anonymous
Thanks, good work
Brown University 1271 Tutors California Institute of Technology 2131 Tutors Carnegie Mellon University 982 Tutors Columbia University 1256 Tutors Dartmouth University 2113 Tutors Emory University 2279 Tutors Harvard University 599 Tutors Massachusetts Institute of Technology 2319 Tutors New York University 1645 Tutors Notre Dam University 1911 Tutors Oklahoma University 2122 Tutors Pennsylvania State University 932 Tutors Princeton University 1211 Tutors Stanford University 983 Tutors University of California 1282 Tutors Oxford University 123 Tutors Yale University 2325 Tutors Ongoing Conversations
|
Our website has a team of professional writers who can help you write any of your homework. They will write your papers from scratch. We also have a team of editors just to make sure all papers are of HIGH QUALITY & PLAGIARISM FREE. To make an Order you only need to click Ask A Question and we will direct you to our Order Page at WriteDemy. Then fill Our Order Form with all your assignment instructions. Select your deadline and pay for your paper. You will get it few hours before your set deadline.
Fill in all the assignment paper details that are required in the order form with the standard information being the page count, deadline, academic level and type of paper. It is advisable to have this information at hand so that you can quickly fill in the necessary information needed in the form for the essay writer to be immediately assigned to your writing project. Make payment for the custom essay order to enable us to assign a suitable writer to your order. Payments are made through Paypal on a secured billing page. Finally, sit back and relax.
About Writedemy
We are a professional paper writing website. If you have searched a question and bumped into our website just know you are in the right place to get help in your coursework. We offer HIGH QUALITY & PLAGIARISM FREE Papers.
How It Works
To make an Order you only need to click on “Place Order” and we will direct you to our Order Page. Fill Our Order Form with all your assignment instructions. Select your deadline and pay for your paper. You will get it few hours before your set deadline.
Are there Discounts?
All new clients are eligible for 20% off in their first Order. Our payment method is safe and secure.








