Non Preemptive Priority Scheduling

/**
 * @file
 * @brief
 * [Non-Preemptive Priority
 * Scheduling](https://en.wikipedia.org/wiki/Scheduling_(computing))
 * is a scheduling algorithm that selects the tasks to execute based on
 * priority.
 *
 * @details
 * In this algorithm, processes are executed according to their
 * priority. The process with the highest priority is to be executed first and
 * so on. In this algorithm, a variable is maintained known as the time quantum.
 * The length of the time quantum is decided by the user. The process which is
 * being executed is interrupted after the expiration of the time quantum and
 * the next process with the highest priority is executed. This cycle of
 * interrupting the process after every time quantum and resuming the next
 * process with the highest priority continues until all the processes have
 * been executed.
 * @author [Aryan Raj](https://github.com/aryaraj132)
 */
#include <assert.h>   /// for assert
#include <stdbool.h>  /// for boolean data type
#include <stdio.h>    /// for IO operations (`printf`)
#include <stdlib.h>  /// for memory allocation eg: `malloc`, `realloc`, `free`, `exit`
/**
 * @brief Structure to represent a process
 */
 typedef struct node {
     int ID;             ///< ID of the process node
     int AT;             ///< Arrival Time of the process node
     int BT;             ///< Burst Time of the process node
     int priority;       ///< Priority of the process node
     int CT;             ///< Completion Time of the process node
     int WT;             ///< Waiting Time of the process node
     int TAT;            ///< Turn Around Time of the process node
     struct node *next;  ///< pointer to the node
 } node;

/**
 * @brief To insert a new process in the queue
 * @param root pointer to the head of the queue
 * @param id process ID
 * @param at arrival time
 * @param bt burst time
 * @param prior priority of the process
 * @returns void
 */
void insert(node **root, int id, int at, int bt, int prior)
{
    // create a new node and initialize it
    node *new = (node *)malloc(sizeof(node));
    node *ptr = *root;
    new->ID = id;
    new->AT = at;
    new->BT = bt;
    new->priority = prior;
    new->next = NULL;
    new->CT = 0;
    new->WT = 0;
    new->TAT = 0;
    // if the root is null, make the new node the root
    if (*root == NULL)
    {
        *root = new;
        return;
    }
    // else traverse to the end of the queue and insert the new node there
    while (ptr->next != NULL)
    {
        ptr = ptr->next;
    }
    ptr->next = new;
    return;
}
/*
 * @brief To delete a process from the queue
 * @param root pointer to the head of the queue
 * @param id process ID
 * @returns void
 */
void delete(node **root, int id)
{
    node *ptr = *root, *prev;
    // if the root is null, return
    if (ptr == NULL)
    {
        return;
    }
    // if the root is the process to be deleted, make the next node the root
    if (ptr->ID == id)
    {
        *root = ptr->next;
        free(ptr);
        return;
    }
    // else traverse the queue and delete the process
    while (ptr != NULL && ptr->ID != id)
    {
        prev = ptr;
        ptr = ptr->next;
    }
    if (ptr == NULL)
    {
        return;
    }
    prev->next = ptr->next;
    free(ptr);
}
/**
 * @brief To show the process queue
 * @param head pointer to the head of the queue
 * @returns void
 */
void show_list(node *head)
{
    printf("Process Priority AT BT CT TAT WT \n");
    while (head != NULL)
    {
        printf("P%d. %d %d %d %d %d %d \n", head->ID, head->priority, head->AT,
               head->BT, head->CT, head->TAT, head->WT);
        head = head->next;
    }
}
/**
 * @brief To length process queue
 * @param root pointer to the head of the queue
 * @returns int total length of the queue
 */
int l_length(node **root)
{
    int count = 0;
    node *ptr = *root;
    while (ptr != NULL)
    {
        count++;
        ptr = ptr->next;
    }
    return count;
}
/**
 * @brief To update the completion time, turn around time and waiting time of
 * the processes
 * @param root pointer to the head of the queue
 * @param id process ID
 * @param ct current time
 * @param wt waiting time
 * @param tat turn around time
 * @returns void
 */
void update(node **root, int id, int ct, int wt, int tat)
{
    node *ptr = *root;
    // If process to be updated is head node
    if (ptr != NULL && ptr->ID == id)
    {
        if (ct != 0)
        {
            ptr->CT = ct;
        }
        if (wt != 0)
        {
            ptr->WT = wt;
        }
        if (tat != 0)
        {
            ptr->TAT = tat;
        }
        return;
    }
    // else traverse the queue and update the values
    while (ptr != NULL && ptr->ID != id)
    {
        ptr = ptr->next;
    }
    if (ct != 0)
    {
        ptr->CT = ct;
    }
    if (wt != 0)
    {
        ptr->WT = wt;
    }
    if (tat != 0)
    {
        ptr->TAT = tat;
    }
    return;
}
/**
 * @brief To compare the priority of two processes based on their arrival time
 * and priority
 * @param a pointer to the first process
 * @param b pointer to the second process
 * @returns true if the priority of the first process is greater than the
 * the second process
 * @returns false if the priority of the first process is NOT greater than the
 * second process
 */
bool compare(node *a, node *b)
{
    if (a->AT == b->AT)
    {
        return a->priority < b->priority;
    }
    else
    {
        return a->AT < b->AT;
    }
}
/**
 * @brief To calculate the average completion time of all the processes
 * @param root pointer to the head of the queue
 * @returns float average completion time
 */
float calculate_ct(node **root)
{
    // calculate the total completion time of all the processes
    node *ptr = *root, *prior, *rpt;
    int ct = 0, i, time = 0;
    int n = l_length(root);
    float avg, sum = 0;
    node *duproot = NULL;
    // create a duplicate queue
    while (ptr != NULL)
    {
        insert(&duproot, ptr->ID, ptr->AT, ptr->BT, ptr->priority);
        ptr = ptr->next;
    }
    ptr = duproot;
    rpt = ptr->next;
    // sort the queue based on the arrival time and priority
    while (rpt != NULL)
    {
        if (!compare(ptr, rpt))
        {
            ptr = rpt;
        }
        rpt = rpt->next;
    }
    // ptr is the process to be executed first.
    ct = ptr->AT + ptr->BT;
    time = ct;
    sum += ct;
    // update the completion time, turn around time and waiting time of the
    // process
    update(root, ptr->ID, ct, 0, 0);
    delete (&duproot, ptr->ID);
    // repeat the process until all the processes are executed
    for (i = 0; i < n - 1; i++)
    {
        ptr = duproot;
        while (ptr != NULL && ptr->AT > time)
        {
            ptr = ptr->next;
        }
        rpt = ptr->next;
        while (rpt != NULL)
        {
            if (rpt->AT <= time)
            {
                if (rpt->priority < ptr->priority)
                {
                    ptr = rpt;
                }
            }
            rpt = rpt->next;
        }
        ct += ptr->BT;
        time += ptr->BT;
        sum += ct;
        update(root, ptr->ID, ct, 0, 0);
        delete (&duproot, ptr->ID);
    }
    avg = sum / n;
    return avg;
}
/**
 * @brief To calculate the average turn around time of all the processes
 * @param root pointer to the head of the queue
 * @returns float average turn around time
 */
float calculate_tat(node **root)
{
    float avg, sum = 0;
    int n = l_length(root);
    node *ptr = *root;
    // calculate the completion time if not already calculated
    if (ptr->CT == 0)
    {
        calculate_ct(root);
    }
    // calculate the total turn around time of all the processes
    while (ptr != NULL)
    {
        ptr->TAT = ptr->CT - ptr->AT;
        sum += ptr->TAT;
        ptr = ptr->next;
    }
    avg = sum / n;
    return avg;
}
/**
 * @brief To calculate the average waiting time of all the processes
 * @param root pointer to the head of the queue
 * @returns float average waiting time
 */
float calculate_wt(node **root)
{
    float avg, sum = 0;
    int n = l_length(root);
    node *ptr = *root;
    // calculate the completion if not already calculated
    if (ptr->CT == 0)
    {
        calculate_ct(root);
    }
    // calculate the total waiting time of all the processes
    while (ptr != NULL)
    {
        ptr->WT = (ptr->TAT - ptr->BT);
        sum += ptr->WT;
        ptr = ptr->next;
    }
    avg = sum / n;
    return avg;
}

/**
 * @brief Self-test implementations
 * @returns void
 */
static void test()
{
    // Entered processes
    // printf("ID Priority Arrival Time Burst Time \n");
    // printf("1 0 5 1 \n");
    // printf("2 1 4 2 \n");
    // printf("3 2 3 3 \n");
    // printf("4 3 2 4 \n");
    // printf("5 4 1 5 \n");

    node *root = NULL;
    insert(&root, 1, 0, 5, 1);
    insert(&root, 2, 1, 4, 2);
    insert(&root, 3, 2, 3, 3);
    insert(&root, 4, 3, 2, 4);
    insert(&root, 5, 4, 1, 5);
    float avgCT = calculate_ct(&root);
    float avgTAT = calculate_tat(&root);
    float avgWT = calculate_wt(&root);
    assert(avgCT == 11);
    assert(avgTAT == 9);
    assert(avgWT == 6);
    printf("[+] All tests have successfully passed!\n");
    // printf("Average Completion Time is : %f \n", calculate_ct(&root));
    // printf("Average Turn Around Time is : %f \n", calculate_tat(&root));
    // printf("Average Waiting Time is : %f \n", calculate_wt(&root));
}

/**
 * @brief Main function
 * @returns 0 on exit
 */
int main()
{
    test();  // run self-test implementations

    return 0;
}