CS302 -- Lab 4-- Stock Reports
Lab Objective
This lab will accomplish a number of objectives:
- It will give you experience writing classes
- It will expand your knowledge of C's IO system
- It will help you review your knowledge of hash tables
- It will give you more experience using doubly linked lists and balanced
binary trees
- It will give you experience using C++'s reference type
- It will give you experience with overloading operators and constructors
- It will give you experience using template classes
Problem Statement
Your program is going to read a file
(e.g., transactions) of stock transactions and organize
the transactions by person. For each person you are going to sort the
transactions alphabetically by stock symbol and for each stock symbol you
are going to sort the transactions into ascending order by date. You will
use a hash table to search for people, a red-black tree for each person
to sort each person's stocks into alphabetical order, and a red-black for
each stock to sort the transactions into ascending order based on date.
The hash table will use separate-chaining and you will use doubly-linked lists
to handle the lists for each hash table entry.
Once you have read all the data from the transactions file you will allow
the user to enter the last names of individuals. You will then print out
all of the stocks for that individual in alphabetical order and for each
stock you will print the transactions for that stock in ascending order by
date. You will repeatedly query the user for lastnames
until the user hits Ctrl-D, thus
ending the program.
A binary executable (called SortStock)
has been provided for you so that
you can see what your program should do on a given input file. To
access it you should copy over the transactions file to your directory and
type:
/home/parker/courses/cs302/labs/lab4/SortStock transactions
Format of the Input File
The input file is going to consist of lines that have no blank spaces.
Your program will identify the fields in a line according to the following
format:
| Character Positions | Field Name | Type |
| 0-9 | first name |
string |
| 10-24 | last name |
string |
| 25-28 | transaction type |
string |
| 29-33 | stock symbol |
string |
| 34-48 | stock name |
string |
| 49-52 | quantity |
integer |
| 53-60 | price |
double |
| 61-62 | month |
integer |
| 63-64 | day |
integer |
| 65-68 | year |
integer |
An example transaction file can be found in
transactions. Note that all the fields are
right-justified. We have also provided you a program, called
write_transactions.cpp,
(with the cpp and binary also available
in /home/parker/courses/cs302/labs/lab4), so that
you can easily create new transactions data files to test your program.
Because there are no blank spaces on a line you will not be able to use the
Fields library to read the input file for this lab.
(Although you will be using the Fields library to read the user input.)
Instead you will write your own class, called
FormattedReader, that will read lines and break them into fields.
The interface for this class should be as follows:
- FormattedReader(string filename, int num_fields, int field_sizes[]): The
constructor will take the name of the file, the number of fields per
line, and an array that gives the number of characters per field. It
needs to open the file and allocate any data structures required to
implement the reader's operations.
- ~FormattedReader(): The destructor should close the file and de-allocate
any data structures allocated by the constructor.
- int read_line(): read_line should read the next line into memory and
return the number of characters read. If EOF is reached then read_line
should return -1. read_line may or may not break the line into fields.
Whether or not it does so is up to you.
- int get_line_number(): returns the current line number starting at 1.
- string get_field(int index): returns the indicated field as a string.
For example, get_field(3) should return the fourth field in the
file, not the field starting at character location 3.
- int get_int(int index, bool &success_flag): returns the indicated field
as an integer. success_flag should be set to true if the
field is successfully converted to an integer and false otherwise.
If the field cannot be converted to an integer then the return value
is undefined (i.e., you can return any integer you want).
- double get_double(int index, bool &success_flag): returns the indicated
field as a double. success_flag should be set to true if the
field is successfully converted to a double and false otherwise.
If the field cannot be converted to a double then the return value
is undefined (i.e., you can return any double you want).
- string get_line(): returns the line that was read into the buffer,
terminated by a '\0' character.
You should use C's getc function to read characters one at time
into a character buffer. When your reader has read all the characters for
that line it should discard the remaining characters up to and including
the newline character. For this lab you may assume that each line is at
least as long as the sum of all the field sizes but you cannot assume that
the line will be exactly as long as the sum of all the field sizes. In other
words we might give you lines that are longer than the supposed size of a
line.
Storing Transactions
As indicated earlier you should store transactions with the appropriate
person. You should use the lastname as the key and you may assume that lastnames
are unique. For each transaction that you read you will perform roughly the
following actions:
- Type check the quantity, price, and date fields to ensure that they
are of the appropriate type. If they are not the correct type
you should print
an error message with the line number, the name of the field, and
the type that the user should have input. You should then skip to
the next line. Do not exit the program. Check the executable
to see the exact error messages that you should use.
- Look up the person executing the transaction in a hash table. If the
person does not exist then create a record for that person and
insert it into the hash table. Each record will minimally need to
have the person's lastname and a red-black tree keyed on strings
that stores the stocks the person has bought or sold. You should name
the class for this record PersonRecord. It is okay to make all of
the instance variables in this class public since you plan to use
it solely as a data record.
- Extract the red-black tree representing the stocks seen thus far for
the person. Search for the stock symbol associated with the current
transaction in the tree. If the stock symbol is not found then create
an entry for the stock in the tree. You should insert the symbol as
the key and a red-black tree that stores transaction records as the
value. Note that you will have to insert the transaction record
as a jval which is a void *. The red-black tree will need to store
the transaction records as actual objects, not pointers to transaction
records.
- Create a transaction record that contains the fields that you will need
and insert it into the transactions tree based on the date. You will need
to insert the transaction record as an object, not a pointer to an object.
Thus your tree declaration will look like:
rbTree<TransactionRecord> *transaction_tree;
Note that TransactionRecord is the key field.
In order
to sort properly by date
you will need to write <, >, and == operators for
a transaction record. You will also need to create a copy constructor and
an assignment operator so that both your program and the Red-Black tree library
can handle your TransactionRecords as objects and not pointers.
You should declare the transaction record as
a class and then define the following operators and copy constructor
for the transaction record:
class TransactionRecord {
public:
TransactionRecord(const TransactionRecord &);
bool operator<(const TransactionRecord &rec);
bool operator>(const TransactionRecord &rec);
bool operator==(const TransactionRecord &rec);
TransactionRecord &operator=(const TransactionRecord &rec);
... other methods and instance variables ...
};
It is permissable to make all the members of this class public since
your program will be using it as a data record.
Format of the Output
Each time the user enters the lastname for a person your program will
print a report with the following lines:
Stock transactions for firstname lastname
StockSymbol: StockName
xx/xx/xxxx type qty price
...
StockSymbol: StockName
xx/xx/xxxx type qty price
...
Try executing the provided executable to see example output for both
people who are found and people who are not found. The lines of the
report for a person who is found should
be specifically formatted as follows:
- Person's Name: The line should start "Stock transactions for" and then
print the persons first and lastname, separated by a space.
- Stock Symbol: StockName: The line should start with 4 blank spaces,
followed by the stock symbol, a colon, a space, and then the stock's
name. For example:
xom: Exxon Mobile
- Transaction: The line should have the following format:
| Character Position | Field Name |
| 0-7 | blank spaces |
| 8-17 | the date in month, day, year format |
| 18-19 | blank spaces |
| 20-23 | transaction type |
| 24-25 | blank spaces |
| 26-30 | quantity |
| 31-32 | blank spaces |
| 33-40 | price |
All fields should be right justified.
As previously described, when
a person has stock transactions with more than one type of
stock, the transactions should be printed in lexicographic order
by the stock name. Within a particular stock, the transactions
should be printed in chronological order by date. (Try the
example program with a last name input of "Duck" for an example
of this ordered output.)
Data Structures
You will need to use the rbTree and dList libraries that have been provided
for you. You should check out
rbTree.h and
Dllist.h files in
/home/parker/courses/cs302/include to see what operations are available to you.
You will need to write your own HashTable class. The hash function you
should use can be found in Figure 5.4 on page 183 of the Weiss text. The
interface for your class should be as follows:
You will need to further declare your instance variables and your hash
function as protected members of your hash table class.
Queries
User's queries should be read from stdin using the C++ version of the
Fields library. You can find the public interface for the Fields library
in Fields.h (again, found as usual in
/home/parker/courses/cs302/include/Fields.h).
Testing Tools
We are providing you the following tools to test your code:
- testFormat.cpp: Reads a transactions file and uses your FormattedReader methods to ensure that the file is in the proper format. (There is a
working binary of this program in the usual course directory.)
- testHash.cpp: Reads in a file of inputstrings (i.e., names),
inserts them into a hash table using your hash table classes, and
then accepts strings from stdin and finds them in the hash table. (There
is a working binary of this program, and a test inputstrings
file called testInputStrings in the usual course directory.)
- write_transactions.cpp: Writes new transactions file,
consistent with the format defined for this lab. (There is a
working binary of this program in the usual course directory.)
- testMake: Make file to compile the above. (Note, however,
that you have to write your own FormattedReader and HashTable
classes and implementations for you to recompile the test programs.)
What to Hand In
- makefile: Your -I flags will need to include both
/home/parker/courses/cs302/include and /home/parker/courses/cs302/src.
You will have to include /home/parker/courses/cs302/objs/lib302.a to
get the Fields and jval classes linked into your program.
- HashTable.h: The include file for your hash table class
- FormattedReader.h: The include file for your formatted reader class
- SortStock.h: The include file for your main program. This file will
include declarations for your PersonRecord and TransactionRecord
classes.
- HashTable.cpp: The C++ code for your hash table class
- FormattedReader.cpp: The C++ code for your formatted reader class
- SortStock.cpp: The C++ code for reading and printing transactions and
the C++ code for the operator methods associated with the
TransactionRecord class.
As always, you submit labs using
the special script discussed on the
TA web site.