What you should learn from this assignment includes the use and implementation of two standard data structures: Stacks and Queues. The assignment will also give you practice working with specifications, testing and documentation.
In order for all this to succeed, you must follow the specifications precisely. It is common even for quite experienced programmers to find that they have neglected some point of a specification and that their "obviously correct" program does not work properly. For beginners, reading carefully does not come naturally, and one thing you will learn from this assignment is that you do have to read parts of the handout more than once.
You will also learn that specifications never achieve complete precision; sometimes you'll need to ask us for further details, and sometimes you'll need to make an intelligent extrapolation on your own.
This assignment will also extend your experience with good programming practices, in the form of JUnit testing and Javadoc documentation.
PureStack
(defined
in section
8.1.1 of the text) using only instances of PureQueue
(defined in
section 8.2.1 of the text). You are given an implementation of
PureQueue
in the starter code for use in this part; this
is the class
LLPureQueue
. You are also given most of the implementation
for the
PureStack
, in the class QStack
. Your only
job is to implement the pop()
function. The peek()
function is to be left
unimplemented. pop()
function as you see fit, but
you cannot
use anything from java.util.*,
and you cannot use any
data structures
other than PureQueue
for implementing this method.
Not following
these constraints will cost you all correctness marks for this part. PureStack
using a PureQueue
a good idea?
Why or why not? (Hint: Comment on how long it takes to execute your
implementation of the pop()
function compared to how long
it would take
if you had used java.util.LinkedList
for the
implementation instead.) Submit your answer in a text file called
answer.txt. Your answer should be no
more than a sentence or two in length. EditableQueue
Interface and LLEditableQueue
Class (YOU MUST WRITE CODE)EditableQueue
interface extends the PureQueue
interface (section
8.2.1 of the text) by adding the following method:public boolean remove(E element)
- removes the
specified element
from the queue (regardless of where it is in the queue). EditableQueue
using a
linked list.
Note: you are allowed to use java.util.*
in this part.
Your
implementation should be put in the class LLEditableQueue.
PureStack
Interface and LLPureStack
Class (provided in full in the Starter code)PureStack
interface is the interface as defined in
section 8.1.1 of
the text. LLPureStack
is an implementation of it using a
linked
list. Instruction, OtherInstruction, CallInstruction,
GotoInstruction, and ReturnInstruction
Classes (provided in full
in the Starter Code)Instruction
class defines an instruction that can be
executed by
the robot. It contains 4 constants used for specifying the different
types of instructions: CALL_TYPE, RETURN_TYPE, GOTO_TYPE, and
OTHER_TYPE. Its constructor takes three parameters: the type, the
execution time, and the operands. The type is one of the 4 constants,
the
execution time is the time it takes the robot to execute the
instruction, and the operands are the "extra" information required for
executing that instruction (think of "operands" like arguments to a
function). Instruction
is an abstract class and you
should never have
to construct an instance of it; instead, you should use one of the four
subclasses: OtherInstruction, CallInstruction, GotoInstruction,
ReturnInstruction.
Instruction
class defines the following methods:int getType()
- returns the type (one of the
predefined constants)long getExecuteTime()
- returns the time it takes to
execute the
instructionObject[] getOperands()
- returns the operands
required by this
instructionOtherInstruction
has 1 parameter: the
execution
time of the instruction. It has no operands. ReturnInstruction
takes no
parameters; its
execution time is predefined in the class. It has no operands. CallInstruction
takes 1 parameter:
the function to
be called. CallInstruction's execution time is predefined in the class.
The function to be called can be retrieved as the first element from
the operands array.GotoInstruction
takes 1 parameter:
the (integer)
index of the next instruction to goto. GotoInstruction's execution time
is predefined in the class. The index can be retrieved as the first
element from the operands array. Function
class (provided in full in the Starter
Code) Function
class is used for grouping instructions, and
has the
following methods:public void addInstruction(Instruction i)
- adds an
instruction
to the sequence of instructions to be executed by this functionpublic Instruction getInstruction(int index)
-
retrieves the
intstruction at the specified index. SampleRoundRobinRobotTester
). TaskState
class (provided in full in the Starter
Code) TaskState
class is used to specify the current state
of a Task.
It
contains the following methods:public Function getCurrentFunction()
- get the
function that's
currently executingpublic int getInstructionIndex()
- get the index of
the
instruction to be next executed in the current functionpublic long getRunningTime()
- get the "actual"
total time that
the task has been running forpublic boolean isDone()
- return if the task is done
or not
(i.e., if the main function of the task has encountered a return
instruction or not)Task
consists of a "main" function, which
defines where the
task's execution begins. Its constructor takes the "main" function as
its sole parameter. A Task
has the following methods:public TaskState getState()
- returns the current
state of the
taskpublic TaskState run(long duration)
- runs the task
for at most
the specified duration and returns the new state at the end of
execution. You need to provide an
implementation of this
method. See the method documentation in the starter code for how
your
implementation should behave. PureStack<Function> callStack
PureStack<Integer> indexStack
TaskState curState
callStack
maintains a stack of functions
that have
currently
been called. The top of the stack is the function that is currently
executing, one level from the top is the function that called the
currently executing function, and so forth. indexStack
is stack that is "parallel" to
the callStack
and maintains the index of the instruction that should be
executed next. The top of the stack contains the instruction that
should be
executed next in the function that is at the top of callStack. One
level from the top is the index of the instruction that should be next
executed when the function one level from the top in callStack starts
executing again, and so forth. curState
maintains the current state of the
Task. run()
should basically
follow this pseudocode:while (there is time remaining and task isn't done) {
Function curFunction = top of callStack
int curIndex = top of indexStack
Instruction curInstruction =
curFunction.getInstruction(curIndex)
if (curInstruction.getExecuteTime() <= time
remaining) {
update remaining time based on the time
it takes to execute the instruction
switch(instruction type) {
case CALL_TYPE:
update indexStack
appropriately
push new function onto
callStack
push new index onto
indexStack (what should the index be???)
case RETURN_TYPE:
left to student to figure
out what to do here
case GOTO_TYPE:
update indexStack
appropriately
case OTHER_TYPE:
update indexStack
appropriately
}
}
}
update the task state (note that the time run may be less than the
duration passed into the run() function)
Note that the pseudocode above only gives you a rough idea of
how your implementation should work -- there may be corner cases or
other gotchas that you need to handle. Furthermore, the pseudocode is
intentionally vague so that the solution isn't entirely laid out for
you. Robot
Interface and NonPreemptiveRobot and
RoundRobinRobot
Classes (YOU MUST WRITE CODE)Robot
interface defines the the main functionality
provided by the
robot. It contains the following methodsvoid addTask(Task t)
- "submits" a task for
execution by the
robot. (Implementation provided for you)void removeTask(Task t)
- removes a task previously
submitted. You must provide an
implementation of this method. void run(long duration)
- runs the robot for at most
the
specified duration, where the duration is specified in milliseconds. You must provide an implementation of this
method. long getRunningTime()
- returns the "actual" amount
of time the
robot has been running for. (Implementation provided for you) NonPreemptiveRobot
and RoundRobinRobot.
There are
two instance variables defined in both of these classes in the starter
code to help you:long runningTime
EditableQueue<Task> queue
runningTime
stores the total amount of time
that the robot
has actually been running for. This value may be slightly less than the
sum of the "duration" values passed into calls of run().
This may
happen when the duration tasks are expected to run are not an integral
multiple of the execution time of the instructions. For example, if you
have an instruction that takes 7 ms to execute, and ask the robot to
run for 5ms, then the instruction cannot be executed and the "actual"
time that the robot ran for is 0 ms. queue
is used for maintaining the queue of
tasks that the
robot has to execute. Tasks are placed at the end of the queue whenever
the method addTask() is called. Tasks are removed from this queue
whenever the method removeTask() is called. RoundRobinRobot
class defines one additional instance
variable:long quantum
quantum
is passed in as a parameter to the
constructor of
RoundRobinRobot
and defines the how long the front task in
the queue
should be run before moving to the next task. NonPreemptiveRobot
and
RoundRobinRobot
for the removeTask()
and run()
functions. run()
function
should basically
follow this pseudocode:while(the time remaining is greater than the quantum and the
queue is not empty) {
dequeue the next task
run the task for the specified quantum
update the remaining time left based on the actual
time that the task was run for (it may run less than the specified
quantum)
reenqueue the task if it isn't done
}
update the total runningTime
The run() method in NonPreemptiveRobot
is slightly
different and left to the student to figure out. Task
RoundRobinRobot
NonPreemptiveRobot
LLEditableQueue
TaskTester
RoundRobinRobotTester
NonPreemptiveRobotTester
LLEditableQueueTester
RoundRobinRobot,
none of the
instructions that we
test your robot with will have an execution time longer than the time
quantum defined for the robot. In other words, whenever you run a task
for a given quantum, it should always execute at least one instruction.
RoundRobinRobot
will not execute "partial"
quantums. That is,
when running a task, it will only run it if there is enough time left
to run it for a full quantum. SampleRoundRobinRobotTester.java.
It should give you
some more concrete
grounding in becoming familiar with how this assignment is designed and
how things are expected to work. Your test cases should cover boundary cases (e.g., no tasks submitted to a robot), as well as "middle of the road" cases.
Test basic methods before complicated ones.
Test simple objects before objects that include or contain the simple objects.
Don't test too many things in a single test case.
Be careful testing static variables (which you should avoid anyway).
Write your test cases early. It's easier to think of test cases when you can remember the troubles you had writing the code, and the process of writing and running tests can also help you to write the code if you do it early.
Re-read the specifications before deciding you're finished with a class. Check the spelling and capitalization of your class names. Check method signatures especially: parameter types and return types, and the spelling of the method name must match exactly. If these don't match the description in the assignment handout your program will not compile and run correctly and the automarker will give a mark of zero.
Write your Javadoc documentation before writing the method it describes. Change it if you realize you've misunderstood. Don't leave documentation to the end, when you no longer remember your code.
Make sure your class names are spelled exactly right, agreeing with the spelling in this handout -- including capitalization -- and make sure your file names match the class names.
Start with the simple operations and the operations that facilitate testing. You may want to restrict the functionality of a class and once the limited version is working add more methods.
Feel free to use private "helper" methods as needed. Document them in the same way you would document public methods.
The expectations in CSC 148H/A48H are much the same as in CSC 108H/A08H: comments explaining complicated code, well-chosen names for variables and methods, clear indentation and spacing, etc. In particular, we expect you to follow the capitalization conventions: variableName, methodName, ClassName, PUBLIC_STATIC_FINAL_NAME.
Do NOT submit anything NOT listed above.
Do NOT make ANY changes to those
classes from the starter that you don't submit.
Do NOT make changes to those classes from to starter code that you are
submitting except where the
assignment calls for it.
You are encouraged (and it's part of the marking scheme) to write helpers when
needed.