Saint Louis University |
Computer Science A220/P126
|
Dept. of Math & Computer Science |
Please see the general programming webpage for details about the programming environment for this course, guidelines for programming style, and details on electronic submission of assignments.
The files you may need for this assignment can be downloaded here.
For this assignment, you must work individually in regard to the design and implementation of your project.
Please make sure you adhere to the policies on academic integrity in this regard.
Web browsers commonly allow you to navigate through a "history" of web pages which have previously been visited. Example 4.4 of the textbook suggests that a stack ADT might be used to maintain this history, allowing the user to step "back" to get to the previously visited sites. If a web browser could hold infinitely many entries in its history, then this analogy might be valid. In reality, there may exist some fixed limit on the size of the history.
If the history must exist with a fixed maximum capacity, one approach may be to use the ArrayStack implementation of a Stack, as given in Code Fragment 4.13-4.14 of the text. In the case where an item is pushed onto an already full stack, this implementation throws up its hands (i.e., throws an Exception).
This is not how your Web browser behaves. If it only has room to save 50 pages in its history and yet you visit more pages, it will make room in the history for a new page by throwing away the page which is on the very bottom of the history (i.e., the least recently visited page). The formal Stack interface does not help, as it gives us no way to directly access or remove the object on the bottom of the stack.
In this assignment, we define a new ADT which we call a BoundedStack. The interface for a BoundedStack is very similar to the interface given for a Stack. However in the case when the capacity is exhausted, a call to push will result in the placement of the new page at the expense of the loss of the least recently accessed page. We formalize this interface as an abstract class definition, provided online in file BoundedStack.h.
/** * Interface for a bounded stack: A collection of objects that are inserted * and removed according to the last-in first-out principle, but with * overflow handled by the removal of the least-recently accessed item. */ class BoundedStack { public: /** * Returns number of objects in the stack. */ virtual int size() const =0; /** * Returns true if the stack is empty, false otherwise. */ virtual bool isEmpty() const =0; /** * Returns reference to the top object in the stack. * Throws BoundedStackEmptyException if the stack is empty. */ virtual string& top() throw(BoundedStackEmptyException) =0; /** * Inserts an object at the top of the stack. */ virtual void push(const string& obj) =0; /** * Removes and returns the top object from the stack. * Throws BoundedStackEmptyException if the stack is empty. */ virtual string pop() throw(BoundedStackEmptyException) =0; /** * Virtual Destructor */ virtual ~BoundedStack() {}; };
For this assignment, you will be required to give two different implementations for the BoundedStack interface, as described below. We will provide you with sample initial files for these classes.
One way to implement a BoundedStack is as follows. Use the ArrayStack implementation essentially verbatim from the book, modifying only the push method accordingly. In the case when pushing onto a "full" stack, use a loop to shift all items down one location in the array. By doing this, you will lose your bottommost item while opening up the topmost location for the newly pushed object.
A second way to implement a BoundedStack is to use an array viewed in a circular manner. You can mark the "top" of the circular stack with an extra integer variable which is an index into your array. By also keeping explicit count of the current size of the stack, you can effectively identify the "bottom" of the stack as well, when needed. With a bit of care, you can more efficiently handle pushes, even those involving overflow.
Please note that the text discusses the use of a circular array to implement a Queue in Chapter 4.3.2. You may find this discussion quite informative, but please note two important differences. First, a Queue and a BoundedStack do not have the same behavior. Second, the text describes an implementation using two variables to represent the front and back of the queue (as opposed to one variable for the front, and one variable for the size). Because of this design decision, they use an array of capacity N to represent a queue with effective capacity of only N-1. For a BoundedStack, you must carefully ensure that you maintain a stack of the specified capacity. Of course you can always choose to use an array of size one larger if you wish, so long as you carefully adapt the text's technique.
All such files can be downloaded here.
BoundedStack.h
This file formally defines the abstract class BoundedStack.
BoundedStackA.h
This file should be used to formally define the
BoundedStackA class, which is the first of the two
implementations of the BoundedStack interface.
The initial file which we provide is not complete. Rather it
defines all of the public methods which you will need to
implement, yet with stubs for most of the method bodies. You
will need to modify this file appropriately.
BoundedStackA.cpp
This file should contain the implementations for most of the
methods defined in the BoundedStackA class.
The initial file which we provide is not complete. Rather it
defines so-called stubs for each such method. The
stubs are syntactically correct so that they can be compiled,
but they are in no way semantically correct implementations of
the desired behaviors. You
will need to modify this file appropriately.
BoundedStackB.h
Analog to BoundedStackA.h but to be used for the
second of the two implementations as outlined in this
assignment.
BoundedStackB.cpp
Analog to BoundedStackA.cpp but to be used for the
second of the two implementations as outlined in this
assignment.
RuntimeExcepetion.h
BoundedStackEmptyExcepetion.h
These two files define the necessary Exception classes used in
this assignment. You will not need to modify this code.
WebHistoryDriver.cpp
This file provides a main driver to be used in testing your
program. You will not need to modify this code.
The use of the driver is discussed in the next section.
makefile
This makefile should allow you to rebuild your project by
simply typing 'make' rather than in invoking the compiler
directly.
WebHistoryDriver is a text-based, menu-driven program for testing your BoundedStack implementations. The menu allows you to call any combination of BoundedStack methods. You may even call the constructors for either of the two implementations anytime you want to restart with a different capacity.
By default, the program takes input from the keyboard. However the driver has an optional feature which allows it to read input from a file rather than from a keyboard. The file should have the identical characters which you would use if you were typing them yourself on the keyboard. This feature simply allows you to test and retest your program on a particular input without having to retype the input each time you want to run the program. It is also convenient at times when running with a debugger to have the input read from a file rather than from the keyboard. If the program reaches the end of the file without exiting, then it will revert back to reading input from the keyboard for the remainder of the program.
To use this feature, you must specify the exact filename (including any suffix), as a single argument at runtime. Details on how to provide such arguments are given in the general programming webpage.
Part of developing good software is also knowing how to perform meaningful testing of your software. Our hypothetical motivation for this data structure was that it might be used in the larger context of a web browser. One approach to testing would be to wait until th entire project were completed and then to test the final result. Of course, if the browser history were not operating properly at that point, we would have to wonder whether the problem was due to an errant implementation of the data structure, to an errant use of a well-written data structure, or some other problem. Rather than relying on a test of the entire end product, a useful approach is to make sure that each individual piece works in isolation. This is called unit testing. So for this assignment, you will develop a test plan for evaluating the correctness of the BoundedStack implementations without actually considering the larger application (in fact, we will not actually be writing any such hypothetical web browser). To perform unit testing of this data structure, you will use the provided driver which allows you to individually call any of the public methods supported by the data structure.
There is another issue we wish for you to consider when developing test plans. Often, testing is performed in close coordination with an examination of source code. For example, we may write some code or view some code, and then try to test that particular chunk of code. There is an inherent danger in relying only on such tests. In particular, if the tests are based on the same flow of thoughts as (errant) code, we are more likely to forget to test for a particular combination of events that were not considered by the code. The practice of "black box" testing is to design a set of tests based purely on the formal specifications for a data structure, but without actually seeing the source code.
On this assignment, we will have you develop a black-box unit test plan for BoundedStack implementations. Specifically, you must submit a plain text file named "inputfile" which will be used to test other students' implementations. The goal is to create test input which causes as many as possible of the other students' programs to fail in some way. Because our execution of student tests will be automated, you must make sure that your "inputfile" strictly adheres to the file format which is expected by the text-based driver, WebHistoryDriver, described earlier. Your file may use at most 100 commands. If your input file contains more than the prescribed number of commands, we will simply truncate the end of the file. In order to make sure that your inputfile follows the proper format, we strongly recommend that you run your program on your inputfile to make sure it is accepted by the driver.
Here is a sample input file, appropriately formatted.
Source Code
Please submit all source code files which you have either
modified or added. At minimum, we expect this to include the
four files (BoundedStackA.h,
BoundedStackA.cpp,
BoundedStackB.h,
BoundedStackB.cpp).
Test Input
Please submit a single file, inputfile, as outlined in the
earlier section on testing.
Readme File
For each assignment, you are to submit a separate file "readme" as
specified in the general programming webpage.
For this assignment, you should include two separate tables, similar
to Table 4.1 on page 165 of the text, summarizing the
worst-case asymptotic running times for your implementations.
One table should discuss BoundedStackA and one should discuss
BoundedStackB.
The assignment is worth 10 points. Eight points will be awarded based on our own evaluation of your assignment and the readme file. One additional point will be awarded fractionally based on how well your program performs on other students' test inputs. The final point will be awarded fractionally based on how well your test input fools other students' flawed programs.
When dealing with an array in a circular fashion, there are many possible ways for adjusting indicies as they "wrap around" the ends of the array. The text chooses to use modular arithmetic (the "%" operator in C++). Further discussion of this topic can be found in the general programming webpage.