CS
215 - Fundamentals of Programming II
Spring 2008 - Project 7
30
points
Out:
April 14, 2008
Due: April 28, 2008 (last day of classes)
This project is to build a Morse code tree and use it to encode and decode messages.
Morse Code
Morse code was developed for sending messages across telegraphic wires. The code consists of patterns of short and long elements often called dots and dashes, respectively. Each letter, digit, and symbol is assigned a specific pattern of dots and dashes. The Morse code can be represented using a binary tree where the left branches represent a dot and the right branches represent a dash.
For example, here is a portion of the Morse code tree:

The code for each letter is a string formed by appending a '.' when taking a left branch and a '-' for a right branch when traversing the path from the root to the node containing the letter. In the codetree above, the code for 'r' is “.-.” (left, right, left), the code for 'm' is “--”, and the code for 'a' is “.-”. A message is encoded by giving a sequence of codes for letters in the message. Since Morse code does not have a prefix property (that is, a sequence of elements that represents one character may be a prefix of a longer sequence for some other character, e.g., the codes for 'm' and 'g'), there is a space between each character code. For example, the Morse code for the message “ram” is “.-. .- --”, which is formed by listing the codes for 'r', 'a', and 'm' separated by spaces. Since a space is used to separate character codes, an actual space between words will be represented in the code as '|'. Likewise a newline will be represented in the code as "||". (In actual Morse code, words and sentences are separated by longer silences than between the silence between characters, but we don't want to count spaces for this project.) Thus if the message "garment ten" is followed by a newline, our Morse code for the message would be " --. .- .-. -- . -. - | - . -. ||".
For this project, you will be given codefiles containing the Morse code. Your assignment is to write a MorseTree class (specification given below) that can read a codefile and construct a codetree for use in encoding and decoding messages, and write a (main) program to encode and decode messages using a MorseTree object.
This project assumes the Tnode<T> structure definition of Chapter 10 of the textbook (page 511) as used in lecture. Since it is a template, the definition should be placed outside and before the MorseTree class definition in the morse.h header file.
Specifications for MorseTree Class
Note: this is not a template class, though it does use the Tnode structure template from the textbook.
There is only one attribute of this class, a single Tnode<char> pointer variable that points to the root of the constructed codetree.
Explicit-value constructor - builds the codetree from an input stream connected to a codefile.
Analysis
|
Objects |
Type |
Kind |
Movement |
Name |
|
input stream connected to codefile |
istream |
variable |
received & passed back |
codeFile |
The codefile will consist of an integer n, followed by n lines of the form letter code where code is the Morse encoding for letter. The constructor is to read the codefile and build the codetree. For example, the codefile for the codetree above would be:
7 a .- e . g --. m -- n -. r .-. t -
The basic algorithm is to read each letter (as a character) and its code (as a string) and use the code to traverse the tree (starting with the root) as one would do when decoding. If an interior node does not exist, create it and continue. When the end of the code is reached, you are at the node that stores the letter.
Copy constructor - creates a copy of the original MorseTree using the CopyTree function
Analysis
|
Objects |
Type |
Kind |
Movement |
Name |
|
original tree |
MorseTree |
variable |
received |
original |
CopyTree - private recursive helper function to copy a tree (page 529 of textbook) that returns the root of the copy.
Analysis
|
Objects |
Type |
Kind |
Movement |
Name |
|
root of tree to copy |
Tnode<char>* |
variable |
received |
root |
|
root of copy |
Tnode<char>* |
variable |
returned |
copyRoot |
Destructor - deletes MorseTree nodes using DeleteTree function
Analysis - no objects
DeleteTree - private recursive helper function to delete tree nodes (page 530 of textbook)
Analysis
|
Objects |
Type |
Kind |
Movement |
Name |
|
root of tree to delete |
Tnode<char>* |
variable |
received |
root |
operator= - assignment operator, make this MorseTree the same as the original MorseTree using both DeleteTree and CopyTree, returns a reference to this object
Analysis
|
Objects |
Type |
Kind |
Movement |
Name |
|
original tree |
MorseTree |
variable |
received |
original |
|
this tree |
MorseTree & |
variable |
returned |
*this |
WriteTree - writes out the codetree “sideways” to an output stream using WriteTreeHelper function
Analysis
|
Objects |
Type |
Kind |
Movement |
Name |
|
output stream |
ostream |
variable |
received & passed back |
out |
WriteTreeHelper - private recursive helper function to write out tree to an output stream using RNL traversal (right, visit node, left). Each value should be written on a separate line with indent number of spaces before it. In order for the tree to "look right" on the screen, an extra newline should be output after each step when the indent number is 16 or less.
Analysis
|
Objects |
Type |
Kind |
Movement |
Name |
|
output stream |
ostream |
variable |
received & passed back |
out |
|
root of tree to write |
Tnode<char>* |
variable |
received |
root |
|
number of spaces to indent |
int |
variable |
received |
indent |
By using a RNL traversal with indenting, the tree will be written “sideways” with the root on the left side of the output. Each recursive call to WriteTreeHelper should increase the indenting by 8 spaces. For example, the codetree above would print out as:
m
g
t
n
*
a
r
e
Encode - encodes message from an input stream, writing results to an output stream, using CharToCode
Analysis
|
Objects |
Type |
Kind |
Movement |
Name |
|
input stream connected to message file |
istream |
variable |
received & passed back |
messageFile |
|
output stream |
ostream |
variable |
received & passed back |
out |
The basic algorithm for this function is to repeatedly read a character (including spaces and newlines) from the input stream, encode it using CharToCode, then write the encoding to the output stream. You may assume that the message file contains only the characters in the codetree and ignore any that do not encode properly. However, you may want to display an error message to cerr if no encoding is found for a read character stating that the character is being ignored.
CharToCode - private recursive helper function to find an encoding from the codetree.
Analysis
|
Objects |
Type |
Kind |
Movement |
Name |
|
character to encode |
char |
variable |
received |
ch |
|
node of tree being traversed |
Tnode<char>* |
variable |
received |
t |
|
path constructed so far |
string |
variable |
received |
pathSoFar |
|
encoding for character |
string |
variable |
returned |
code |
The basic algorithm for this function is to build a path (in pathSoFar) as the function traverses the codetree. For each node traversed, if the node value is the character being encoded, return the path. Otherwise, recursively try the paths going down the left and right subtrees by appending '.' or '-' respectively to pathSoFar. If the traversal reaches an empty tree, the character to encode is not in this path from the root, and return the empty string.
Decode - decodes message from an input stream, writing results to an output stream
Analysis
|
Objects |
Type |
Kind |
Movement |
Name |
|
input stream connected to encoded message file |
istream |
variable |
received & passed back |
messageFile |
|
output stream |
ostream |
variable |
received & passed back |
out |
The basic algorithm for this function is to read in a character code and use it to traverse the codetree starting at the root. When the end of the character code is reached, write out the letter in the node, then start over again with the next character code. In addition to character codes, there may be "codes" of '|' and "||" representing a space and a newline, respectively, that are to be decoded to those characters. You may assume that the encoded message file contains only valid input, and ignore any other inputs. However, you may want to display an error message to cerr if invalid characters or character codes are found in the input stream stating they are being ignored.
(20 points) Write the implementation of the MorseTree class. The Tnode struct definition and the MorseTree class definition should be put in header file morse.h with suitable compilation guards (the Tnode struct first, then the MorseTree class). The implementations of the MorseTree class functions should be put in source file morse.cpp. This project must be implemented using a binary tree with linked nodes. Projects that do not so will be returned for resubmission with late penalties.
(10 points) Write a (main) program coder.cpp that has one command-line argument, the name of a codefile. The program should construct a MorseTree object using the given codefile, then display the constructed codetree to the screen. Then the program should enter a menu driven loop that asks the user if they want to encode, decode or quit. For encode and decode, the program should ask for an input file name and an output filename, then do the appropriate action reading in from the input file and sending the output to the output file.
Implementation note: If you want to reuse an input stream in your coder program (i.e., you close one file and use the open member function to attach a different file to the input stream object), please note that the stream must be reset using the clear member function after it has been closed (and before the next open). I.e.,
infile.close(); infile.clear();
You can also explicitly construct a new input stream object every time through the coder loop by putting the declaration for infile inside the loop body. E.g.,
while (choice != 'Q')
{ :
cin >> inputFileName;
ifstream infile (inputFileName.c_str());
:
Two
codefiles will be given in /home/hwang/cs215/project7
on csserver. The file samplecodefile.dat
will be the partial Morse code used in the examples above, thus
suitable for preliminary testing of your class. The file
codefile.dat
will contain the full Morse code for the lowercase Latin alphabet.
You will have to write your own sample input files. Note that when
Encode and Decode are working, you should be able to send the output
of Encode into Decode and get back your original unencoded message
(and vice versa). You can use the diff
command to see if two files are the same.
You must submit a makefile named Makefile.project7 that creates executable name coder for your project. Submissions without working makefiles will be assessed up to a 3-point penalty as indicated in the syllabus. It should conform to the examples given in the handout Very Basic make and demonstrated in class.
REMINDER: Your project must compile for it to be graded. Submissions that do not compile will be returned for resubmission and assessed a late penalty. Submissions that do not substantially work also will be returned for resubmission and assessed a late penalty.
Follow the guidelines in the C++ Programming Style Guideline handout. As stated in the syllabus, part of the grade on a programming project depends on how well you adhere to the guidelines. The grader will look at your code listing and grade it according to the guidelines. s.
Electronically submit a tarfile containing Makefile.project7, morse.h, morse.cpp, and coder.cpp as explained in the handout Submission Instructions for CS 215 . Turn in a hardcopy of your Makefile.project7, morse.h, morse.cpp, and coder.cpp. The submission system will compile and test the MorseTree implementation against a test driver program. It will compile the project main program, but will not test it.
04/13/08