assg10.zip

assg10.md

--- title: 'Assignment 10: Priority Queues and Queue Applications' author: 'COSC 2336: Data Structures and Algorithms' date: 'Fall 2020' --- # Objectives - Extend queue ADT to add a priority queue type - Use queue ADT for a job scheduling simulation - Practice with manipulating linked list data structures - Practice with inheritance and class abstractions - Practice with operator overloading - Practice using queue data types for applications and simulations - Introduction to some concepts in scientific simulations an programming # Description In this assignment, we will be building a simulation of a job scheduling and processing system. The purpose of the simulation is to be able to run simulations in order to compare their performance, when they use different queuing disciplines in order to schedule and run jobs in the system as they arrive. We will only be building a simulator that will compare a basic queuing discipline (first-come-first-serve) with a priority queuing discipline. In the system being simulated, jobs will have a priority level assigned to them. It is more costly to keep high priority jobs waiting. Thus we will define an execution cost of a job as \begin{equation} \text{cost} = \text{priority} \times \text{waitTime} \end{equation} In this simulation, higher numbers will represent higher priority jobs. Thus in the formula to compute cost, if a high priority job ends up having a long wait time before it is executed and finishes, then the cost will be high. This is not so much of an issue if a low priority task ends up waiting a long time, or if a high priority task is executed immediately without waiting. In general, we want to run simulations of jobs arriving, and being managed, dispatched and run, using different queuing disciplines. As already mentioned, we will only compare a basic first-come-first-serve queue with a priority queue, but the basic framework you will create and see in this assignment is useful for many kinds of simulation system problems. In this assignment, you are going to be given quite a bit of starting code. We will be using the basic `Queue` data type that was developed in the videos for this weeks content about Queues ("Queue.hpp" and "Queue.cpp"). You will also be given files called "JobSimulator.[hpp|cpp]" which contain definitions already for a `Job` class, and for a `JobSchedulerSimulator` class. A `Job` class/object represents a single job being managed and run in a simulation. The basic characteristics of a Job for our assignment is that it is created or arrives in a system at some `startTime`. The simulator will simulate the creation/arrival of new jobs. New jobs are also randomly assigned a `priority` level and a `serviceTime` upon arrival (again by the simulator). The `serviceTime` is the amount of system time that the `Job` needs in order to complete its work, or in other words it is the amount of resources or time that the system will need to allocate to and be busy with in order to complete the work of the `Job`. The `JobSchedulerSimulator` runs a simulation of jobs arriving, being put on a waiting `jobQueue`, and being dispatched and run when the system is ready to run the next `Job`. Different types of queues will effect the performance of the system and how well it does in reducing the overall and average cost of running jobs on the system. The simulator will simulate time using discrete time steps, starting at time 1, 2, ... up to the `simulationTime` that is specified as one of the simulation parameters. Other parameters of a simulation, besides the type of queuing discipline to use, is the min and max priority range to randomly assign when new jobs arrive, and the min and max amount of serviceTime that newly created jobs will need from the system. You basically have to perform two different parts to complete the simulator you have been given. You have to implement a `PriorityQueue` class (derived from the given `LQueue` linked list based queue class), in order to have a `PriorityQueue` we can use for the simulator. And the main `runSimulation()` method has not been implemented in the `JobSchedulerSimulator` class, you will need to create it according to the description below. **NOTE** You are to use the Queue ADT abstraction give to you for this assignment. If you are familiar with STL queue adapter containers, you are not to use them for this assignment. Part of the assignment is to look over and learn the Stack ADT implementation we give you here based on our textbook Stack examples. # Setup For this assignment you will be given the following files: | File Name | Description | |--------------------|---------------------------------------------------| | `assg10-tests.cpp` | Unit tests for the member functions and queue | | | functions you are to write. | | `JobSimulator.hpp` | Header file for a `JobSimulator` class that uses | | | queues and priority queues to simulate processing | | | jobs from queues. | | `JobSimulator.cpp` | Implementation file, the implementation of the | | | `runSimulation()` function you are to complete goes | | | here in this file. | | `Queue.hpp` | Header file defining a Queue ADT for use in | | | implementing the functions for this assignment. | | `Queue.cpp` | Implementation file for the Queue ADT | | | template class. | Set up a multi-file project to compile the `.cpp` source files and run them as shown for the class. The `Makefile` you were given should be usable to create a build project using the Atom editor as required in this class. The general approach you should take for this assignment, and all assignment is: 1. Set up your project with the given starting code. The files should compile and run, but either no tests will be run, or tests will run but be failing. 2. For this project, start by uncommenting the first `TEST_CASE` in the `assg10-tests.cpp` file. These are the unit tests to test the functionality of your `PriorityQueue` `enqueue()` function, the class and overridden member function you are to implement. 3. Add the `PriorityQueue` class to the `Queue.hpp` header file. The class should inherit from `LQueue` and override the `enqueue()` method as described in more detail below. 4. Add a stub for your `enqueue()` member function to the `assg07-stackfun.cpp` implementation file. You could start by doing nothing, or by copying the code of the enqueue() function from the `LQueue` class. 5. Your code should compile and run now. Make sure after adding the class and your stub method your code compiles and runs. However, your unit tests will be failing initially. 6. Incrementally implement the functionality of your `enqueue()` member function. You should try to add no more than 2 or 3 lines of code, and then make sure your program still compiles and runs. Start by adding code to get the first failing test to pass. Then once that test passes, move on to the next failing tests until you have all tests passing. If you write something that causes a previously passing test to fail, you should stop and figure out why, and either fix it so that the original test still passes, or remove what you did and try a new approach. 7. Once you have the `enqueue()` member function implemented and all unit tests passing, you should then move on to the other functions in the order suggested. Some functions use previous ones in this assignment, so do them in the order given for you in the tasks below. # Tasks You should set up your project/code as described in the previous section. In this section we give some more details on implementing the member functions for this assignment. You should perform the following tasks for this assignment: 1. Create a `PriorityQueue` class and implement a new `enqueue()` method to implement a priority queuing discipline. For efficiency, priority queues are often implemented using a heap, so that both insertion and removal of items can be done in $\mathcal{O}(\log n)$ time. However, we are going to use a simple naive method and simply insert new items into a link list that we keep sorted by priority. Doing this means that the insertion (`enqueue()`) operation becomes $\mathcal{O}(n)$, but since for a priority queue we always want the next item with the highest priority for a `dequeue()`, `dequeue()` is still $\mathcal{O}(1)$ constant time, since we will keep items ordered by priority and the highest priority item in the queue should always be at the front of the list. This may sound like a lot, but there is really not too much to do to get a basic priority queue working. You need to perform the following steps 1. Create a new class called `PriorityQueue` that inherits (using public inheritance) and derives from the `LQueue` class. This class still needs to be a template class, as it needs to be able to handle queues of different types of objects. I have set the member variables of the `LQueue` class to be protected, which means that your `PriorityQueue` class methods will be able to access and refer to the `queueFront` and `queueBack` member variables. You should insert your class definition for your `PriorityQueue` at the end of the "Queue.hpp" file. The only method you need to implement/override is the `enqueue()` method. All other methods should work correctly using the `LQueue()` implementations you will inherit. But since you are overriding `enqueue()` you do need to put a new declaration for this function in your `PriorityQueue` class declaration, but this will be the only function or variable (re)defined in the `PriorityQueue`. 1. Once you have the declaration working, you will need to create your implementation of the `enqueue()` method for the `PriorityQueue`. As we already mentioned, instead of always just inserting the new item/node at the back of the queue, you instead need to do some some extra work and insert the new node into the linked list at the proper position so that the linked list is ordered by priority. You can assume that the objects inserted into a `PriorityQueue` are overloaded so that boolean comparisons (like operator<, operator<=, operator>, etc.) are defined to order object by their priority. The `Job` class that you will be managing with your `PriorityQueue` has had these operators defined to order jobs by their priority level. The pseudo-code for the algorithm your `enqueue()` function needs to perform is as follow: ``` 0. Create a new Node dynamically, and assign the newItem as its item. You may need to set link to NULL and don't forget to update the numitems variable of the LQueue parent as well. 1. Case 1, if the queue is empty, then the new node becomes both the queueFront and the queueBack. You don't have to do anything else, because a list of only 1 item is already sorted. 2. Case 2, if the priority of the newItem is bigger than the priority of the queueFront, then simply make the newNode the new queueFront (and don't forget to link the newNode back to the old front item). 3. Case 3, if the newNode priority is less than or equal to the front node, then it is going to end up somewhere in the middle (or end) of the linked list. Here you need a loop to search for the correct position. You need to keep following links until the current node is greater or equal in priority to the newNode, and the node it links to is smaller priority. a. Once you have found that position, you need to link the newNode between the two nodes, in the correct position to keep the list sorted. b. You may or may not need to do something special to detect if you are linking as the last node, so it becomes the new queueBack. For the priority queue, you don't need the queueBack pointer anymore, since you aren't inserting on the end, so you can safely ignore the queueBack member variable for your priority queue. But you do need to make sure if your newNode ends up being the last node, that its link is properly NULL terminated. ``` You should try and implement the cases in order, and uncomment out the individual tests of the `PriorityQueue` given one by one to test them. 1. This task is for 25 points of extra credit. It is not required, but if you have time and would like a chance to make up some missed points you can attempt. Once you have your `PriorityQueue` working, and it is passing all of the tests given for it, you should then try and implement the `runSimulation()` class method. This is a member of the `JobSchedulerSimulator` class. You need to add the function prototype for this function in the "JobSimulator.hpp" file, and add the implementation of the function at the end of the "JobSimulator.cpp" file. There are 2 examples of how this function should be called in the tests give to you. The `runSimulation()` method takes a `Queue<Job>`, a queue class derived from the `Queue` base class, that is parameterized to hold objects of type `Job`, as its first parameter. The second parameter to the function is a simple string `description` of the simulation type/queue that will be run, for display purposes. You should assign the class member variable this value of the passed in `description` as one of the things you din in the `runSimulation()` method. We already briefly described the `runSimulation()` method. Here is the pseudo-code of what you need to implement in this method: ``` for time in range 1..simulanTime do // 1. process new job creation, use jobArrived() // to check this if job arrived then - generate a random priority (using generateRandomPriority() ) - generate a random serviceTime (generateRandomServiceTime() ) - create a new instance of a Job - enqueue the job on the job queue passed in to this function endif // 2. dispatch jobs to run on system if no job is running and job queue is not empty then - dequeue the next job from the job queue - set remaining time for this job to its service time endif // 3. If job is currently running, update remaining time if job running then - decrement the jobs remaining time // handle when job has finished here if job is now finished then - update stats on number of jobs completed - update totalWaitTime - update totalCost endif endif done ``` The order of execution of the 3 steps is important in the simulation. If no job is running at the start of the time step, it should be possible for a new job to enter at that time step, it be immediately dispatched, and it executes for 1 simulated time step. Thus you have to check the arrival, dispatch and update in that order. After your main loop simulating the system time steps, you may have to do some more things to calculate your final statistics, which is not shown. For example, when a job completes, you should keep track of the number of jobs that have completed so far, and keep track of the total wait time and total cost. You need these sums so you can calculate the average wait time and the average cost after the simulation ends. These averages should be calculated only for the jobs that successfully completed before the simulation ended. There is also a parameter named `numJobsUnfinished` in the simulator, which is the number of jobs still on the job queue when the simulation ends that you should fill in as well. # Example Output Here is the correct output you should get from your program if you correctly implement all the class functions and successfully pass all of the unit tests given for this assignment. If you invoke your function with no command line arguments, only failing tests are usually shown by default. In the second example, we use the -s command line option to have the unit test framework show both successful and failing tests, and thus we get reports of all of the successfully passing tests as well on the output. ``` $ ./test =============================================================================== All tests passed (251 assertions in 4 test cases) $ ./test -s ------------------------------------------------------------------------------- test is a Catch v2.7.2 host application. Run with -? for options ------------------------------------------------------------------------------- <basic Queue> test basic Queue functionality ------------------------------------------------------------------------------- assg10-tests.cpp:30 ............................................................................... assg10-tests.cpp:38: PASSED: CHECK( ilq.isEmpty() ) with expansion: true ... output snipped ... =============================================================================== All tests passed (251 assertions in 4 test cases) ``` If you attempt the extra credit, an example of testing your `runSimulation()` method is given in the `assg10-main.cpp` file. Uncomment that example, and use the `debug` target to test calling and running your function. Example output for how your results should look if you implement the function correctly should look like this: ``` $ ./debug Job Scheduler Simulation Results -------------------------------- Simulation Parameters -------------------------- Description : Normal (non-priority based) queueing discipline Simulation Time : 10000 Job Arrival Probability : 0.1 Priority (min,max) : (1, 10) Service Time (min,max) : (5, 15) Simulation Results -------------------------- Number of jobs started : 990 Number of jobs completed : 956 Number of jobs unfinished: 34 Total Wait Time : 165051 Total Cost : 906378 Average Wait Time : 172.6475 Average Cost : 948.0941 Job Scheduler Simulation Results -------------------------------- Simulation Parameters -------------------------- Description : Priority queueing discipline Simulation Time : 10000 Job Arrival Probability : 0.1 Priority (min,max) : (1, 10) Service Time (min,max) : (5, 15) Simulation Results -------------------------- Number of jobs started : 990 Number of jobs completed : 956 Number of jobs unfinished: 34 Total Wait Time : 113251 Total Cost : 204155 Average Wait Time : 118.4634 Average Cost : 213.5513 ``` # Assignment Submission A MyLeoOnline submission folder has been created for this assignment. There is a target named `submit` that will create a tared and gziped file named `assg02.tar.gz`. You should do a `make submit` when finished and upload your resulting gzip file to the MyLeoOnline Submission folder for this assignment. ``` $ make submit tar cvfz assg10.tar.gz assg10-tests.cpp assg10-main.cpp Queue.hpp Queue.cpp JobSimulator.hpp JobSimulator.cpp assg10-tests.cpp assg10-main.cpp Queue.hpp Queue.cpp JobSimulator.hpp JobSimulator.cpp ``` # Requirements and Grading Rubrics ## Program Execution, Output and Functional Requirements 1. Your program must compile, run and produce some sort of output to be graded. 0 if not satisfied. 1. (100 pts.) `PriorityQueue` is implemented correctly. - Correctly derived class from `LQueue` base class. - Class is still a template class - Correctly specify prototype to override `enqueue()` method of base class. - `enqueue()` implementation works for the empty list case - `enqueue()` works to insert node at front when it is highest priority - `enqueue()` works to insert node in list in correct order by priority - `enqueue()` works when inserting node with lowest priority at back of list 1. (25 bouns pts.) `runSimulation()` function is implemented correctly. - Correctly creating new jobs and assigning random priority and service time on Poisson random arrival. - Correctly dispatching jobs from queue when no job is running - Correctly simulating job running, keeping track of remaining - Correctly update statistics when jobs finish. - All simulation steps and statistics look correctly calculated. ## Program Style Your programs must conform to the style and formatting guidelines given for this class. The following is a list of the guidelines that are required for the assignment to be submitted this week. 1. Most importantly, make sure you figure out how to set your indentation settings correctly. All programs must use 2 spaces for all indentation levels, and all indentation levels must be correctly indented. Also all tabs must be removed from files, and only 2 spaces used for indentation. 1. A function header must be present for member functions you define. You must give a short description of the function, and document all of the input parameters to the function, as well as the return value and data type of the function if it returns a value for the member functions, just like for regular functions. However, setter and getter methods do not require function headers. 1. You should have a document header for your class. The class header document should give a description of the class. Also you should document all private member variables that the class manages in the class document header. 1. Do not include any statements (such as `system("pause")` or inputting a key from the user to continue) that are meant to keep the terminal from going away. Do not include any code that is specific to a single operating system, such as the `system("pause")` which is Microsoft Windows specific.

assg10.pdf

Assignment 10: Priority Queues and Queue Applications

COSC 2336: Data Structures and Algorithms

Fall 2020

Objectives • Extend queue ADT to add a priority queue type • Use queue ADT for a job scheduling simulation • Practice with manipulating linked list data structures • Practice with inheritance and class abstractions • Practice with operator overloading • Practice using queue data types for applications and simulations • Introduction to some concepts in scientific simulations an programming

Description In this assignment, we will be building a simulation of a job scheduling and processing system. The purpose of the simulation is to be able to run simulations in order to compare their performance, when they use different queuing disciplines in order to schedule and run jobs in the system as they arrive. We will only be building a simulator that will compare a basic queuing discipline (first-come-first-serve) with a priority queuing discipline. In the system being simulated, jobs will have a priority level assigned to them. It is more costly to keep high priority jobs waiting. Thus we will define an execution cost of a job as

cost = priority×waitTime (1)

In this simulation, higher numbers will represent higher priority jobs. Thus in the formula to compute cost, if a high priority job ends up having a long wait time before it is executed and finishes, then the cost will be high. This is not so much of an issue if a low priority task ends up waiting a long time, or if a high priority task is executed immediately without waiting.

In general, we want to run simulations of jobs arriving, and being managed, dispatched and run, using different queuing disciplines. As already mentioned, we will only compare a basic first-come-first-serve queue with a priority queue, but the basic framework you will create and see in this assignment is useful for many kinds of simulation system problems.

In this assignment, you are going to be given quite a bit of starting code. We will be using the basic Queue data type that was developed in the videos for this weeks content about Queues (“Queue.hpp” and “Queue.cpp”). You will also be given files called “JobSimulator.[hpp|cpp]” which contain definitions already for a Job class, and for a JobSchedulerSimulator class. A Job class/object represents a single job being managed and run in a simulation. The basic characteristics of a Job for our assignment is that it is created or arrives in a system at some startTime. The simulator will simulate the creation/arrival of new jobs. New jobs are also randomly assigned a priority level and a serviceTime upon arrival (again by the simulator). The serviceTime is the amount of system time that the Job needs in order to complete its work, or in other words it is the amount of resources or time that the system will need to allocate to and be busy with in order to complete the work of the Job.

The JobSchedulerSimulator runs a simulation of jobs arriving, being put on a waiting jobQueue, and being dispatched and run when the system is ready to run the next Job. Different types of queues will effect the performance of the system and how well it does in reducing the overall and average cost of running jobs on the system. The simulator will simulate time using discrete time steps, starting at time 1, 2, . . . up to the simulationTime that is

1

specified as one of the simulation parameters. Other parameters of a simulation, besides the type of queuing discipline to use, is the min and max priority range to randomly assign when new jobs arrive, and the min and max amount of serviceTime that newly created jobs will need from the system.

You basically have to perform two different parts to complete the simulator you have been given. You have to implement a PriorityQueue class (derived from the given LQueue linked list based queue class), in order to have a PriorityQueue we can use for the simulator. And the main runSimulation() method has not been implemented in the JobSchedulerSimulator class, you will need to create it according to the description below.

NOTE You are to use the Queue ADT abstraction give to you for this assignment. If you are familiar with STL queue adapter containers, you are not to use them for this assignment. Part of the assignment is to look over and learn the Stack ADT implementation we give you here based on our textbook Stack examples.

Setup For this assignment you will be given the following files:

File Name Description assg10-tests.cpp Unit tests for the member functions and queue

functions you are to write. JobSimulator.hpp Header file for a JobSimulator class that uses

queues and priority queues to simulate processing jobs from queues.

JobSimulator.cpp Implementation file, the implementation of the runSimulation() function you are to complete goes here in this file.

Queue.hpp Header file defining a Queue ADT for use in implementing the functions for this assignment.

Queue.cpp Implementation file for the Queue ADT template class.

Set up a multi-file project to compile the .cpp source files and run them as shown for the class. The Makefile you were given should be usable to create a build project using the Atom editor as required in this class.

The general approach you should take for this assignment, and all assignment is:

1. Set up your project with the given starting code. The files should compile and run, but either no tests will be run, or tests will run but be failing.

2. For this project, start by uncommenting the first TEST_CASE in the assg10-tests.cpp file. These are the unit tests to test the functionality of your PriorityQueue enqueue() function, the class and overridden member function you are to implement.

3. Add the PriorityQueue class to the Queue.hpp header file. The class should inherit from LQueue and override the enqueue() method as described in more detail below.

4. Add a stub for your enqueue() member function to the assg07-stackfun.cpp implementation file. You could start by doing nothing, or by copying the code of the enqueue() function from the LQueue class.

5. Your code should compile and run now. Make sure after adding the class and your stub method your code compiles and runs. However, your unit tests will be failing initially.

6. Incrementally implement the functionality of your enqueue() member function. You should try to add no more than 2 or 3 lines of code, and then make sure your program still compiles and runs. Start by adding code to get the first failing test to pass. Then once that test passes, move on to the next failing tests until you have all tests passing. If you write something that causes a previously passing test to fail, you should stop and figure out why, and either fix it so that the original test still passes, or remove what you did and try a new approach.

7. Once you have the enqueue() member function implemented and all unit tests passing, you should then move on to the other functions in the order suggested. Some functions use previous ones in this assignment, so do them in the order given for you in the tasks below.

2

Tasks You should set up your project/code as described in the previous section. In this section we give some more details on implementing the member functions for this assignment. You should perform the following tasks for this assignment:

1. Create a PriorityQueue class and implement a new enqueue() method to implement a priority queuing discipline. For efficiency, priority queues are often implemented using a heap, so that both insertion and removal of items can be done in O(log n) time. However, we are going to use a simple naive method and simply insert new items into a link list that we keep sorted by priority. Doing this means that the insertion (enqueue()) operation becomes O(n), but since for a priority queue we always want the next item with the highest priority for a dequeue(), dequeue() is still O(1) constant time, since we will keep items ordered by priority and the highest priority item in the queue should always be at the front of the list.

This may sound like a lot, but there is really not too much to do to get a basic priority queue working. You need to perform the following steps

1. Create a new class called PriorityQueue that inherits (using public inheritance) and derives from the LQueue class. This class still needs to be a template class, as it needs to be able to handle queues of different types of objects. I have set the member variables of the LQueue class to be protected, which means that your PriorityQueue class methods will be able to access and refer to the queueFront and queueBack member variables. You should insert your class definition for your PriorityQueue at the end of the “Queue.hpp” file.

The only method you need to implement/override is the enqueue() method. All other methods should work correctly using the LQueue() implementations you will inherit. But since you are overriding enqueue() you do need to put a new declaration for this function in your PriorityQueue class declaration, but this will be the only function or variable (re)defined in the PriorityQueue.

2. Once you have the declaration working, you will need to create your implementation of the enqueue() method for the PriorityQueue. As we already mentioned, instead of always just inserting the new item/node at the back of the queue, you instead need to do some some extra work and insert the new node into the linked list at the proper position so that the linked list is ordered by priority. You can assume that the objects inserted into a PriorityQueue are overloaded so that boolean comparisons (like operator<, operator<=, operator>, etc.) are defined to order object by their priority. The Job class that you will be managing with your PriorityQueue has had these operators defined to order jobs by their priority level. The pseudo-code for the algorithm your enqueue() function needs to perform is as follow:

0. Create a new Node dynamically, and assign the newItem as its item. You may need to set link to NULL and don't forget to update the numitems variable of the LQueue parent as well.

1. Case 1, if the queue is empty, then the new node becomes both the queueFront and the queueBack. You don't have to do anything else, because a list of only 1 item is already sorted.

2. Case 2, if the priority of the newItem is bigger than the priority of the queueFront, then simply make the newNode the new queueFront (and don't forget to link the newNode back to the old front item).

3. Case 3, if the newNode priority is less than or equal to the front node, then it is going to end up somewhere in the middle (or end) of the linked list. Here you need a loop to search for the correct position. You need to keep following links until the current node is greater or equal in priority to the newNode, and the node it links

3

to is smaller priority.

a. Once you have found that position, you need to link the newNode between the two nodes, in the correct position to keep the list sorted.

b. You may or may not need to do something special to detect if you are linking as the last node, so it becomes the new queueBack. For the priority queue, you don't need the queueBack pointer anymore, since you aren't inserting on the end, so you can safely ignore the queueBack member variable for your priority queue. But you do need to make sure if your newNode ends up being the last node, that its link is properly NULL terminated.

You should try and implement the cases in order, and uncomment out the individual tests of the PriorityQueue given one by one to test them.

3. This task is for 25 points of extra credit. It is not required, but if you have time and would like a chance to make up some missed points you can attempt.

Once you have your PriorityQueue working, and it is passing all of the tests given for it, you should then try and implement the runSimulation() class method. This is a member of the JobSchedulerSimulator class. You need to add the function prototype for this function in the “JobSimulator.hpp” file, and add the implementation of the function at the end of the “JobSimulator.cpp” file.

There are 2 examples of how this function should be called in the tests give to you. The runSimulation() method takes a Queue<Job>, a queue class derived from the Queue base class, that is parameterized to hold objects of type Job, as its first parameter. The second parameter to the function is a simple string description of the simulation type/queue that will be run, for display purposes. You should assign the class member variable this value of the passed in description as one of the things you din in the runSimulation() method.

We already briefly described the runSimulation() method. Here is the pseudo-code of what you need to implement in this method:

for time in range 1..simulanTime do

// 1. process new job creation, use jobArrived() // to check this

if job arrived then

- generate a random priority (using generateRandomPriority() )

- generate a random serviceTime (generateRandomServiceTime() )

- create a new instance of a Job - enqueue the job on the job queue

passed in to this function endif

// 2. dispatch jobs to run on system if no job is running and job queue is not empty then

- dequeue the next job from the job queue - set remaining time for this job to its service time

endif

4

// 3. If job is currently running, update remaining time if job running then

- decrement the jobs remaining time

// handle when job has finished here if job is now finished then

- update stats on number of jobs completed - update totalWaitTime - update totalCost

endif endif

done

The order of execution of the 3 steps is important in the simulation. If no job is running at the start of the time step, it should be possible for a new job to enter at that time step, it be immediately dispatched, and it executes for 1 simulated time step. Thus you have to check the arrival, dispatch and update in that order.

After your main loop simulating the system time steps, you may have to do some more things to calculate your final statistics, which is not shown. For example, when a job completes, you should keep track of the number of jobs that have completed so far, and keep track of the total wait time and total cost. You need these sums so you can calculate the average wait time and the average cost after the simulation ends. These averages should be calculated only for the jobs that successfully completed before the simulation ended. There is also a parameter named numJobsUnfinished in the simulator, which is the number of jobs still on the job queue when the simulation ends that you should fill in as well.

Example Output Here is the correct output you should get from your program if you correctly implement all the class functions and successfully pass all of the unit tests given for this assignment. If you invoke your function with no command line arguments, only failing tests are usually shown by default. In the second example, we use the -s command line option to have the unit test framework show both successful and failing tests, and thus we get reports of all of the successfully passing tests as well on the output.

$ ./test =============================================================================== All tests passed (251 assertions in 4 test cases)

$ ./test -s

------------------------------------------------------------------------------- test is a Catch v2.7.2 host application. Run with -? for options

------------------------------------------------------------------------------- <basic Queue> test basic Queue functionality ------------------------------------------------------------------------------- assg10-tests.cpp:30 ...............................................................................

assg10-tests.cpp:38: PASSED: CHECK( ilq.isEmpty() )

5

with expansion: true

... output snipped ...

=============================================================================== All tests passed (251 assertions in 4 test cases)

If you attempt the extra credit, an example of testing your runSimulation() method is given in the assg10-main.cpp file. Uncomment that example, and use the debug target to test calling and running your function. Example output for how your results should look if you implement the function correctly should look like this:

$ ./debug Job Scheduler Simulation Results -------------------------------- Simulation Parameters -------------------------- Description : Normal (non-priority based) queueing discipline Simulation Time : 10000 Job Arrival Probability : 0.1 Priority (min,max) : (1, 10) Service Time (min,max) : (5, 15)

Simulation Results -------------------------- Number of jobs started : 990 Number of jobs completed : 956 Number of jobs unfinished: 34 Total Wait Time : 165051 Total Cost : 906378 Average Wait Time : 172.6475 Average Cost : 948.0941

Job Scheduler Simulation Results -------------------------------- Simulation Parameters -------------------------- Description : Priority queueing discipline Simulation Time : 10000 Job Arrival Probability : 0.1 Priority (min,max) : (1, 10) Service Time (min,max) : (5, 15)

Simulation Results -------------------------- Number of jobs started : 990 Number of jobs completed : 956 Number of jobs unfinished: 34 Total Wait Time : 113251 Total Cost : 204155 Average Wait Time : 118.4634 Average Cost : 213.5513

6

Assignment Submission A MyLeoOnline submission folder has been created for this assignment. There is a target named submit that will create a tared and gziped file named assg02.tar.gz. You should do a make submit when finished and upload your resulting gzip file to the MyLeoOnline Submission folder for this assignment.

$ make submit tar cvfz assg10.tar.gz assg10-tests.cpp assg10-main.cpp

Queue.hpp Queue.cpp JobSimulator.hpp JobSimulator.cpp assg10-tests.cpp assg10-main.cpp Queue.hpp Queue.cpp JobSimulator.hpp JobSimulator.cpp

Requirements and Grading Rubrics Program Execution, Output and Functional Requirements

1. Your program must compile, run and produce some sort of output to be graded. 0 if not satisfied. 2. (100 pts.) PriorityQueue is implemented correctly.

• Correctly derived class from LQueue base class. • Class is still a template class • Correctly specify prototype to override enqueue() method of base class. • enqueue() implementation works for the empty list case • enqueue() works to insert node at front when it is highest priority • enqueue() works to insert node in list in correct order by priority • enqueue() works when inserting node with lowest priority at back of list

3. (25 bouns pts.) runSimulation() function is implemented correctly. • Correctly creating new jobs and assigning random priority and service time on Poisson random arrival. • Correctly dispatching jobs from queue when no job is running • Correctly simulating job running, keeping track of remaining • Correctly update statistics when jobs finish. • All simulation steps and statistics look correctly calculated.

Program Style Your programs must conform to the style and formatting guidelines given for this class. The following is a list of the guidelines that are required for the assignment to be submitted this week.

1. Most importantly, make sure you figure out how to set your indentation settings correctly. All programs must use 2 spaces for all indentation levels, and all indentation levels must be correctly indented. Also all tabs must be removed from files, and only 2 spaces used for indentation.

2. A function header must be present for member functions you define. You must give a short description of the function, and document all of the input parameters to the function, as well as the return value and data type of the function if it returns a value for the member functions, just like for regular functions. However, setter and getter methods do not require function headers.

3. You should have a document header for your class. The class header document should give a description of the class. Also you should document all private member variables that the class manages in the class document header.

4. Do not include any statements (such as system("pause") or inputting a key from the user to continue) that are meant to keep the terminal from going away. Do not include any code that is specific to a single operating system, such as the system("pause") which is Microsoft Windows specific.

7

  • Objectives
  • Description
  • Setup
  • Tasks
  • Example Output
  • Assignment Submission
  • Requirements and Grading Rubrics
    • Program Execution, Output and Functional Requirements
    • Program Style

assg10-main.cpp

assg10-main.cpp

/**  @file  assg10-main.cpp
 *  @brief  main/debug executable for Assignment 10 queues and priority queues.
 *
 *  @author  Jane Programmer
 *  @note    cwid : 123 45 678
 *  @note    class: COSC 2336, Summer 2020
 *  @note    ide  : Atom Text Editor 1.46.0 / build package / GNU gcc tools
 *  @note    assg : Assignment 10
 *  @date    June 1, 2020
 *
 * Assignment 10 queues and priority queues.  Use the given Queue ADT to create
 * a new PriorityQueue ADT, then use queues and your priority queue to create a
 * random job processing simulation. This file contains a main() function we can
 * use to build a debug target for debugging.
 */
#include   < iostream >
#include   "Queue.hpp"
#include   "JobSimulator.hpp"
using   namespace  std ;


/** main
 * The main entry point for this program.  Execution of this program
 * will begin with this main function.
 *
 *  @param  argc The command line argument count which is the number of
 *     command line arguments provided by user when they started
 *     the program.
 *  @param  argv The command line arguments, an array of character
 *     arrays.
 *
 *  @returns  An int value indicating program exit status.  Usually 0
 *     is returned to indicate normal exit and a non-zero value
 *     is returned to indicate an error condition.
 */
int  main ( int  argc ,   char **  argv )
{
   // example of invoking some of the functions to debug them
   JobSchedulerSimulator  sim ;

   // seed random number generator so we can recreate sequence of jobs and
   // priorities that are randomly generated
   int  seed  =   32 ;
  srand ( seed );

   // test with a non priority queue first
   LQueue < Job >  jobQueue ;
   //sim.runSimulation(jobQueue, "Normal (non-priority based) queueing discipline");
  cout  <<  sim ;


   // reseed with same seed, in theory we should end up with the same sequence
   // of random jobs, but just handled with a priorty queue now
  srand ( seed );

   // test with a priority queue
   //PriorityQueue<Job> jobPriorityQueue;
   //sim.runSimulation(jobPriorityQueue, "Priority queueing discipline");
  cout  <<  sim ;


   // return 0 to indicate successful completion of program
   return   0 ;
}

assg10-tests.cpp

assg10-tests.cpp

/**  @file  assg10-tests.cpp
 *  @brief  Unit tests for Assignment 10 queues and priority queues.
 *
 *  @author  Jane Programmer
 *  @note    cwid : 123 45 678
 *  @note    class: COSC 2336, Summer 2020
 *  @note    ide  : Atom Text Editor 1.46.0 / build package / GNU gcc tools
 *  @note    assg : Assignment 10
 *  @date    June 1, 2020
 *
 * Assignment 10 queues and priority queues.  Use the given Queue ADT to create
 * a new PriorityQueue ADT, then use queues and your priority queue to create a
 * random job processing simulation. This file has catch2 unit tests you need to
 * test and implement the functions for your assignment.
 */
#define  CATCH_CONFIG_MAIN
#include   "catch.hpp"
#include   "Queue.hpp"
#include   "JobSimulator.hpp"
using   namespace  std ;



/** test basic queue functions() functions.  Just some general tests
 * of the Queue implementation given to you, to make sure nothing
 * breaks while trying to add/implement new functionality to the Queue.
 */
TEST_CASE ( "<basic Queue> test basic Queue functionality" ,
           "[class]" )
{
   //--------------------------------------------------------------------
   // use a linked list queue implementation of ints
   LQueue < int >  ilq ;

   // initial queue should be empty
  CHECK (  ilq . isEmpty ()   );
  CHECK (  ilq . length ()   ==   0   );
  CHECK (  ilq . tostring ()   ==   "Front: :Back\n"   );
  CHECK_THROWS (  ilq . front (),   EmptyQueueException ()   );
  CHECK_THROWS (  ilq . dequeue (),   EmptyQueueException ()   );

   // add item to empty queue
  ilq . enqueue ( 5 );
  CHECK_FALSE (  ilq . isEmpty ()   );
  CHECK (  ilq . length ()   ==   1   );
  CHECK (  ilq . tostring ()   ==   "Front: 5 :Back\n"   );
  CHECK (  ilq . front ()   ==   5   );

   // add item to non empty queue
  ilq . enqueue ( 7 );
  CHECK_FALSE (  ilq . isEmpty ()   );
  CHECK (  ilq . length ()   ==   2   );
  CHECK (  ilq . tostring ()   ==   "Front: 5 7 :Back\n"   );
  CHECK (  ilq . front ()   ==   5   );

   // add another item to non empty queue
  ilq . enqueue ( 3 );
  CHECK_FALSE (  ilq . isEmpty ()   );
  CHECK (  ilq . length ()   ==   3   );
  CHECK (  ilq . tostring ()   ==   "Front: 5 7 3 :Back\n"   );
  CHECK (  ilq . front ()   ==   5   );

   // test dequeu
  ilq . dequeue ();
  CHECK_FALSE (  ilq . isEmpty ()   );
  CHECK (  ilq . length ()   ==   2   );
  CHECK (  ilq . tostring ()   ==   "Front: 7 3 :Back\n"   );
  CHECK (  ilq . front ()   ==   7   );

  ilq . dequeue ();
  CHECK_FALSE (  ilq . isEmpty ()   );
  CHECK (  ilq . length ()   ==   1   );
  CHECK (  ilq . tostring ()   ==   "Front: 3 :Back\n"   );
  CHECK (  ilq . front ()   ==   3   );

   // test dequeue to make queue become empty again
  ilq . dequeue ();
  CHECK (  ilq . isEmpty ()   );
  CHECK (  ilq . length ()   ==   0   );
  CHECK (  ilq . tostring ()   ==   "Front: :Back\n"   );
  CHECK_THROWS (  ilq . front (),   EmptyQueueException ()   );

   // test clear of queue
  ilq . enqueue ( 1 );
  ilq . enqueue ( 2 );
  CHECK_FALSE (  ilq . isEmpty ()   );
  CHECK (  ilq . length ()   ==   2   );
  CHECK (  ilq . tostring ()   ==   "Front: 1 2 :Back\n"   );

  ilq . clear ();
  CHECK (  ilq . isEmpty ()   );
  CHECK (  ilq . length ()   ==   0   );
  CHECK (  ilq . tostring ()   ==   "Front: :Back\n"   );

   //--------------------------------------------------------------------
   // use a linked list queue implementation of strings
   LQueue < string >  slq ;

   // initial queue should be empty
  CHECK (  slq . isEmpty ()   );
  CHECK (  slq . length ()   ==   0   );
  CHECK (  slq . tostring ()   ==   "Front: :Back\n"   );
  CHECK_THROWS (  slq . front (),   EmptyQueueException ()   );
  CHECK_THROWS (  slq . dequeue (),   EmptyQueueException ()   );

   // add item to empty queue
  slq . enqueue ( "echo" );
  CHECK_FALSE (  slq . isEmpty ()   );
  CHECK (  slq . length ()   ==   1   );
  CHECK (  slq . tostring ()   ==   "Front: echo :Back\n"   );
  CHECK (  slq . front ()   ==   "echo"   );

   // add item to non empty queue
  slq . enqueue ( "hotel" );
  CHECK_FALSE (  slq . isEmpty ()   );
  CHECK (  slq . length ()   ==   2   );
  CHECK (  slq . tostring ()   ==   "Front: echo hotel :Back\n"   );
  CHECK (  slq . front ()   ==   "echo"   );

   // add another item to non empty queue
  slq . enqueue ( "bravo" );
  CHECK_FALSE (  slq . isEmpty ()   );
  CHECK (  slq . length ()   ==   3   );
  CHECK (  slq . tostring ()   ==   "Front: echo hotel bravo :Back\n"   );
  CHECK (  slq . front ()   ==   "echo"   );

   // test dequeu
  slq . dequeue ();
  CHECK_FALSE (  slq . isEmpty ()   );
  CHECK (  slq . length ()   ==   2   );
  CHECK (  slq . tostring ()   ==   "Front: hotel bravo :Back\n"   );
  CHECK (  slq . front ()   ==   "hotel"   );

  slq . dequeue ();
  CHECK_FALSE (  slq . isEmpty ()   );
  CHECK (  slq . length ()   ==   1   );
  CHECK (  slq . tostring ()   ==   "Front: bravo :Back\n"   );
  CHECK (  slq . front ()   ==   "bravo"   );

   // test dequeue to make queue become empty again
  slq . dequeue ();
  CHECK (  slq . isEmpty ()   );
  CHECK (  slq . length ()   ==   0   );
  CHECK (  slq . tostring ()   ==   "Front: :Back\n"   );
  CHECK_THROWS (  slq . front (),   EmptyQueueException ()   );

   // test clear of queue
  slq . enqueue ( "alpha" );
  slq . enqueue ( "bravo" );
  CHECK_FALSE (  slq . isEmpty ()   );
  CHECK (  slq . length ()   ==   2   );
  CHECK (  slq . tostring ()   ==   "Front: alpha bravo :Back\n"   );

  slq . clear ();
  CHECK (  slq . isEmpty ()   );
  CHECK (  slq . length ()   ==   0   );
  CHECK (  slq . tostring ()   ==   "Front: :Back\n"   );


   //--------------------------------------------------------------------
   // use an array queue implementation of ints
   AQueue < int >  iaq ;

   // initial queue should be empty
  CHECK (  iaq . isEmpty ()   );
  CHECK (  iaq . length ()   ==   0   );
  CHECK (  iaq . tostring ()   ==   "Front: :Back\n"   );
  CHECK_THROWS (  iaq . front (),   EmptyQueueException ()   );
  CHECK_THROWS (  iaq . dequeue (),   EmptyQueueException ()   );

   // add item to empty queue
  iaq . enqueue ( 5 );
  CHECK_FALSE (  iaq . isEmpty ()   );
  CHECK (  iaq . length ()   ==   1   );
  CHECK (  iaq . tostring ()   ==   "Front: 5 :Back\n"   );
  CHECK (  iaq . front ()   ==   5   );

   // add item to non empty queue
  iaq . enqueue ( 7 );
  CHECK_FALSE (  iaq . isEmpty ()   );
  CHECK (  iaq . length ()   ==   2   );
  CHECK (  iaq . tostring ()   ==   "Front: 5 7 :Back\n"   );
  CHECK (  iaq . front ()   ==   5   );

   // add another item to non empty queue
  iaq . enqueue ( 3 );
  CHECK_FALSE (  iaq . isEmpty ()   );
  CHECK (  iaq . length ()   ==   3   );
  CHECK (  iaq . tostring ()   ==   "Front: 5 7 3 :Back\n"   );
  CHECK (  iaq . front ()   ==   5   );

   // test dequeu
  iaq . dequeue ();
  CHECK_FALSE (  iaq . isEmpty ()   );
  CHECK (  iaq . length ()   ==   2   );
  CHECK (  iaq . tostring ()   ==   "Front: 7 3 :Back\n"   );
  CHECK (  iaq . front ()   ==   7   );

  iaq . dequeue ();
  CHECK_FALSE (  iaq . isEmpty ()   );
  CHECK (  iaq . length ()   ==   1   );
  CHECK (  iaq . tostring ()   ==   "Front: 3 :Back\n"   );
  CHECK (  iaq . front ()   ==   3   );

   // test dequeue to make queue become empty again
  iaq . dequeue ();
  CHECK (  iaq . isEmpty ()   );
  CHECK (  iaq . length ()   ==   0   );
  CHECK (  iaq . tostring ()   ==   "Front: :Back\n"   );
  CHECK_THROWS (  iaq . front (),   EmptyQueueException ()   );

   // test clear of queue
  iaq . enqueue ( 1 );
  iaq . enqueue ( 2 );
  CHECK_FALSE (  iaq . isEmpty ()   );
  CHECK (  iaq . length ()   ==   2   );
  CHECK (  iaq . tostring ()   ==   "Front: 1 2 :Back\n"   );

  iaq . clear ();
  CHECK (  iaq . isEmpty ()   );
  CHECK (  iaq . length ()   ==   0   );
  CHECK (  iaq . tostring ()   ==   "Front: :Back\n"   );

   //--------------------------------------------------------------------
   // use a linked list queue implementation of strings
   LQueue < string >  saq ;

   // initial queue should be empty
  CHECK (  saq . isEmpty ()   );
  CHECK (  saq . length ()   ==   0   );
  CHECK (  saq . tostring ()   ==   "Front: :Back\n"   );
  CHECK_THROWS (  saq . front (),   EmptyQueueException ()   );
  CHECK_THROWS (  saq . dequeue (),   EmptyQueueException ()   );

   // add item to empty queue
  saq . enqueue ( "echo" );
  CHECK_FALSE (  saq . isEmpty ()   );
  CHECK (  saq . length ()   ==   1   );
  CHECK (  saq . tostring ()   ==   "Front: echo :Back\n"   );
  CHECK (  saq . front ()   ==   "echo"   );

   // add item to non empty queue
  saq . enqueue ( "hotel" );
  CHECK_FALSE (  saq . isEmpty ()   );
  CHECK (  saq . length ()   ==   2   );
  CHECK (  saq . tostring ()   ==   "Front: echo hotel :Back\n"   );
  CHECK (  saq . front ()   ==   "echo"   );

   // add another item to non empty queue
  saq . enqueue ( "bravo" );
  CHECK_FALSE (  saq . isEmpty ()   );
  CHECK (  saq . length ()   ==   3   );
  CHECK (  saq . tostring ()   ==   "Front: echo hotel bravo :Back\n"   );
  CHECK (  saq . front ()   ==   "echo"   );

   // test dequeu
  saq . dequeue ();
  CHECK_FALSE (  saq . isEmpty ()   );
  CHECK (  saq . length ()   ==   2   );
  CHECK (  saq . tostring ()   ==   "Front: hotel bravo :Back\n"   );
  CHECK (  saq . front ()   ==   "hotel"   );

  saq . dequeue ();
  CHECK_FALSE (  saq . isEmpty ()   );
  CHECK (  saq . length ()   ==   1   );
  CHECK (  saq . tostring ()   ==   "Front: bravo :Back\n"   );
  CHECK (  saq . front ()   ==   "bravo"   );

   // test dequeue to make queue become empty again
  saq . dequeue ();
  CHECK (  saq . isEmpty ()   );
  CHECK (  saq . length ()   ==   0   );
  CHECK (  saq . tostring ()   ==   "Front: :Back\n"   );
  CHECK_THROWS (  saq . front (),   EmptyQueueException ()   );

   // test clear of queue
  saq . enqueue ( "alpha" );
  saq . enqueue ( "bravo" );
  CHECK_FALSE (  saq . isEmpty ()   );
  CHECK (  saq . length ()   ==   2   );
  CHECK (  saq . tostring ()   ==   "Front: alpha bravo :Back\n"   );

  saq . clear ();
  CHECK (  saq . isEmpty ()   );
  CHECK (  saq . length ()   ==   0   );
  CHECK (  saq . tostring ()   ==   "Front: :Back\n"   );
}


/** test PriorityQueue class using integers
 */
/* uncomment the test cases 1 at a time.  This test case tests implementation
 * of the some PriorityQueue enqueue() and other functions.
   TEST_CASE("<PriorityQueue enqueue int> Test PriorityQueue using int items",
          "[class]")
   {
   PriorityQueue<int> pq;

   // priority queue is initially empty
   CHECK( pq.isEmpty() );
   CHECK( pq.length() == 0 );
   CHECK( pq.tostring() == "Front: :Back\n" );
   CHECK_THROWS( pq.front(), EmptyQueueException() );
   CHECK_THROWS( pq.dequeue(), EmptyQueueException() );

   // test case 1 insertion into empty priority queue
   pq.enqueue(5);
   CHECK_FALSE( pq.isEmpty() );
   CHECK( pq.length() == 1 );
   CHECK( pq.tostring() == "Front: 5 :Back\n" );
   CHECK( pq.front() == 5 );

   // test case 2 new node is highest priority and should end up on front
   pq.enqueue(10);
   CHECK_FALSE( pq.isEmpty() );
   CHECK( pq.length() == 2 );
   CHECK( pq.tostring() == "Front: 10 5 :Back\n" );
   CHECK( pq.front() == 10 );

   // Test case 3 new node ends up somewhere in middle of the queue
   pq.enqueue(7);
   CHECK_FALSE( pq.isEmpty() );
   CHECK( pq.length() == 3 );
   CHECK( pq.tostring() == "Front: 10 7 5 :Back\n" );
   CHECK( pq.front() == 10 );

   // Test case 3+/4 need to correctl handle when new node ends up on the end
   pq.enqueue(3);
   CHECK_FALSE( pq.isEmpty() );
   CHECK( pq.length() == 4 );
   CHECK( pq.tostring() == "Front: 10 7 5 3 :Back\n" );
   CHECK( pq.front() == 10 );

   pq.enqueue(1);
   CHECK_FALSE( pq.isEmpty() );
   CHECK( pq.length() == 5 );
   CHECK( pq.tostring() == "Front: 10 7 5 3 1 :Back\n" );
   CHECK( pq.front() == 10 );

   // equal priority, we can't see if this works on ints, but when
   // items of equal priority are put on queue, the first item should be ahead
   // of the new item in the resulting queue
   pq.enqueue(5);
   CHECK_FALSE( pq.isEmpty() );
   CHECK( pq.length() == 6 );
   CHECK( pq.tostring() == "Front: 10 7 5 5 3 1 :Back\n" );
   CHECK( pq.front() == 10 );

   // also test dequeue and clear still work.
   pq.dequeue();
   CHECK( pq.length() == 5 );
   CHECK( pq.front() == 7 );
   CHECK( pq.tostring() == "Front: 7 5 5 3 1 :Back\n" );

   pq.dequeue();
   pq.dequeue();
   CHECK( pq.length() == 3 );
   CHECK( pq.front() == 5 );
   CHECK( pq.tostring() == "Front: 5 3 1 :Back\n" );

   pq.clear();
   CHECK( pq.isEmpty() );
   CHECK( pq.length() == 0 );
   CHECK( pq.tostring() == "Front: :Back\n" );
   CHECK_THROWS( pq.front(), EmptyQueueException() );
   CHECK_THROWS( pq.dequeue(), EmptyQueueException() );
   }
 */



/** test PriorityQueue class using strings
 */
/* uncomment the test cases 1 at a time.  This test case tests implementation
 * of the some PriorityQueue enqueue() and other functions on string types.
   TEST_CASE("<PriorityQueue enqueue string> Test PriorityQueue using string items",
          "[class]")
   {
   PriorityQueue<string> pq;

   // priority queue is initially empty
   CHECK( pq.isEmpty() );
   CHECK( pq.length() == 0 );
   CHECK( pq.tostring() == "Front: :Back\n" );
   CHECK_THROWS( pq.front(), EmptyQueueException() );
   CHECK_THROWS( pq.dequeue(), EmptyQueueException() );

   // test case 1 insertion into empty priority queue
   pq.enqueue("mike");
   CHECK_FALSE( pq.isEmpty() );
   CHECK( pq.length() == 1 );
   CHECK( pq.tostring() == "Front: mike :Back\n" );
   CHECK( pq.front() == "mike" );

   // test case 2 new node is highest priority and should end up on front
   pq.enqueue("zulu");
   CHECK_FALSE( pq.isEmpty() );
   CHECK( pq.length() == 2 );
   CHECK( pq.tostring() == "Front: zulu mike :Back\n" );
   CHECK( pq.front() == "zulu" );

   // Test case 3 new node ends up somewhere in middle of the queue
   pq.enqueue("tango");
   CHECK_FALSE( pq.isEmpty() );
   CHECK( pq.length() == 3 );
   CHECK( pq.tostring() == "Front: zulu tango mike :Back\n" );
   CHECK( pq.front() == "zulu" );

   // Test case 3+/4 need to correctl handle when new node ends up on the end
   pq.enqueue("hotel");
   CHECK_FALSE( pq.isEmpty() );
   CHECK( pq.length() == 4 );
   CHECK( pq.tostring() == "Front: zulu tango mike hotel :Back\n" );
   CHECK( pq.front() == "zulu" );

   pq.enqueue("echo");
   CHECK_FALSE( pq.isEmpty() );
   CHECK( pq.length() == 5 );
   CHECK( pq.tostring() == "Front: zulu tango mike hotel echo :Back\n" );
   CHECK( pq.front() == "zulu" );

   // equal priority, we can't see if this works on ints, but when
   // items of equal priority are put on queue, the first item should be ahead
   // of the new item in the resulting queue
   pq.enqueue("mike");
   CHECK_FALSE( pq.isEmpty() );
   CHECK( pq.length() == 6 );
   CHECK( pq.tostring() == "Front: zulu tango mike mike hotel echo :Back\n" );
   CHECK( pq.front() == "zulu" );

   // also test dequeue and clear still work.
   pq.dequeue();
   CHECK( pq.length() == 5 );
   CHECK( pq.front() == "tango" );
   CHECK( pq.tostring() == "Front: tango mike mike hotel echo :Back\n" );

   pq.dequeue();
   pq.dequeue();
   CHECK( pq.length() == 3 );
   CHECK( pq.front() == "mike" );
   CHECK( pq.tostring() == "Front: mike hotel echo :Back\n" );

   pq.clear();
   CHECK( pq.isEmpty() );
   CHECK( pq.length() == 0 );
   CHECK( pq.tostring() == "Front: :Back\n" );
   CHECK_THROWS( pq.front(), EmptyQueueException() );
   CHECK_THROWS( pq.dequeue(), EmptyQueueException() );
   }
 */


/** test PriorityQueue class using Job objects
 */
/* uncomment the test cases 1 at a time.  This test case tests implementation
 * of the PriorityQueue enqueue() and other functions on Job types.
   TEST_CASE("<PriorityQueue enqueue Job> Test PriorityQueue using Job items",
          "[class]")
   {
   PriorityQueue<Job> pq;

   // priority queue is initially empty
   CHECK( pq.isEmpty() );
   CHECK( pq.length() == 0 );
   CHECK( pq.tostring() == "Front: :Back\n" );
   CHECK_THROWS( pq.front(), EmptyQueueException() );
   CHECK_THROWS( pq.dequeue(), EmptyQueueException() );

   // test case 1 insertion into empty priority queue
   // look in JobSimulator.hpp for Job constructor
   // Job(5, 0, 0) is Job(priority, serviceTime, startTime), we only use priority
   // for these tests
   pq.enqueue( Job(5, 0, 0) );
   CHECK_FALSE( pq.isEmpty() );
   CHECK( pq.length() == 1 );
   CHECK( pq.tostring() == "Front: [id: 1 priority: 5] :Back\n" );
   CHECK( pq.front().tostring() == "[id: 1 priority: 5]" );

   // test case 2 new Job is highest priority and should end up on front
   pq.enqueue( Job(10, 5, 5) );
   CHECK_FALSE( pq.isEmpty() );
   CHECK( pq.length() == 2 );
   CHECK( pq.tostring() == "Front: [id: 2 priority: 10] [id: 1 priority: 5] :Back\n" );
   CHECK( pq.front().tostring() == "[id: 2 priority: 10]" );

   // Test case 3 new Job ends up somewhere in middle of the queue
   pq.enqueue( Job(7, 1, 1) );
   CHECK_FALSE( pq.isEmpty() );
   CHECK( pq.length() == 3 );
   CHECK( pq.tostring() == "Front: [id: 2 priority: 10] [id: 3 priority: 7] [id: 1 priority: 5] :Back\n" );
   CHECK( pq.front().tostring() == "[id: 2 priority: 10]" );

   // Test case 3+/4 need to correctl handle when new node ends up on the end
   pq.enqueue( Job(3, 4, 5) );
   CHECK_FALSE( pq.isEmpty() );
   CHECK( pq.length() == 4 );
   CHECK( pq.tostring() == "Front: [id: 2 priority: 10] [id: 3 priority: 7] [id: 1 priority: 5] [id: 4 priority: 3] :Back\n" );
   CHECK( pq.front().tostring() == "[id: 2 priority: 10]" );

   pq.enqueue( Job(1, 2, 3) );
   CHECK_FALSE( pq.isEmpty() );
   CHECK( pq.length() == 5 );
   CHECK( pq.tostring() == "Front: [id: 2 priority: 10] [id: 3 priority: 7] [id: 1 priority: 5] [id: 4 priority: 3] [id: 5 priority: 1] :Back\n" );
   CHECK( pq.front().tostring() == "[id: 2 priority: 10]" );

   // equal priority, we insert another Job with priority 5, it should end up on
   // the queu after the existing Job with same priority if you implement your
   // enqueue function correctly
   pq.enqueue( Job(5, 9, 9) );
   CHECK_FALSE( pq.isEmpty() );
   CHECK( pq.length() == 6 );
   CHECK( pq.tostring() == "Front: [id: 2 priority: 10] [id: 3 priority: 7] [id: 1 priority: 5] [id: 6 priority: 5] [id: 4 priority: 3] [id: 5 priority: 1] :Back\n" );
   CHECK( pq.front().tostring() == "[id: 2 priority: 10]" );
   CHECK( pq[2].getId() == 1 );
   CHECK( pq[3].getId() == 6 );
   }
 */

JobSimulator.cpp

JobSimulator.cpp

/**  @file  JobSimulator.cpp
 *  @brief  Implementation file containing implementations of member functions
 *   of the JobSimulator class for Assignment 10 queues and priority queues.
 *
 *  @author  Jane Programmer
 *  @note    cwid : 123 45 678
 *  @note    class: COSC 2336, Summer 2020
 *  @note    ide  : Atom Text Editor 1.46.0 / build package / GNU gcc tools
 *  @note    assg : Assignment 10
 *  @date    June 1, 2020
 *
 * Assignment 10 queues and priority queues.  Use the given Queue ADT to create
 * a new PriorityQueue ADT, then use queues and your priority queue to create a
 * random job processing simulation. This implementation file contains the
 * implementations of the member functions for the JobSchedulerSimulator class
 * that can create and run simulations of random job arrivals using queues and
 * priority queues to manage.  You need to add the implementation for the
 * runSimulation() method to this file.
 */
#include   < climits >
#include   < cmath >
#include   < iomanip >
#include   < iostream >
#include   < string >
#include   < sstream >
#include   "JobSimulator.hpp"
#include   "Queue.hpp"

using   namespace  std ;


//-------------------------------------------------------------------------
/**
 * A constant for the Job class, used to keep track of
 * and assign unique id's for each job created.
 */
int   Job :: nextListId  =   1 ;


/** Job default constructor
 * Default constructor for Job, needed because some Queue types
 * create an empty array of Job objects, so they need to get filled
 * with empty jobs.  We simply initialize everything to 0, as
 * Job objects created this way, without a priority or service time,
 * can't be used as real jobs in a simulation.
 */
Job :: Job ()
{
   this -> id  =   0 ;
   this -> priority  =   0 ;
   this -> serviceTime  =   0 ;
   this -> startTime  =   0 ;
   this -> endTime  =   0 ;
}


/** Job constructor
 * The actual constructor that needs to be used for Jobs in a simulation.
 * The job is assigned a priority, serviceTime and we record the startTime
 * when the job arrived and began waiting on the system queue for processing.
 *
 *  @param  priority Each job in our simulations has a priority level.  In this
 *   Job object and simulation, the higher the number, the higher the priority
 *   of the Job.
 *  @param  serviceTime This is the time that the job needs to run, once it is
 *   selected/scheduled by the system dispatcher to be executed.  This represents
 *   the time the system is busy processing this Job once it starts running.
 *  @param  startTime The system time at which this jobs was created.  Also will be
 *   the system time this job was added to a job queue in the system and began
 *   waiting to be executed.
 */
Job :: Job ( int  priority ,   int  serviceTime ,   int  startTime )
{
   this -> id  =  nextListId ++ ;
   this -> priority  =  priority ;
   this -> serviceTime  =  serviceTime ;
   this -> startTime  =  startTime ;
   this -> endTime  =  startTime ;
}


/** endTime setter
 * Setter method to set the endTime of this Job.  This is actually the endTime
 * of when the job stoped waiting and began executing (not the time when the job
 * was finished).  The endTime - startTime gives the total waitTime this job
 * spent waiting.
 *
 *  @param  endTime The time when job stoped waiting on a queue and began executing.
 */
void   Job :: setEndTime ( int  endTime )
{
   this -> endTime  =  endTime ;
}


/** id getter
 * Getter method to return this Job's id.  Used for display purposes.
 *
 *  @returns  int The unique id of this Job is returned.
 */
int   Job :: getId ()   const
{
   return  id ;
}


/** service time getter
 * Getter method to return this Job's service time.  Service time
 * is the amount of time this job needs from the system to complete
 * its task.
 *
 *  @returns  int The serviceTime for this Job is returned.
 */
int   Job :: getServiceTime ()   const
{
   return  serviceTime ;
}


/** priority getter
 * Getter method to return this Job's priority level.  Priority
 * is a measure of the job importance.  In this simulation, higher
 * priority means higher importance.
 *
 *  @returns  int The priority level for this Job is returned.
 */
int   Job :: getPriority ()   const
{
   return  priority ;
}


/** waitTime getter
 * Getter method to return this Job's waitTime.  Wait time is
 * the difference from the endTime when the job stoped waiting
 * (and began executing) and startTime when the job was created
 * and began waiting to get access to the system for execution.
 *
 *  @returns  int The wait time for this Job is returned.  Wait time
 *   is not valid until after the job as exited the wait queue, and
 *   its endTime has been set.
 */
int   Job :: getWaitTime ()   const
{
   return  endTime  -  startTime ;
}


/** cost getter
 * Getter method to return this Job's cost.  Cost is a measure of
 * used to evaluate how well a particular system performs in
 * processing jobs.  In this simulation, we want to minimize time
 * high priority jobs spend waiting, and maximize high priority
 * job throughput.  Thus cost is a combination of priority and how
 * long the job spent waiting to be processed.  Since higher numbers
 * are used to mean higher priorities, multiplying the wait time
 * times the priority scales the cost so that high priority jobs
 * that have to wait for long periods have high costs.  In the
 * system, we want to minimize cost as a measure of performance.
 *
 *  @returns  int The cost for this Job is returned.  Cost is a measure
 *   of performance with regards to this parcitular jobs.  Cost is
 *   measured in this system as the job priority times the time the
 *   job was forced to wait before it could start executing.
 */
int   Job :: getCost ()   const
{
   return  priority  *  getWaitTime ();
}


/** overload boolean equal comparison
 * Overload boolean comparison between jobs.  The main purpose of
 * providing boolean comparisons between jobs in this simulation is
 * so that priority based schedulers can order the jobs based on
 * priority level, from lowest priority to highest priority.  Thus
 * for a Job, jobs are equal when they have equal priorities.
 *
 *  @param  rhs The Job object on the right hand side of the boolean
 *   comparison.
 *
 *  @returns  bool True if the two jobs have equal priority, false
 *   otherwise.
 */
bool   Job :: operator == ( const   Job &  rhs )   const
{
   return   this -> priority  ==  rhs . priority ;
}


/** overload boolean less than
 * Overload boolean less than comparison between jobs.  The main
 * purpose of providing boolean comparisons between jobs in this
 * simulation is so that priority based schedulers can order the jobs
 * based on priority level, from lowest priority to highest priority.
 * Thus for a Job, a job is less than another job if its priority
 * is smaller.
 *
 *  @param  rhs The Job object on the right hand side of the boolean
 *   comparison.
 *
 *  @returns  bool True if this job has lower priority than the rhs
 *   job, false otherwise.
 */
bool   Job :: operator < ( const   Job &  rhs )   const
{
   return   this -> priority  <  rhs . priority ;
}


/** overload boolean greater than
 * Overload boolean greater than comparison between jobs.  The main
 * purpose of providing boolean comparisons between jobs in this
 * simulation is so that priority based schedulers can order the jobs
 * based on priority level, from lowest priority to highest priority.
 * Thus for a Job, a job is greater than another job if its priority
 * is bigger.
 *
 *  @param  rhs The Job object on the right hand side of the boolean
 *   comparison.
 *
 *  @returns  bool True if this job has higher priority than the rhs
 *   job, false otherwise.
 */
bool   Job :: operator > ( const   Job &  rhs )   const
{
   return   this -> priority  >  rhs . priority ;
}


/** overload boolean <=
 * Overload boolean less than or equal comparison between jobs.  The
 * main purpose of providing boolean comparisons between jobs in this
 * simulation is so that priority based schedulers can order the jobs
 * based on priority level, from lowest priority to highest priority.
 * Thus for a Job, a job is less than or equal another job if its
 * priority is smaller or the same.
 *
 *  @param  rhs The Job object on the right hand side of the boolean
 *   comparison.
 *
 *  @returns  bool True if this job has lower or equal priority than the
 *   rhs job, false otherwise.
 */
bool   Job :: operator <= ( const   Job &  rhs )   const
{
   return   this -> priority  <=  rhs . priority ;
}


/** to string
 * Make a string representation of this Job suitable for display.
 *
 *  @returns  string Returns the constructed string representation of this
 *   Job object.
 */
string  Job :: tostring ()   const
{
  ostringstream out ;

  out  <<   "[id: "   <<  id
       <<   " priority: "   <<  priority
       <<   "]" ;
   return  out . str ();
}


/** overload output stream operator
 * Overload the output stream operator to provide a representation
 * of this Job suitable for display.  Mainly useful for displaying
 * queue contents and for debugging purposes.
 *
 *  @param  out The output stream we are to insert a representation of this
 *   Job item onto.
 *  @param  aJob The Job instance we are to access and represent on the given
 *   output stream.
 *
 *  @returns  ostream& Returns a reference to the original output strem but after
 *   we insert a representation of aJob onto it.
 */
ostream &   operator << ( ostream &  out ,   const   Job &  aJob )
{
   // out << "[id: " << aJob.id
   //     << " priority: " << aJob.priority
   //     << " serviceTime: " << aJob.serviceTime
   //     << " startTime: " << aJob.startTime
   //     << " endTime: " << aJob.endTime
   //     << " waitTime: " << aJob.getWaitTime()[id: 2 priority: 10]
   //     << "]";
   //out << "[id: " << aJob.id
   //    << " priority: " << aJob.priority
   //    << "]";
  out  <<  aJob . tostring ();
   return  out ;
}


//-------------------------------------------------------------------------
/** random uniform
 * Return a random floating point value in the range of [0.0, 1.0] with
 * uniform probability of any value in the range being returned.
 * The algorithm is that rand() returns an int in range [0, RAND_MAX]
 * and doing floating point division on the random integer by
 * RAND_MAX recasts the result into a floating point number in range
 * [0.0, 1.0].
 *
 *  @returns  double Returns a randomly generated double valued number
 *   with uniform probability in the range [0.0, 1.0]
 */
double   JobSchedulerSimulator :: randomUniform ()
{
   double  randValue  =   double ( rand ())   /   double ( RAND_MAX );
   return  randValue ;
}


/** random range
 * Generate a random ingeger number in the given range from [minValue to
 * maxValue].  We are given minValue and maxValue, a random integer is
 * generated (with uniform probability) that is between minValue and
 * maxValue (inclusive, so minValue or maxValue are valid results
 * that can be returned, or any integer in between).
 *
 *  @param  minValue The minimum value of the range of integers to generate.
 *  @param  maxValue The maximum of the range of integers to generate.
 *
 *  @returns  int Returns a random integer value in range [minValue,
 *   maxValue] inclusive of the end points.
 */
int   JobSchedulerSimulator :: randomRange ( int  minValue ,   int  maxValue )
{
   // the range is difference between desired max and min.  We need
   // this magnitude in order to correctly generate a random value in
   // the given range
   int  range  =  maxValue  -  minValue  +   1 ;

   // generate a random value in range 0 to range (inclusive)
   int  randValue  =  rand ()   %  range ;

   // shift the value so it is in range [minValue, maxValue]
  randValue  +=  minValue ;

   return  randValue ;
}


/** job arrived
 * Test if a job arrived.  We use a poisson distribution to generate
 * a boolean result of true, a new job arrived in this time period,
 * or false, a new job did not arrive.  A Poisson distribution is often
 * a good model of discrete arrivals of jobs or customers in a system.
 * See Malik Ch. 18, pg. 1233 for description of the poisson arrival
 * calculation here.
 *
 *  @param  none, but we use the class simulation parameter
 *   jobArrivalProbability to determine if a new job arrived
 *   using a Poisson distribution.  The jobArrivalProbability
 *   is the probability of an arrival during a time period (lambda).
 *
 *  @returns  bool True if a new job arrived according to random check,
 *   false otherwise.
 */
bool   JobSchedulerSimulator :: jobArrived ()
{
   // if a random uniform value in range [0.0, 1.0] is greater than
   // e^(-arrivalProbability), then a job has arrived according to
   // the poisson distribution
   return  randomUniform ()   >  exp ( - jobArrivalProbability );
}


/** generate priority
 * Generate a random priority within the given range of the
 * simulation parameters [minPriority, maxPriority] inclusive.
 *
 *  @param  none, but minPriority and maxPriority simulation
 *   parameters are used in this function to randomly select
 *   a priority for a Job in the given range.
 *
 *  @returns  int A random priority in the range [minPriority, maxPriority]
 *   using the current settings of the simulation parameters.
 */
int   JobSchedulerSimulator :: generateRandomPriority ()
{
   return  randomRange ( minPriority ,  maxPriority );
}


/** generate service time
 * Generate a random job service time within the given range of the
 * simulation parameters [minServiceTime, maxServiceTime] inclusive.
 *
 *  @param  none, but minServiceTime and maxServiceTime simulation
 *   parameters are used in this function to randomly select a
 *   service time for a Job in the given range.
 *
 *  @returns  int A random serviceTime in the range [minServiceTime,
 *   maxServiceTime] using the current settings of the simulation
 *   parameters.
 */
int   JobSchedulerSimulator :: generateRandomServiceTime ()
{
   return  randomRange ( minServiceTime ,  maxServiceTime );
}


/** simulator constructor
 * Mostly just a constructor to allow all of the simulation parameters
 * to be set to initial values when a simulation is created.  All of these
 * simulation parameters have default values specified for a standard
 * job simulation.  All simulation result member values are initialized
 * to 0 or null values in preparation for a simulation run.
 *
 *  @param  simulationTime The total amount of discrete time steps to run
 *   the simulation for.
 *  @param  jobArrivalProbability The probability that a job will arrive at
 *   each time step of the simulation.  This is a double value in the range
 *   from 0.0 (jobs never arrive) to 1.0 (a new job arrives at each time
 *   step of the simulation).
 *  @param  minPriority The minimum priority that will be randomly generated
 *   for newly randomly arriving jobs.
 *  @param  maxPriority The maximum priority that will be randomly genreated
 *   for newly randomly arriving jobs.
 *  @param  minServiceTime The minimum service time to randomly genreate for
 *   newly arriving random jobs.
 *  @param  maxServiceTime The maximum service time to randomly genreate for
 *   newly arriving random jobs.
 */
JobSchedulerSimulator :: JobSchedulerSimulator ( int  simulationTime ,
                                              double  jobArrivalProbability ,
                                              int  minPriority ,
                                              int  maxPriority ,
                                              int  minServiceTime ,
                                              int  maxServiceTime )
{
   // initialize/remember the simulation parameters
   this -> simulationTime  =  simulationTime ;
   this -> jobArrivalProbability  =  jobArrivalProbability ;
   this -> minPriority  =  minPriority ;
   this -> maxPriority  =  maxPriority ;
   this -> minServiceTime  =  minServiceTime ;
   this -> maxServiceTime  =  maxServiceTime ;

   // initialize simulation results to 0, ready to be calculated
   this -> description  =   "" ;
   this -> numJobsStarted  =   0 ;
   this -> numJobsCompleted  =   0 ;
   this -> numJobsUnfinished  =   0 ;
   this -> totalWaitTime  =   0 ;
   this -> totalCost  =   0 ;
   this -> averageWaitTime  =   0.0 ;
   this -> averageCost  =   0.0 ;
}


/** summary results
 * Convenience methods for creating a string for display listing
 * all of the simulation parameters, and all of the simulation
 * results.  Mostly useful after a simulation has just completed,
 * to get a summary of the simulation results for the given
 * simulation parameters.
 *
 *  @returns  string This method constructs and returns a string
 *   with a summary of the current simulation parameter settings
 *   and a summary of the simulation results.
 */
string  JobSchedulerSimulator :: summaryResultString ()
{
  ostringstream out ;

  out  <<   "Job Scheduler Simulation Results"   <<  endl
       <<   "--------------------------------"   <<  endl
       <<   "Simulation Parameters"   <<  endl
       <<   "--------------------------"   <<  endl
       <<   "Description              : "   <<  description  <<  endl
       <<   "Simulation Time          : "   <<  simulationTime  <<  endl
       <<   "Job Arrival Probability  : "   <<  jobArrivalProbability  <<  endl
       <<   "Priority (min,max)       : ("   <<  minPriority  <<   ", "   <<  maxPriority  <<   ")"   <<  endl
       <<   "Service Time (min,max)   : ("   <<  minServiceTime  <<   ", "   <<  maxServiceTime  <<   ")"   <<  endl  <<  endl
       <<   "Simulation Results"   <<  endl
       <<   "--------------------------"   <<  endl
       <<   "Number of jobs started   : "   <<  numJobsStarted  <<  endl
       <<   "Number of jobs completed : "   <<  numJobsCompleted  <<  endl
       <<   "Number of jobs unfinished: "   <<  numJobsUnfinished  <<  endl
       <<   "Total Wait Time          : "   <<  totalWaitTime  <<  endl
       <<   "Total Cost               : "   <<  totalCost  <<  endl
       <<   "Average Wait Time        : "   <<  setprecision ( 4 )   <<  fixed  <<  averageWaitTime  <<  endl
       <<   "Average Cost             : "   <<  setprecision ( 4 )   <<  fixed  <<  averageCost  <<  endl
       <<  endl  <<  endl ;

   return  out . str ();
}


/** csv results
 * A method for outputing the simulation results as a string of
 * comma separated values (csv).  This method is useful for generating
 * data about large numbers of simulations for later analysis.
 *
 *  @returns  string This method constructs and returns a string
 *   of comma separated values (csv) of current simulation results,
 *   suitable for constructing a x.csv file for data analysis.
 */
string  JobSchedulerSimulator :: csvResultString ()
{
  ostringstream out ;
  out  <<  numJobsStarted  <<   ","
       <<  numJobsCompleted  <<   ","
       <<  numJobsUnfinished  <<   ","
       <<  totalWaitTime  <<   ","
       <<  totalCost  <<   ","
       <<  setprecision ( 4 )   <<  fixed  <<  averageWaitTime  <<   ","
       <<  setprecision ( 4 )   <<  fixed  <<  averageCost  <<  endl ;
   return  out . str ();
}


/** overload output stream operator
 * Overload the output stream operator for convenience so we can
 * output a simulation object directly to an output stream.
 *
 *  @param  out A reference to the output stream we are sending our
 *   output to.
 *  @param  sim The JobSchedulerSimulator object we are outputing
 *   the values of.  This is a friend funciton of the class, so
 *   the object is given as second parameter of this binary operator.
 *
 *  @returns  ostream Returns the given output stream object, but after
 *   we have inserted the output representation of the sim into it.
 */
ostream &   operator << ( ostream &  out ,   JobSchedulerSimulator &  sim )
{
  out  <<  sim . summaryResultString ();
   return  out ;
}


// Assignment 10  If you are attempting the extra credit then
// you need to add the implementation of your runSimulation()
// method, as described in the assignment, here.  Don't forget
// your function documentation

JobSimulator.hpp

/** @file JobSimulator.hpp * @brief Header file containing a JobSimulator class for Assignment 10 * queues and priority queues. * * @author Jane Programmer * @note cwid : 123 45 678 * @note class: COSC 2336, Summer 2020 * @note ide : Atom Text Editor 1.46.0 / build package / GNU gcc tools * @note assg : Assignment 10 * @date June 1, 2020 * * Assignment 10 queues and priority queues. Use the given Queue ADT to create * a new PriorityQueue ADT, then use queues and your priority queue to create a * random job processing simulation. This header file contains the declaration * of the JobSchedulerSimulator class that can create and run simulations of * random job arrivals using queues and priority queues to manage. You need to * add the prototype for the runSimulation() method to this file. */ #include "Queue.hpp" #ifndef JOBSIMULATOR_HPP #define JOBSIMULATOR_HPP /** Job class * Class for job scheduling simulation. A Job enters a system * at random intervals (determined by the JobSchedulerSimulator on * a random Poisson basis). A job has a priority level and a serviceTime * which is the amount of system time it needs in order to complete * it task. The main property to keep track of for jobs in a * simulation is how long they have to wait before they are selected * to be processed/run by the system. Jobs keep track of their cost, * which can be used to measure a particular system's performance * (lower costs mean the system performed well, higher costs mean the * system performed more poorly). For systems with priority based jobs, * the measure of the cost is determined of the function a job spent * waiting, and how high of a priority the job had. We use the * simple calculation of cost = priority * waitTime to calculate * the cost for a job once it completes. */ class Job { private: /// @brief A static int variable, used to assign unique ids when processes /// are created, for identification purposes. static int nextListId; /// @brief The actual unique id assigned to a job object. int id; /// @brief This jobs priority level. Higher numbers mean higher priority /// jobs in this simulation. int priority; /// @brief The amount of system time this job needs in order to complete /// its task. int serviceTime; /// @brief The time when the job was created. Also the time when the job /// began waiting in a queue to be selected to run. int startTime; /// @brief The time when the job finished waiting (when it was finally /// selected by the system to begin execution). The difference between /// endTime - startTime determines the total waitTime for this process /// (calculated by getWaitTime() accessor method). int endTime; public: Job(); Job(int priority, int serviceTime, int startTime); void setEndTime(int endTime); int getId() const; int getServiceTime() const; int getPriority() const; int getWaitTime() const; int getCost() const; bool operator==(const Job& rhs) const; bool operator<(const Job& rhs) const; bool operator>(const Job& rhs) const; bool operator<=(const Job& rhs) const; string tostring() const; friend ostream& operator<<(ostream& out, const Job& aJob); }; /** JobSchedulerSimulator * This class organizes and executes simulations of job scheduling, using * different scheduling methods. The simulations are goverend by a number * of system parameters, that are specified when a simulation is created. * When a simulation is run, various data is gathered that describes the * results of the simulation. In general, the job scheduling being * simulated is simple. The system runs for discrete time steps (total * number of which is goverened by simulationTime parameter). At each step * we check for and simulate new job arrivals. When jobs arrive, they * are placed on a single job queue. We then check if the processor/executor * is busy or not, and if not and if the job queue has some jobs on it, we * simulate dispatching a job. Differences in how jobs are organized on * a queue, and their effects on system performance (as a function of * total or average cost) can be explored with this simulator. */ struct JobSchedulerSimulator { private: /// simulation parameters /// These are parameters of the simulation, they govern properties of /// job arrivals and characteristics when a simulation is run: /// @brief The total number of time steps a simulation will run. Time /// steps will run from 1..simulationTime number of discrete steps. int simulationTime; /// @brief The Poisson probability that a job will arrive any any given /// discrete time interval. This governs how often new jobs arrive and /// are created in the simulation. double jobArrivalProbability; /// @brief The range of Job priority levels for /// jobs that arrive. This simulation generates priorities for the /// jobs randomly with uniform probability within this range, and assigns /// them to new jobs that arrive. The minium random priority to generate. int minPriority; /// @brief The maximum random priority that will be generated. int maxPriority; /// @brief The range of Job serviceTimes /// for jobs that arrive in the system simulation. Service times are how /// long a job needs to execute, once it is selected to be processed. /// Service times are generated with uniform probability in this given /// range when new jobs arrive. This is the minimum random service time /// that will be generated. int minServiceTime; /// @brief The maximum random service time that will be generated. int maxServiceTime; /// simulation results /// These are resulting statistics of a simultion. While a simulation is /// being run, data is gathered about various performance characteristics, /// like wait times and costs. At the end of a simulation, these statistical /// results are available for analysis of the system preformance. /// @brief A description of the dispatching/queueing method used. string description; /// @brief The new number of jobs that entered and were started during /// the most recent simulation run. int numJobsStarted; /// @brief The number of jobs that were successfully run during a simulation. int numJobsCompleted; /// @brief The number of jobs left in the waiting queues when a simulation /// finished. int numJobsUnfinished; /// @brief The total amount of time spent waiting by jobs that completed /// in the simulation. Mainly useful for calculating the averageWaitTime. int totalWaitTime; /// @brief The total cost of all jobs that completed in the simulation. Also /// mainly useful for calculating averageCost statistic. int totalCost; /// @brief The average waiting time for completed jobs of the most recent /// simulation. double averageWaitTime; /// @brief The average system cost for completed jobs of the most recent /// simulation. double averageCost; // private functions to support runSimulation(), mostly // for generating random times, priorities and poisson arrivals double randomUniform(); int randomRange(int minValue, int maxValue); bool jobArrived(); int generateRandomPriority(); int generateRandomServiceTime(); public: JobSchedulerSimulator(int simulationTime = 10000, double jobArrivalProbability = 0.1, int minPriority = 1, int maxPriority = 10, int minServiceTime = 5, int maxServiceTime = 15); string summaryResultString(); string csvResultString(); // Assignment 10 // If you want to attempt the extra credit, then you need to add a // new method to the class definition of the JobSchedulerSimulator // named runSimulation() here. friend ostream& operator<<(ostream& out, JobSchedulerSimulator& sim); }; #endif

Makefile

# source files in this project (for beautification) PROJECT_NAME=assg10 sources = $(PROJECT_NAME)-tests.cpp \ $(PROJECT_NAME)-main.cpp \ Queue.hpp \ Queue.cpp \ JobSimulator.hpp \ JobSimulator.cpp # template files, list all files that define template classes # or functions and should not be compiled separately (template # is included where used) template-files = Queue.hpp \ Queue.cpp # object file targets used for both testing and simulation assg-objects = JobSimulator.o # common targets and variables used for all assignments/projects include ../../include/Makefile.inc

Queue.cpp

Queue.cpp

/**  @file  Queue.cpp
 *  @brief  Implementation file of Queue class member functions for
 *   Assignment 10 queues and priority queues.
 *
 *  @author  Jane Programmer
 *  @note    cwid : 123 45 678
 *  @note    class: COSC 2336, Summer 2020
 *  @note    ide  : Atom Text Editor 1.46.0 / build package / GNU gcc tools
 *  @note    assg : Assignment 10
 *  @date    June 1, 2020
 *
 * Assignment 10 queues and priority queues.  Use the given Queue ADT to create
 * a new PriorityQueue ADT, then use queues and your priority queue to create a
 * random job processing simulation. This implementation file contains the
 * actual implementation of the functions for the Queue ADT given for this
 * assignment.
 */
#include   "Queue.hpp"


//-------------------------------------------------------------------------
/** Queue equivalence
 * Compare two given queues to determine if they are equal or not.
 * stacks are equal if they are both of the same size, and each
 * corresponding item on each stack is equal at the same position on the
 * stack.  This function relies on overloaded operator[] to access
 * items on stack by index for the comparison.
 *
 *  @param  rhs The stack on the right hand side of the boolean comparison
 *   expression to compare this stack against to check for equivalence.
 *
 *  @returns  bool Returns true if the stacks are equal, and false otherwise.
 */
template   < class  T >
bool   Queue < T >:: operator == ( const   Queue &  rhs )   const
{
   // if number of items on the stacks don't match, then they can't
   // be equivalent
   if   ( this -> length ()   !=  rhs . length ())
   {
     return   false ;
   }

   // otherwise need to check each item individually
   for   ( int  index  =   0 ;  index  <   this -> length ();  index ++ )
   {
     if   (( * this )[ index ]   !=  rhs [ index ])
     {
       return   false ;
     }
   }

   // if we get to this point, all itmes checked were equivalent, so
   // we are done and the answer is yes the stacks are equal
   return   true ;
}


/** Queue output stream operator
 * Friend function for Queue ADT, overload output stream operator to allow
 * easy output of queue representation to an output stream.
 *
 *  @param  out The output stream we are to insert a representation of the
 *   given queue into.
 *  @param  aQueue The queue we are to access and represent out onto the output
 *   stream.
 *
 *  @returns  ostream& Returns a reference to the original output stream we were
 *   given but after we have inserted the aQueue representation onto it.
 */
template   < typename  U >
ostream &   operator << ( ostream &  out ,   const   Queue < U >&  aQueue )
{
  out  <<  aQueue . tostring ();
   return  out ;
}


//-------------------------------------------------------------------------
/** queue (array) constructor
 * Constructor for queue.  Default to enough room for 100 items
 * NOTE: the front pointer points directly to the index of the front item, but
 * the backIndex pointer points to the index-1 of the item where next insertion
 * will happen.
 * NOTE: we treat the items array as a circular buffer, so all increments of
 * indexes must be modulo current allocSize, to wrap backIndex around to beginning.
 *
 *  @param  initialAlloc Initial space to allocate for queue, defaults to
 *   100.
 */
template   < class  T >
AQueue < T >:: AQueue ( int  initialAlloc )
{
  allocSize  =  initialAlloc ;
  numitems  =   0 ;
  frontIndex  =   0 ;
  backIndex  =  allocSize  -   1 ;   // back points to (x-1) % allocSize index
  items  =   new  T [ allocSize ];
}


/** queue (array) constructor
 * Constructor for queue using an array initializer.
 * NOTE: the front pointer points directly to the index of the front item, but
 * the backIndex pointer points to the index-1 of the item where next insertion
 * will happen.
 * NOTE: we treat the items array as a circular buffer, so all increments of
 * indexes must be modulo current allocSize, to wrap backIndex around to beginning.
 *
 *  @param  initItems The array of items we are to use to copy from and construct
 *   the new queue from.
 *  @param  numItems The number of items in the array initialize we are to copy.
 */
template   < class  T >
AQueue < T >:: AQueue ( int  initItems [],   int  numItems )
{
   this -> allocSize  =  numitems ;
   this -> numitems  =  numitems ;
  frontIndex  =   0 ;
  items  =   new  T [ allocSize ];

   // copy the initialize items into this queue
   for   ( int  index  =   0 ;  index  <  numitems ;  index ++ )
   {
    items [ index ]   =  initItems [ index ];
   }

   // set up the back index
  backIndex  =  numitems  -   1 ;
}


/** queue (array) destructor
 */
template   < class  T >
AQueue < T >::~ AQueue ()
{
   // free up currently allocated memory
   delete   []  items ;
}


/** queue (array) clear
 * Function to initialize the queue back to an empty state.
 * Postcondition: frontIndex = 0; backIndex = allocSize-1; numitems=0; isEmpty() == true
 */
template   < class  T >
void   AQueue < T >:: clear ()
{
  frontIndex  =   0 ;
  backIndex  =  allocSize  -   1 ;
  numitems  =   0 ;
}


/** queue (array) isEmpty
 * Determine whether queue is currently empty or not.
 *
 *  @returns  returns true if the queue is empty, otherwise
 *   returns false.
 */
template   < class  T >
bool   AQueue < T >:: isEmpty ()   const
{
   return  numitems  ==   0 ;
}


/** queue (array) isFull
 * Determine whether queue is currently full or not.
 *
 *  @returns  returns true if the queue is full, otherwise
 *   returns false.
 */
template   < class  T >
bool   AQueue < T >:: isFull ()   const
{
   return  numitems  ==  allocSize ;
}


/** queue (array) enqueue
 * Add newItem to the back of the queue.
 * Preconditon: The queue exists
 * Postcondition: The queue is changed and newItem is added to the back
 *   of the queue.
 *  @param  newItem The new item to add to the frontIndex of this queue.
 */
template   < class  T >
void   AQueue < T >:: enqueue ( const  T &  newItem )
{
   // if queue is full, grow it
   if   ( isFull ())
   {
     // double the current size
     int  newAllocSize  =   2   *  allocSize ;

     // alloc the new space
    T *  newItems  =   new  T [ newAllocSize ];

     // and copy the queue to the new storage space
     // since we are copying anyway, we shift the items from the old
     // frontIndex back to index 0
     int  oldIndex  =  frontIndex ;
     for   ( int  index  =   0 ;  index  <  numitems ;  index ++ )
     {
      newItems [ index ]   =  items [ oldIndex ];
      oldIndex  =   ( oldIndex  +   1 )   %  allocSize ;
     }
    frontIndex  =   0 ;
    backIndex  =  numitems - 1 ;

     // free up the old space, start using the new space
     delete   []  items ;
    items  =  newItems ;
    allocSize  =  newAllocSize ;
   }

   // add the item, and increment our top
  backIndex  =   ( backIndex  +   1 )   %  allocSize ;
  numitems ++ ;
  items [ backIndex ]   =  newItem ;
}


/** queue (array) front
 * Peek at and return the front element of the queue.
 * Preconditon: The queue exists and is not empty
 * Postcondition: If the queue is empty, we throw QueueEmpty
 *   exception; otherwise, the front element of the queue is
 *   returned
 *  @returns  T The item of type T currently on the front of this
 *   queue.
 */
template   < class  T >
AQueue < T >:: front ()   const
{
   //assert(topIndex != 0);
   if   ( isEmpty ())
   {
     throw   EmptyQueueException ( "AQueue<T>::front()" );
   }
   else
   {
     return  items [ frontIndex ];
   }
}


/** queue (array) dequeue
 * Remove the front element from the queue.  Some ADT combine dequeue
 * and front.  We have two separate operations in this ADT.
 * Preconditon: The queue exists and is not empty.
 * Postcondition: If the queue is empty, we throw QueueEmpty
 *   exception; otherwise the front element of the queue is removed
 *   from the queue.
 */
template   < class  T >
void   AQueue < T >:: dequeue ()
{
   // assert(topIndex != 0);
   if   ( isEmpty ())
   {
     throw   EmptyQueueException ( "Aqueue<T>::dequeue()" );
   }
   else
   {
    numitems -- ;
    frontIndex  =   ( frontIndex  +   1 )   %  allocSize ;
   }
}


/** queue (array) length
 * Getter method to access the current queue length.
 *
 *  @returns  length Returns the current queue length.
 */
template   < class  T >
int   AQueue < T >:: length ()   const
{
   return  numitems ;
}


/** queue (array) tostring
 * Represent this queue as a string.
 *
 *  @returns  string Returns the contents of queue as a string.
 */
template   < class  T >
string  AQueue < T >:: tostring ()   const
{
  ostringstream out ;

  out  <<   "Front: " ;
   int  index  =  frontIndex ;
   while   ( index  !=   ( backIndex  +   1 )   %  allocSize )
   {
    out  <<  items [ index ]   <<   " " ;
    index ++ ;
   }
  out  <<   ":Back"   <<  endl ;

   return  out . str ();
}


/** Queue (array) indexing operator
 * Access internel elements of queue using indexing operator[].
 * This is not a normal queue operation, we use mainly for testing
 * so that we can compare if two queues are equal at each internal
 * element of the queue.  For this reason, this operator should
 * probably be private to the Queue class.
 *
 *  @param  index The index of the item on the queue we want to access
 *   and return, where index 0 represents the front of the queue and
 *   index == numitems-1 is the back.
 *
 *  @returns  T Returns the item at "index" on the queue.
 */
template   < class  T >
const  T &   AQueue < T >:: operator []( int  index )   const
{
   // bounds checking, we will throw our stack exception if fails
   if   ( index  <   0   ||  index  >=  numitems )
   {
     throw   InvalidIndexQueueException ( "AQueue<T>::operator[]" );
   }
   // otherwise we can directly access the asked for item from our items array
   // our memory buffer is being treated as a circular buffer, so we
   // have to calculated the indicated index by hand
   else
   {
     return  items [( frontIndex  +  index )   %  allocSize ];
   }
}


//-------------------------------------------------------------------------
/** queue (list) constructor
 * Constructor for linked list version of queue.
 * An empty queue is indicated by both front and back
 * pointers pointing to null.
 */
template   < class  T >
LQueue < T >:: LQueue ()
{
  queueFront  =  NULL ;
  queueBack  =  NULL ;
  numitems  =   0 ;
}


/** queue (list) destructor
 * Destructor for linked list version of queue.
 */
template   < class  T >
LQueue < T >::~ LQueue ()
{
  clear ();
}


/** queue (list) clear
 * This will empty out the queue.  This method frees up all of the
 * dynamically allocated memory being used by the queue linked list
 * nodes.
 */
template   < class  T >
void   LQueue < T >:: clear ()
{
   Node < T >*  temp ;

   // iterate through Nodes in queue, freeing them up
   // as we visit them
   while   ( queueFront  !=  NULL )
   {
    temp  =  queueFront ;
    queueFront  =  queueFront -> link ;

     // dellocate this Node memory
     delete  temp ;
   }

   // make sure all private members are cleard correctly
  queueBack  =  NULL ;
  numitems  =   0 ;
}


/** queue (list) isEmpty
 * Check if queue is empty or not.
 *
 *  @returns  true if the queue is currently empty, or
 *   false otherwise.
 */
template   < class  T >
bool   LQueue < T >:: isEmpty ()   const
{
   return  queueFront  ==  NULL ;
   // return numitems == 0;
}


/** queue (list) enqueue
 * Add the indicated item onto the back of the queue.
 *
 *  @param  newItem The new item we will add to the back of
 *   this queue.
 */
template   < class  T >
void   LQueue < T >:: enqueue ( const  T &  newItem )
{
   // dynamically allocate space for the new Node to hold
   // this newItem
   Node < T >*  newNode  =   new   Node < T > ;

   // initialize the node
  newNode -> item  =  newItem ;
  newNode -> link  =  NULL ;

   // if the queue is empty, then this new node is the
   // front and back node
   if   ( queueFront  ==  NULL )
   {
    queueFront  =  newNode ;
   }
   // otherwise, it gets added onto the back
   else
   {
    queueBack -> link  =  newNode ;
   }

   // the new node added is now the new back of the queue
  queueBack  =  newNode ;
  numitems ++ ;
}


/** queue (list) front
 * Return the front item from the queue.
 *
 *  @returns  T Returns the item currently at the front of
 *   this queue.
 */
template   < class  T >
LQueue < T >:: front ()   const
{
   //assert(queueFront != NULL)
   if   ( isEmpty ())
   {
     throw   EmptyQueueException ( "LQueue<T>::front()" );
   }
   else
   {
     return  queueFront -> item ;
   }
}


/** queue (list) dequeue
 * This function actually removes the item at the front of the queue from
 * the queue.  It is undefined what happens if you try and dequeue() from
 * an empty queue.  This method throws an exception if dequeue is attempted
 * from an empty queue.
 */
template   < class  T >
void   LQueue < T >:: dequeue ()
{
   //assert(queueTop != NULL)
   if   ( isEmpty ())
   {
     throw   EmptyQueueException ( "LQueue<T>::dequeue()" );
   }
   else
   {
     // keep track of the current front, so we can deallocate
     Node < T >*  temp ;
    temp  =  queueFront ;

     // remove the front item from the queue
     // if queue becomes empty, make sure both front and back
     // are NULL
    queueFront  =  queueFront -> link ;
     if   ( queueFront  ==  NULL )
     {
      queueBack  =  NULL ;
     }
    numitems -- ;

     // deallocate the old top now
     delete  temp ;
   }
}


/** queue (array) length
 * Accessor method to return the current length of this queue.
 *
 *  @returns  int The current queue length
 */
template   < class  T >
int   LQueue < T >:: length ()   const
{
   return  numitems ;
}


/** queue (array) tostring
 * Represent this queue as a string.
 *
 *  @returns  string Returns the contents of queue as a string.
 */
template   < class  T >
string  LQueue < T >:: tostring ()   const
{
  ostringstream out ;
   Node < T >*  temp  =  queueFront ;

  out  <<   "Front: " ;
   while   ( temp  !=  NULL )
   {
    out  <<  temp -> item  <<   " " ;
    temp  =  temp -> link ;
   }
  out  <<   ":Back"   <<  endl ;

   return  out . str ();
}


/** Queue (list) indexing operator
 * Access internel elements of queue using indexing operator[].
 * This is not a normal queue operation, we use mainly for testing
 * so that we can compare if two queues are equal at each internal
 * element of the queue.  For this reason, this operator should
 * probably be private to the Queue class.
 *
 *  @param  index The index of the item on the queue we want to access
 *   and return, where index 0 represents the front of the queue and
 *   index == length-1 is the back.
 *
 *  @returns  T Returns the item at "index" on the queue.
 */
template   < class  T >
const  T &   LQueue < T >:: operator []( int  index )   const
{
   // bounds checking, we will throw our stack exception if fails
   if   ( index  <   0   ||  index  >=  numitems )
   {
     throw   InvalidIndexQueueException ( "LQueue<T>::operator[]" );
   }
   // otherwise we will have to search our list for the desired item
   // we will search from the queue front, which is considered
   // index 0
   else
   {
     int  currentIndex  =   0 ;
     Node < T >*  currentNode  =  queueFront ;

     while   ( currentIndex  !=  index )
     {
      currentIndex ++ ;
      currentNode  =  currentNode -> link ;
     }

     return  currentNode -> item ;
   }
}


//-------------------------------------------------------------------------
// The implementation of your overridden enqueue() method for your new
// derived PriorityQueue class should go here.

Queue.hpp

/** @file Queue.hpp * @brief Header file containing a Queue class for Assignment 10 * queues and priority queues. * * @author Jane Programmer * @note cwid : 123 45 678 * @note class: COSC 2336, Summer 2020 * @note ide : Atom Text Editor 1.46.0 / build package / GNU gcc tools * @note assg : Assignment 10 * @date June 1, 2020 * * Assignment 10 queues and priority queues. Use the given Queue ADT to create * a new PriorityQueue ADT, then use queues and your priority queue to create a * random job processing simulation. This header file contains the declaration * of the Queue class container that defines the ADT abstraction for queues to * use for this assignment. */ #include <iostream> #include <string> #include <sstream> using namespace std; #ifndef _QUEUE_H_ #define _QUEUE_H_ //------------------------------------------------------------------------- /** queue (base class) * The basic definition of the Queue Abstract Data Type (ADT) * and queue operations. All declared functions here are * virtual, they must be implemented by concrete derived * classes. */ template <class T> class Queue { public: /** clear * Method to clear out or empty any items on queue, * put queue back to empty state. * Postcondition: Queue is empty. * * @returns void Returns nothing */ virtual void clear() = 0; /** isEmpty * Function to determine whether the queue is empty. Needed * because it is undefined to remove from empty queue. This * function will not change the state of the queue (const). * * @returns bool true if queue is empty, false otherwise. */ virtual bool isEmpty() const = 0; /** enqueue * Add a new item onto back of queue. * * @param newItem The item of template type T to add on back of * the current queue. * * @returns void Returns nothing */ virtual void enqueue(const T& newItem) = 0; /** front * Return the front item from the queue. Note in this ADT, peeking * at the front item does not remove the front item. Some ADT combine * front() and dequeue() as one operation. It is undefined to try and * peek at the front item of an empty queue. Derived classes should * throw an exception if this is attempted. * * @returns T Returns the front item from queue. */ virtual T front() const = 0; /** dequeue * Remove the item from the front of the queue. It is undefined what * it means to try and dequeue from an empty queue. Derived classes should * throw an exception if dequeue() from empty is attempted. * * @returns void Returns nothing */ virtual void dequeue() = 0; /** length * Return the current length or number of item son the queue. * * @returns int The current length of this queue. */ virtual int length() const = 0; /** tostring * Represent queue as a string * * @returns string Returns a string representation of the given queue */ virtual string tostring() const = 0; // overload operators, mostly to support boolean comparison betwen // two queues for testing bool operator==(const Queue<T>& rhs) const; /** indexing operator * A virtual method to provide an indexing operator into the queue. * All subclasses must implement this method. This is not a normal * queue method, we use it for debugging. * * @param index The integer index of the item to be accessed and returned * from this queue. * * @returns T& Returns a reference to the indexed item on the queue. */ virtual const T& operator[](int index) const = 0; // overload output stream operator for all queues using tostring() template <typename U> friend ostream& operator<<(ostream& out, const Queue<U>& aQueue); }; //------------------------------------------------------------------------- /** Empty queue exception * Class for empty queue exceptions */ class EmptyQueueException { private: /// @brief The error message associated with this empty queue exctption string message; public: /** default constructor * The default constructor for an empty queue exception. Construct a * generic error/exception message to be returned. */ EmptyQueueException() { message = "Error: operation on empty queue"; } /** string constructor * Alternate constructor for empty queue exception. Use the given string * to construct an error message with more information about the exception. * * @param str A string with additional information about this exception. */ EmptyQueueException(string str) { message = "Error: " + str + " attempted on emtpy queue"; } /** what accessor * Access and return what the error message associated with this exception. * * @returns string Returns the error message associated with this exception. */ string what() { return message; } }; /** InvalidIndex queue exception * Class to be thrown when an invalid index is asked for when indexing * into a queue object. */ class InvalidIndexQueueException { private: /// @brief The error message associated with this invalid index queue exctption string message; public: /** default constructor * The default constructor for an invalid index queue exception. Construct a * generic error/exception message to be returned. */ InvalidIndexQueueException() { message = "Error: invalid index request for queue"; } /** string constructor * Alternate constructor for invalid index queue exception. Use the given string * to construct an error message with more information about the exception. * * @param str A string with additional information about this exception. */ InvalidIndexQueueException(string str) { message = "Error: " + str + " invalid index request for queue"; } /** what accessor * Access and return what the error message associated with this exception. * * @returns string Returns the error message associated with this exception. */ string what() { return message; } }; //------------------------------------------------------------------------- /** queue (array implementation) * Implementation of the queue ADT as a fixed array. This * implementation combines a circular buffer implementation, to make * sure that both enqueue() and dequeue() operations are O(1) constant * time. However, it also uses dynamic memory allocation, and * demonstrates doubling the size of the allocated space as needed to * grow queue if/when the queue becomes full. */ template <class T> class AQueue : public Queue<T> { private: /// @brief The amount of memory currently allocated for this queue. int allocSize; /// @brief The current length or number of items on the queue. int numitems; /// @brief A pointer to the index of the front item on the queue. int frontIndex; /// @brief A pointer to the back or last item on the queu. int backIndex; /// @brief The items on the queue. This is a dynamically allocated array /// that can grow if needed when queue exceeds current allocation. T* items; public: AQueue(int initialAlloc = 100); // constructor AQueue(int initItems[], int numItems); ~AQueue(); // destructor void clear(); bool isEmpty() const; bool isFull() const; void enqueue(const T& newItem); T front() const; void dequeue(); int length() const; string tostring() const; const T& operator[](int index) const; }; //------------------------------------------------------------------------- /** Node * A basic node contaning an item and a link to the next node in * the linked list. */ template <class T> struct Node { /// @brief The item held by this linked list node T item; /// @brief link A pointer to the next node in a linked list Node<T>* link; }; //------------------------------------------------------------------------- /** queue (linked list implementation) * Implementation of the queue ADT as a dynamic linked list. This implementation * uses link nodes and grows (and shrinks) the nodes as items enqueued and dequeued * onto queue. */ template <class T> class LQueue : public Queue<T> { protected: /// @brief A pointer to the front node of our linked list queue Node<T>* queueFront; /// @brief A pointer to the node holding the back item of the queue Node<T>* queueBack; /// @brief The length or number of items currently on the queue. int numitems; // the queue length public: LQueue(); // default constructor ~LQueue(); // destructor void clear(); bool isEmpty() const; void enqueue(const T& newItem); T front() const; void dequeue(); int length() const; string tostring() const; const T& operator[](int index) const; }; //------------------------------------------------------------------------- // You need to add your PriorityQueue class declaration // here. Your PriorityQueue should be derived from (a child // class of) the LQueue linked list queue class. Your // class will only have/override a single method, the // enqueue() member function. // include template implementations so they are included with header #include "Queue.cpp" #endif // _STACK_H_