Assignment question, submitted solution and professor's solution

This commit is contained in:
Manish 2021-04-09 16:02:13 +02:00
parent 4768ba2a35
commit f496f98edf
3 changed files with 725 additions and 0 deletions

BIN
Ass2.pdf Normal file

Binary file not shown.

BIN
Assignment 2.pdf Normal file

Binary file not shown.

725
ass2.c Normal file
View File

@ -0,0 +1,725 @@
/* License: AGPLv3 or later. https://www.gnu.org/licenses/licenses.html
*
* Assignment 2 - Discrete Simulation
* Manish
* Student Login: *****
*
* Compile it as:
* gcc -Wall -std=c11 -o ass2 ass2.c
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum category
{
TOURIST = 0,
BUSINESS = 1,
};
typedef struct event
{
// 0 for arrival 1+ for unique server ids
int id;
double time;
int class; // 0 for tourist and 1 for business
// service time for customer arrival and busy time for servers
double duration;
} event;
// customers_queue is dynamically scaled
typedef struct customers_queue
{
int first;
int next;
int size; // memory in use
int capacity; // memory allocated to queue
event* queue;
} customers_queue;
typedef struct id_busy
{
int id; // unique server ID
double busy; // time server was busy
} id_busy;
// servers_queue are dynamically infantilized than not re-scaled
typedef struct servers_queue
{
int first;
int next;
int available;
int capacity;
id_busy* queue;
} servers_queue;
// Used as temporary storages for processing
event arrival;
event customer_in_queue;
id_busy server;
// Dynamically initialized than not re-scaled
event* heap;
int heap_size = 0;
customers_queue business_q;
customers_queue tourist_q;
servers_queue business_servers;
servers_queue tourist_servers;
bool dummy_arrival(const event* arrival);
int read_next_arrival(FILE* file, event* e);
void read_back_next_arrival(FILE* file, event* e);
void read_front_next_arrival(FILE* file, event* e);
void shiftdown(int i);
void shiftup(int i);
void swap(event* i, event* j);
event dequeue_customers(customers_queue* q);
void enqueue_customers(customers_queue* q, const event* e);
/* Used for sorting servers by id before printing output
* Heap sort is used since we do not need stable sorting as all ids are unique
* and heap sort sorts in place therefore require less memory than merge sort
*/
void server_shiftdown(servers_queue* heap, int i);
void server_makeheap(servers_queue* q);
void server_sort(servers_queue* q);
void server_swap(id_busy* i, id_busy* j);
id_busy dequeue_servers(servers_queue* q);
void enqueue_servers(servers_queue* q, const id_busy* s);
void free_server(servers_queue* q);
int max(int i, int j);
int main(void)
{
printf("Enter file name: ");
char filename[257];
// Assuming filename/file path won't be longer than 256 characters
scanf("%256s", filename);
FILE* file = fopen(filename, "r");
if (!file)
{
perror(filename);
exit(EXIT_FAILURE);
}
for (int pass = 1; pass <= 2; pass++)
{
rewind(file); // move file pointer back to beginning
// Read no. of business and & tourist servers
fscanf(
file,
"%d %d",
&business_servers.available,
&tourist_servers.available);
arrival.id = 0;
if (read_next_arrival(file, &arrival) == EOF)
{
fprintf(stderr, "Error in reading 1st arrival. Exiting\n");
return 1;
}
// INITIALIZE/RESET VARIABLES
heap = malloc(
sizeof(event)
* (1 + business_servers.available + tourist_servers.available));
business_servers.first = 0;
business_servers.next = 0;
business_servers.capacity = business_servers.available;
business_servers.queue = malloc(
sizeof(event) * business_servers.capacity);
tourist_servers.first = 0;
tourist_servers.next = 0;
tourist_servers.capacity = tourist_servers.available;
tourist_servers.queue = malloc(
sizeof(event) * tourist_servers.capacity);
if (heap == NULL
|| business_servers.queue == NULL
|| tourist_servers.queue == NULL)
{
fprintf(
stderr,
"Failed to allocate memory for all data structures\n");
return 1;
}
business_q.first = 0;
business_q.next = 0;
business_q.size = 0;
business_q.capacity = 0;
// Memory is allocated only if needed
business_q.queue = NULL;
tourist_q.first = 0;
tourist_q.next = 0;
tourist_q.size = 0;
tourist_q.capacity = 0;
// Memory is allocated only if needed
tourist_q.queue = NULL;
for (int i = 0; i < business_servers.capacity; i++)
{
business_servers.queue[i].id = i + 1;
business_servers.queue[i].busy = 0;
}
for (int i = 0; i < tourist_servers.capacity; i++)
{
tourist_servers.queue[i].id = i + business_servers.capacity + 1;
tourist_servers.queue[i].busy = 0;
}
heap[heap_size++] = arrival;
// Statistics Variables
double time = 0;
int max_q_length = 0;
int no_of_business_customers = 0;
double business_service_time = 0;
double business_q_time = 0;
int max_business_q_length = 0;
int no_of_tourist_customers = 0;
double tourist_service_time = 0;
double tourist_q_time = 0;
int max_tourist_q_length = 0;
// MAIN LOOP
while (heap_size > 0)
{
time = heap[0].time;
if (heap[0].id == 0) // Arrival
{
if (heap[0].class == TOURIST)
{
no_of_tourist_customers++;
tourist_service_time += heap[0].duration;
if (tourist_servers.available)
{
// Time when server will finish serving
heap[0].time += heap[0].duration;
server = dequeue_servers(&tourist_servers);
heap[0].id = server.id;
// Total busy time of server
heap[0].duration += server.busy;
shiftdown(0);
read_back_next_arrival(file, &arrival);
}
/* Available/free business servers will serve tourist
* customer in 2nd pass if no business customer in q
* NOTE: free server implies business_q is empty
*/
else if (pass == 2 && business_servers.available)
{
// Time when server will finish serving
heap[0].time += heap[0].duration;
server = dequeue_servers(&business_servers);
heap[0].id = server.id;
// Total busy time of server
heap[0].duration += server.busy;
shiftdown(0);
read_back_next_arrival(file, &arrival);
}
// All relevant servers busy, add to tourist customers queue
else
{
enqueue_customers(&tourist_q, &heap[0]);
max_tourist_q_length = max(
max_tourist_q_length, tourist_q.size);
max_q_length = max(
max_q_length, tourist_q.size + business_q.size);
read_front_next_arrival(file, &arrival);
}
}
else if (heap[0].class == BUSINESS)
{
no_of_business_customers++;
business_service_time += heap[0].duration;
if (business_servers.available)
{
// Time when server will finish serving
heap[0].time += heap[0].duration;
server = dequeue_servers(&business_servers);
heap[0].id = server.id;
// Total busy time of server
heap[0].duration += server.busy;
shiftdown(0);
read_back_next_arrival(file, &arrival);
}
// All relevant servers busy, add to tourist customers queue
else
{
enqueue_customers(&business_q, &heap[0]);
max_business_q_length = max(
max_business_q_length, business_q.size);
max_q_length = max(
max_q_length, tourist_q.size + business_q.size);
read_front_next_arrival(file, &arrival);
}
}
else
{
fprintf(
stderr,
"Impossible scenario: Event = Arrival but Class = %d",
heap[0].class);
exit(1);
}
}
else if ( // Business server finished serving
heap[0].id >= 1 && heap[0].id <= business_servers.capacity)
{
if (business_q.size > 0) // Customers waiting in queue
{
customer_in_queue = dequeue_customers(&business_q);
business_q_time += time - customer_in_queue.time;
// Time when server will finish serving
heap[0].time += customer_in_queue.duration;
// Total busy time of server
heap[0].duration += customer_in_queue.duration;
shiftdown(0);
}
/* Freed business servers will serve tourist customer
* 2nd pass if no business customer in q
*/
else if (pass == 2 && tourist_q.size > 0)
{
customer_in_queue = dequeue_customers(&tourist_q);
// Time when server will finish serving
tourist_q_time += time - customer_in_queue.time;
heap[0].time += customer_in_queue.duration;
// Total busy time of server
heap[0].duration += customer_in_queue.duration;
shiftdown(0);
}
else // free server at heap[0] and enqueue to business_servers
free_server(&business_servers);
}
else if (
heap[0].id > business_servers.capacity
&& heap[0].id <= (business_servers.capacity
+ tourist_servers.capacity))
{
if (tourist_q.size > 0) // Customers waiting in queue
{
customer_in_queue = dequeue_customers(&tourist_q);
// Time when server will finish serving
tourist_q_time += time - customer_in_queue.time;
heap[0].time += customer_in_queue.duration;
// Total busy time of server
heap[0].duration += customer_in_queue.duration;
shiftdown(0);
}
else // free server at heap[0] and enqueue to tourist_servers
free_server(&tourist_servers);
}
else
{
fprintf(
stderr,
"Impossible scenario Heap[0].id = %d is out of range\n",
heap[0].id);
exit(1);
}
}
// COMPUTE REMAINING STATISTICS FROM TRACKED STATISTICS
int total_customers = (
no_of_business_customers + no_of_tourist_customers);
double avg_q_time = (
(business_q_time + tourist_q_time) / total_customers);
double avg_q_length = ((business_q_time + tourist_q_time) / time);
double avg_service_time = (
(business_service_time
+ business_q_time
+ tourist_q_time
+ tourist_service_time)
/ total_customers);
double avg_business_q_time = (
business_q_time / no_of_business_customers);
double avg_business_q_length = (business_q_time / time);
double avg_business_service_time = (
(business_service_time + business_q_time)
/ no_of_business_customers);
double avg_tourist_q_time = (tourist_q_time / no_of_tourist_customers);
double avg_tourist_q_length = (tourist_q_time / time);
double avg_tourist_service_time = (
(tourist_service_time + tourist_q_time) / no_of_tourist_customers);
// SIMULATION OUTPUT/PRINT SIMULATION STATISTICS
char pass_title[80];
if (pass == 1)
strncpy(
pass_title,
"Pass 1: Business servers exclusively serve business class",
80);
else if (pass == 2)
strncpy(
pass_title,
"Pass 2: Idle business servers may serve tourist class",
80);
printf(
"%s\n\n"
"%-50s % 5d\n"
"%-50s % 8.2lf\n\n"
"%s\n"
"%-50s % 8.2lf\n"
"%-50s % 8.2lf\n"
"%-50s % 8.2lf\n"
"%-50s % 5d\n\n"
"%s\n"
"%-50s % 8.2lf\n"
"%-50s % 8.2lf\n"
"%-50s % 8.2lf\n"
"%-50s % 5d\n\n"
"%s\n"
"%-50s % 8.2lf\n"
"%-50s % 8.2lf\n"
"%-50s % 8.2lf\n"
"%-50s % 5d\n"
"\n",
pass_title,
"Number of people served: ",
total_customers,
"Time last service is completed: ",
time,
"Business class customers: ",
"Average total service time: ",
avg_business_service_time,
"Average total time in queue: ",
avg_business_q_time,
"Ave length of queue: ",
avg_business_q_length,
"Maximum number queued: ",
max_business_q_length,
"Tourist class customers:",
"Average total service time: ",
avg_tourist_service_time,
"Average total time in queue: ",
avg_tourist_q_time,
"Ave length of queue: ",
avg_tourist_q_length,
"Maximum number queued: ",
max_tourist_q_length,
"All customers:",
"Average total service time: ",
avg_service_time,
"Average total time in queue: ",
avg_q_time,
"Ave length of queue: ",
avg_q_length,
"Maximum number queued: ",
max_q_length);
// Sort servers by ID before printing
server_sort(&business_servers);
printf("Business class servers:\n");
for (int i = 0; i < business_servers.capacity; i++)
printf(
"Total idle time for business class server %3d: %13.2lf\n",
business_servers.queue[i].id,
time - business_servers.queue[i].busy);
// Sort servers by ID before printing
server_sort(&tourist_servers);
printf("\nTourist class servers:\n");
for (int i = 0; i < tourist_servers.capacity; i++)
printf(
"Total idle time for tourist class server %3d: %14.2lf\n",
tourist_servers.queue[i].id - business_servers.capacity,
time - tourist_servers.queue[i].busy);
printf("\n\n\n");
// FREE DYNAMICALLY ALLOCATED MEMORY
free(heap);
free(business_servers.queue);
free(tourist_servers.queue);
free(business_q.queue);
free(tourist_q.queue);
}
fclose(file);
return 0;
}
bool dummy_arrival(const event* arrival)
{
return (
arrival->time == 0 && arrival->duration == 0);
}
int read_next_arrival(FILE* file, event* e)
{
return fscanf(file, "%lf %d %lf", &e->time, &e->class, &e->duration);
}
void read_back_next_arrival(FILE* file, event* e)
{
// WARNING: Potential BUG: dummy_arrival(e) gets old arrival or new?
if (fscanf(file, "%lf %d %lf", &e->time, &e->class, &e->duration) != EOF
&& dummy_arrival(e) == false)
{
heap[heap_size++] = *e;
shiftup(heap_size - 1);
}
}
void read_front_next_arrival(FILE* file, event* e)
{
if (fscanf(file, "%lf %d %lf", &e->time, &e->class, &e->duration) != EOF
&& dummy_arrival(e) == false)
{
heap[0] = *e;
}
else
{ // Since dummy arrival or EOF reached, pop heap[0]
swap(&heap[0], &heap[--heap_size]);
}
shiftdown(0);
}
void shiftdown(int i)
{
int child = (i * 2) + 1; // left child
if (child < heap_size) // has at least one child
{
if (child < heap_size - 1) // has both children
{
// if right child smaller
if (heap[child].time > heap[child + 1].time)
child++; // pick right child
}
if (heap[i].time > heap[child].time)
{
swap(&heap[i], &heap[child]);
shiftdown(child);
}
}
}
void shiftup(int i)
{
if (i == 0)
return;
int parent = (i - 1) / 2;
if (heap[parent].time > heap[i].time)
{
swap(&heap[parent], &heap[i]);
shiftup(parent);
}
}
void swap(event* i, event* j)
{
event tmp = *i;
*i = *j;
*j = tmp;
}
event dequeue_customers(customers_queue* q)
{
if (q->size-- == 0)
{
fprintf(
stderr,
"Impossible scenario: dequeue_customers when queue empty");
exit(1);
}
event customer = q->queue[q->first++];
q->first %= q->capacity;
if (q->size < q->capacity / 4)
{
int new_capacity = (q->capacity / 2) + (q->capacity % 2);
if (q->size != 0
&& (q->first >= new_capacity || q->next >= new_capacity))
{
/* Original queue:
* XX--------|-------XXX
* OR
* --------XX|XXX-------
* OR
* -----------|-XXXXX----
* Where X has some number, - is empty place & | is new_capacity
* Than, copy queue to next location and free old space
*/
event* new_q = malloc(sizeof(event) * new_capacity);
if (new_q == NULL)
{
fprintf(
stderr,
"Failed to reallocate memory for customer_queue\n");
exit(1);
}
int j = 0;
for (int i = q->first; i != q->next; i = (i + 1) % q->capacity)
{
new_q[j++] = q->queue[i];
}
free(q->queue);
q->queue = new_q;
q->first = 0;
q->next = j;
}
else
{
q->queue = realloc(q->queue, sizeof(event) * new_capacity);
if (q->queue == NULL) // Probably unnecessary since shrinking
{
fprintf(
stderr, "Failed to reallocate memory for customer_queue\n");
exit(1);
}
if (q->size == 0)
{
q->first = 0;
q->next = 0;
}
}
q->capacity = new_capacity;
}
return customer;
}
void enqueue_customers(customers_queue* q, const event* e)
{
if (q->size == q->capacity) // Queue full
{
// Double queue and adjust pointers
// max() for when no space allocated previously i.e. capacity == 0
int new_capacity = max(q->capacity * 2, 1);
q->queue = realloc(q->queue, sizeof(event) * new_capacity);
if (q->queue == NULL)
{
fprintf(
stderr, "Failed to reallocate memory for customer_queue\n");
exit(1);
}
if (q->first != 0)
{
/* Original queue:
* XXX|XXXXXXX
* After realloc:
* XX|XXXXXXXX----------
* After this loop:
* XX----------|XXXXXXXX
* Where X has some number, - is empty place
* and | is q->first pointer
*/
for (int i = q->first; i < q->capacity; i++)
{
q->queue[i + q->capacity] = q->queue[i];
}
q->first += q->capacity;
}
/// MISSING THIS CORNER CASE WAS CAUSE OF A MAJOR BUG
else
q->next = q->size;
q->capacity = new_capacity;
}
q->size++;
q->queue[q->next++] = *e;
q->next %= q->capacity;
}
id_busy dequeue_servers(servers_queue* q)
{
if (q->available-- == 0)
{
fprintf(
stderr,
"Impossible scenario: dequeue_servers when queue empty");
exit(1);
}
server = q->queue[q->first++];
q->first %= q->capacity;
return server;
}
void enqueue_servers(servers_queue* q, const id_busy* s)
{
if (q->available++ == q->capacity)
{
fprintf(
stderr, "Impossible scenario: enqueue_servers when queue full");
exit(1);
}
q->queue[q->next++] = *s;
q->next %= q->capacity;
}
void free_server(servers_queue* q)
{
server.id = heap[0].id;
server.busy = heap[0].duration;
enqueue_servers(q, &server);
heap[0] = heap[--heap_size];
shiftdown(0);
}
int max(int i, int j)
{
return (i > j) ? i : j;
}
void server_shiftdown(servers_queue* heap, int i)
{
int child = (i * 2) + 1; // left child
if (child < heap->available) // has at least one child
{
if (child < heap->available - 1) // has both children
{
// if right child smaller
if (heap->queue[child].id < heap->queue[child + 1].id)
child++; // pick right child
}
if (heap->queue[i].id < heap->queue[child].id)
{
server_swap(&heap->queue[child], &heap->queue[i]);
server_shiftdown(heap, child);
}
}
}
void server_makeheap(servers_queue* q)
{
// Shiftdown Method
int shiftdowns_required = (q->capacity / 2)-1;
for (int i = shiftdowns_required; i >= 0; i--)
{
server_shiftdown(q, i);
}
}
void server_sort(servers_queue* q)
{
server_makeheap(q);
while(q->available > 0)
{
server_swap(q->queue, &q->queue[--q->available]);
server_shiftdown(q, 0);
}
q->available = q->capacity;
}
void server_swap(id_busy* i, id_busy* j)
{
id_busy tmp = *i;
*i = *j;
*j = tmp;
}