to implement a program that creates an English dictionary for users to search through for definitions of words

User Generated

xnzry88558855

Programming

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
User generated content is uploaded by users for the purposes of learning and should be used following Studypool's honor code & terms of service.

Explanation & Answer

Hello buddy,Here's the complete ...


Anonymous
Really great stuff, couldn't ask for more.

Studypool
4.7
Trustpilot
4.5
Sitejabber
4.4

Related Tags