⚡

PCCSL307 · Data Structures Lab

KTU B.Tech CSE S3 · Master Mobile Portal

All (18) Arrays & Stacks Queues & Deque Linked Lists Trees & BST Graphs & Puzzles Searching & Sorting Hashing & Allocator

Complete S3 Data Structures Lab Suite

18 Verified Experiments • Step-by-Step Algorithms • SVG Flowcharts • C Programs • Live Visualizers • Viva Bank • 3D Graph

CIE: 50 Marks ESE: 50 Marks CO1 – CO4 Covered Bloom's K1 – K4
Expt 1 CO1, CO2 • Level K3

Sparse Polynomial Addition using Arrays

▼
Problem Statement: Represent two polynomials with sparse non-zero terms using arrays of structures (coefficient, exponent) and compute their sum by merging corresponding terms in descending order of powers.

Structured Algorithm Steps:

  1. Define structure Term with fields coeff and exp.
  2. Initialize read pointers i = 0 for Poly A, j = 0 for Poly B, and k = 0 for Result.
  3. While i < nA and j < nB:
  4. a. If A[i].exp == B[j].exp: Set Res[k].coeff = A[i].coeff + B[j].coeff, Res[k].exp = A[i].exp, and increment i, j, k.
  5. b. If A[i].exp > B[j].exp: Set Res[k] = A[i], increment i and k.
  6. c. If A[i].exp < B[j].exp: Set Res[k] = B[j], increment j and k.
  7. Copy any remaining elements of Poly A to Res.
  8. Copy any remaining elements of Poly B to Res.
  9. Output the resultant polynomial and terminate.
Vector SVG Flowchart Logic:
START Initialize i=0, j=0, k=0 A[i].exp == B[j].exp ? YES Res.coeff = A.coeff + B.coeff NO Copy larger exponent term END
C Source Program (PCCSL307 Compliant)
#include <stdio.h>

struct Term {
    int coeff;
    int exp;
};

int addPolynomials(struct Term A[], int nA, struct Term B[], int nB, struct Term Res[]) {
    int i = 0, j = 0, k = 0;
    while (i < nA && j < nB) {
        if (A[i].exp == B[j].exp) {
            int sum = A[i].coeff + B[j].coeff;
            if (sum != 0) {
                Res[k].coeff = sum;
                Res[k].exp = A[i].exp;
                k++;
            }
            i++; j++;
        } else if (A[i].exp > B[j].exp) {
            Res[k++] = A[i++];
        } else {
            Res[k++] = B[j++];
        }
    }
    while (i < nA) Res[k++] = A[i++];
    while (j < nB) Res[k++] = B[j++];
    return k;
}

int main() {
    struct Term A[] = {{4, 3}, {3, 2}, {5, 0}};
    struct Term B[] = {{2, 3}, {4, 1}, {1, 0}};
    struct Term Res[10];
    int nRes = addPolynomials(A, 3, B, 3, Res);

    printf("Resultant Polynomial: ");
    for (int i = 0; i < nRes; i++) {
        printf("%dx^%d%s", Res[i].coeff, Res[i].exp, (i < nRes - 1) ? " + " : "\n");
    }
    return 0;
}
Actual Terminal Output (program compiled and run with gcc (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r2) 16.2.0, -Wall -Wextra -std=c11 -pedantic):
Resultant Polynomial: 6x^3 + 3x^2 + 4x^1 + 6x^0
Status: Ready. Tap Play or Next Step to begin visual execution.
🚀 Industry Application:

Computer Algebra Systems (SymPy, Wolfram Mathematica), 3D Bézier curve rendering in game engines, and numerical signal polynomial filters.

Expt 2 CO1, CO2 • Level K3

Sparse Matrix Transpose & Addition

▼
Problem Statement: Given matrices with predominantly zero values, represent them in 3-Tuple (Row, Column, Value) format. Add two sparse matrices by merging their tuples in row-major order, and find the transpose with the fast transpose algorithm in O(cols + nonzeros) time.

Structured Algorithm Steps:

  1. Represent each matrix as an array of triplets; tuple[0] stores (rows, cols, total_non_zeros).
  2. For addition: check that both matrices have the same dimensions.
  3. Traverse tuples of A and B in row-major order, comparing (row, col).
  4. a. If positions are equal: add the values and keep the sum only if it is not zero.
  5. b. Otherwise copy the tuple with the smaller (row, col) into the result.
  6. Copy the remaining tuples of A or B, then set the result header.
  7. For Fast Transpose: count the non-zeros in each column of A.
  8. Compute the starting position of each column in the transposed matrix B.
  9. For each tuple of A, place it at its target slot in B with row and column swapped.
  10. Print the 3-tuple and dense forms of A, B, A + B and the transpose.
Vector SVG Flowchart Logic:
START Read 3-Tuple Matrix (r, c, val) Count non-zeros per column Compute start position index array Map elements: Trans[pos] = (c, r, v)
C Source Program (PCCSL307 Compliant)
/*
 * Experiment 2: Sparse Matrix Transpose and Addition
 * Syllabus task: Find the transpose of a sparse matrix and the sum
 * of two sparse matrices.
 *
 * How it works:
 *   A sparse matrix is stored as a 3-tuple (triplet) table. Entry 0
 *   holds (rows, cols, number of non-zeros); entries 1..n hold the
 *   (row, col, value) of each non-zero, in row-major order.
 *   - Addition walks both tables together, like merging two sorted
 *     lists: the earlier position is copied, equal positions are
 *     added, and a sum of zero is dropped.
 *   - Fast transpose counts the terms in each column, turns the
 *     counts into start positions, then puts every term straight
 *     into its slot. Time: O(cols + non-zeros).
 */
#include <stdio.h>
#include <stdbool.h>

#define MAX_TERMS 20   /* table size: 1 header + up to 19 non-zeros */
#define MAX_DIM   10   /* largest number of rows or columns         */

/* One entry of a 3-tuple table. */
typedef struct {
    int row;
    int col;
    int val;
} Term;

/* Compare positions in row-major order: row first, then column.
 * Negative: x comes first, 0: same place, positive: y comes first. */
static int comparePos(Term x, Term y) {
    if (x.row != y.row) return x.row - y.row;
    return x.col - y.col;
}

/* Guard: the table must fit our arrays, every term must lie inside
 * the matrix, and the terms must be in row-major order. */
static bool isValid(const Term t[]) {
    int rows = t[0].row, cols = t[0].col, n = t[0].val;
    if (rows < 1 || rows > MAX_DIM || cols < 1 || cols > MAX_DIM) return false;
    if (n < 0 || n >= MAX_TERMS) return false;
    for (int i = 1; i <= n; i++) {
        if (t[i].row < 0 || t[i].row >= rows) return false;
        if (t[i].col < 0 || t[i].col >= cols) return false;
        if (i > 1 && comparePos(t[i - 1], t[i]) >= 0) return false;
    }
    return true;
}

/* Print a 3-tuple table; entry 0 is the header. */
static void printTuples(const char *name, const Term t[]) {
    printf("%s in 3-tuple form:\n", name);
    printf("  Row Col Val\n");
    for (int i = 0; i <= t[0].val; i++) {
        printf("  %3d %3d %3d%s\n", t[i].row, t[i].col, t[i].val,
               i == 0 ? "   <- rows, cols, non-zeros" : "");
    }
}

/* Expand a 3-tuple table into a full grid and print it. */
static void printDense(const char *name, const Term t[]) {
    int grid[MAX_DIM][MAX_DIM] = {{0}};
    for (int i = 1; i <= t[0].val; i++) {
        grid[t[i].row][t[i].col] = t[i].val;
    }
    printf("%s as a dense matrix:\n", name);
    for (int r = 0; r < t[0].row; r++) {
        printf(" ");
        for (int c = 0; c < t[0].col; c++) printf(" %4d", grid[r][c]);
        printf("\n");
    }
}

/* Append one term to table c at position *k (checks for space). */
static bool appendTerm(Term c[], int *k, Term t) {
    if (*k >= MAX_TERMS) {
        printf("Error: result table is full\n");
        return false;
    }
    c[*k] = t;
    (*k)++;
    return true;
}

/* A term found in only one matrix is copied as it is. */
static bool copyStep(const char *from, Term t, Term c[], int *k) {
    printf("  (%d,%d) only in %s: copy %d\n", t.row, t.col, from, t.val);
    return appendTerm(c, k, t);
}

/* C = A + B, all in 3-tuple form. Returns false on an error. */
static bool addSparse(const Term a[], const Term b[], Term c[]) {
    int i = 1, j = 1, k = 1;
    int na = a[0].val, nb = b[0].val;

    /* Step 1: matrices can be added only if their sizes match. */
    if (a[0].row != b[0].row || a[0].col != b[0].col) {
        printf("Error: cannot add a %d x %d and a %d x %d matrix\n",
               a[0].row, a[0].col, b[0].row, b[0].col);
        return false;
    }
    printf("A + B: both are %d x %d, merge in row-major order\n",
           a[0].row, a[0].col);

    /* Step 2: walk both tables while each has terms left. */
    while (i <= na && j <= nb) {
        int order = comparePos(a[i], b[j]);
        if (order < 0) {                   /* A's term comes first */
            if (!copyStep("A", a[i++], c, &k)) return false;
        } else if (order > 0) {            /* B's term comes first */
            if (!copyStep("B", b[j++], c, &k)) return false;
        } else {                           /* same place: add */
            Term s = {a[i].row, a[i].col, a[i].val + b[j].val};
            printf("  (%d,%d) in both: A %d, B %d -> sum %d%s\n",
                   s.row, s.col, a[i].val, b[j].val, s.val,
                   s.val == 0 ? ", dropped" : "");
            if (s.val != 0 && !appendTerm(c, &k, s)) return false;
            i++;
            j++;
        }
    }

    /* Step 3: copy the terms left over in A or in B. */
    while (i <= na) {
        if (!copyStep("A", a[i++], c, &k)) return false;
    }
    while (j <= nb) {
        if (!copyStep("B", b[j++], c, &k)) return false;
    }

    /* Step 4: header = same size, k - 1 non-zeros. */
    c[0].row = a[0].row;
    c[0].col = a[0].col;
    c[0].val = k - 1;
    return true;
}

/* T = transpose of A using the fast transpose method. */
static void fastTranspose(const Term a[], Term t[]) {
    int count[MAX_DIM] = {0};   /* non-zeros in each column of A */
    int start[MAX_DIM] = {0};   /* next free slot for each column */
    int cols = a[0].col, n = a[0].val;

    /* Header: rows and columns swap; the count stays the same. */
    t[0].row = a[0].col;
    t[0].col = a[0].row;
    t[0].val = n;

    /* Step 1: count the terms in each column of A. */
    for (int i = 1; i <= n; i++) count[a[i].col]++;

    /* Step 2: column c starts right after columns 0..c-1. */
    start[0] = 1;
    for (int c = 1; c < cols; c++) start[c] = start[c - 1] + count[c - 1];

    printf("Transpose of A: fast transpose\n");
    printf("  Column   :");
    for (int c = 0; c < cols; c++) printf(" %3d", c);
    printf("\n  Count    :");
    for (int c = 0; c < cols; c++) printf(" %3d", count[c]);
    printf("\n  Start pos:");
    for (int c = 0; c < cols; c++) printf(" %3d", start[c]);
    printf("\n");

    /* Step 3: drop each term into its slot with row and col swapped. */
    for (int i = 1; i <= n; i++) {
        int slot = start[a[i].col];
        start[a[i].col]++;
        t[slot].row = a[i].col;
        t[slot].col = a[i].row;
        t[slot].val = a[i].val;
        printf("  A(%d,%d) = %d -> slot %d as (%d,%d)\n",
               a[i].row, a[i].col, a[i].val, slot, a[i].col, a[i].row);
    }
}

int main(void) {
    /* Matrix A: the sample matrix used earlier in this experiment. */
    Term a[MAX_TERMS] = {{4, 4, 3}, {0, 1, 10}, {1, 3, 20}, {3, 0, 30}};
    /* Matrix B: same size; (0,1) overlaps A, and at (3,0) the -30
     * cancels A's 30, so that position must vanish from A + B. */
    Term b[MAX_TERMS] = {{4, 4, 3}, {0, 1, 5}, {2, 2, 40}, {3, 0, -30}};
    Term sum[MAX_TERMS] = {{0}};
    Term at[MAX_TERMS] = {{0}};

    printf("Experiment 2: Sparse Matrix Transpose and Addition\n\n");
    if (!isValid(a) || !isValid(b)) {
        printf("Error: an input table is not a valid 3-tuple table\n");
        return 1;
    }

    printTuples("Matrix A", a);
    printDense("Matrix A", a);
    printf("\n");
    printTuples("Matrix B", b);
    printDense("Matrix B", b);
    printf("\n");

    if (addSparse(a, b, sum)) {
        printTuples("A + B", sum);
        printDense("A + B", sum);
    }
    printf("\n");

    fastTranspose(a, at);
    printTuples("Transpose of A", at);
    printDense("Transpose of A", at);
    return 0;
}
Actual Terminal Output (program compiled and run with gcc (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r2) 16.2.0, -Wall -Wextra -std=c11 -pedantic):
Experiment 2: Sparse Matrix Transpose and Addition

Matrix A in 3-tuple form:
  Row Col Val
    4   4   3   <- rows, cols, non-zeros
    0   1  10
    1   3  20
    3   0  30
Matrix A as a dense matrix:
     0   10    0    0
     0    0    0   20
     0    0    0    0
    30    0    0    0

Matrix B in 3-tuple form:
  Row Col Val
    4   4   3   <- rows, cols, non-zeros
    0   1   5
    2   2  40
    3   0 -30
Matrix B as a dense matrix:
     0    5    0    0
     0    0    0    0
     0    0   40    0
   -30    0    0    0

A + B: both are 4 x 4, merge in row-major order
  (0,1) in both: A 10, B 5 -> sum 15
  (1,3) only in A: copy 20
  (2,2) only in B: copy 40
  (3,0) in both: A 30, B -30 -> sum 0, dropped
A + B in 3-tuple form:
  Row Col Val
    4   4   3   <- rows, cols, non-zeros
    0   1  15
    1   3  20
    2   2  40
A + B as a dense matrix:
     0   15    0    0
     0    0    0   20
     0    0   40    0
     0    0    0    0

Transpose of A: fast transpose
  Column   :   0   1   2   3
  Count    :   1   1   0   1
  Start pos:   1   2   3   3
  A(0,1) = 10 -> slot 2 as (1,0)
  A(1,3) = 20 -> slot 3 as (3,1)
  A(3,0) = 30 -> slot 1 as (0,3)
Transpose of A in 3-tuple form:
  Row Col Val
    4   4   3   <- rows, cols, non-zeros
    0   3  30
    1   0  10
    3   1  20
Transpose of A as a dense matrix:
     0    0    0   30
    10    0    0    0
     0    0    0    0
     0   20    0    0
Status: Ready. Tap Play or Next Step to begin visual execution.
🚀 Industry Application:

Google PageRank algorithm (web graph adjacency matrix), Netflix collaborative filtering recommendation engines, structural finite element stress analysis.

Expt 3 CO1, CO2 • Level K3

Infix to Postfix Conversion & Stack Evaluation

▼
Problem Statement: Convert an infix expression to postfix using an operator stack, then evaluate the postfix expression using an operand stack. Handle parentheses, operator precedence, right-associative ^, and errors such as division by zero.

Structured Algorithm Steps:

  1. Scan the infix expression from left to right.
  2. If the symbol is an operand: append it to the postfix output.
  3. If the symbol is (: push it on the operator stack.
  4. If the symbol is ): pop operators to the output until ( is found, then discard (.
  5. If the symbol is an operator: pop operators of higher precedence (or equal precedence, unless the operator is right-associative ^), then push the new operator.
  6. At the end, pop all remaining operators to the output.
  7. To evaluate: scan the postfix string left to right.
  8. a. Operand: push its value on the operand stack.
  9. b. Operator: pop two operands, apply the operator, push the result.
  10. Check for stack underflow and division by zero; the final value left on the stack is the result.
Vector SVG Flowchart Logic:
START Token is Operand? YES Append to Output NO Check Precedence / Stack Repeat until End of String END
C Source Program (PCCSL307 Compliant)
/*
 * Experiment 3: Infix to Postfix Conversion and Evaluation
 * Syllabus task: Convert an infix expression to postfix and then
 * evaluate the postfix expression using a stack.
 *
 * How it works:
 *   1. Conversion: scan the infix string from left to right.
 *      Operands go straight to the postfix string. Operators wait
 *      on an operator stack and leave it when an operator of lower
 *      precedence arrives or a ')' closes their group.
 *      + - * / are left-associative; ^ is right-associative.
 *   2. Evaluation: scan the postfix string. Push each operand on an
 *      integer stack; for an operator pop b, pop a, push (a op b).
 *   Operands are single digits 0-9, so every character is one token.
 *   All arithmetic is integer arithmetic (7/2 gives 3).
 */
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>

#define MAX 50   /* longest expression and deepest stack */

/* Operator stack used for conversion. */
typedef struct {
    char items[MAX];
    int top;            /* index of the top item, -1 when empty */
} CharStack;

/* Operand stack used for evaluation. */
typedef struct {
    int items[MAX];
    int top;
} IntStack;

/* Print an error message and report failure. */
static bool fail(const char *message) {
    printf("    Error: %s\n", message);
    return false;
}

/* Push onto the operator stack; false on overflow. */
static bool pushChar(CharStack *s, char c) {
    if (s->top == MAX - 1) return fail("operator stack overflow");
    s->items[++s->top] = c;
    return true;
}

/* Pop from the operator stack; false on underflow. */
static bool popChar(CharStack *s, char *c) {
    if (s->top == -1) return fail("operator stack underflow");
    *c = s->items[s->top--];
    return true;
}

/* Push onto the operand stack; false on overflow. */
static bool pushInt(IntStack *s, int v) {
    if (s->top == MAX - 1) return fail("operand stack overflow");
    s->items[++s->top] = v;
    return true;
}

/* Pop from the operand stack; false on underflow (missing operand). */
static bool popInt(IntStack *s, int *v) {
    if (s->top == -1) return fail("operand stack underflow");
    *v = s->items[s->top--];
    return true;
}

/* Precedence: ^ highest, then * and /, then + and -; 0 = not an operator. */
static int precedence(char op) {
    if (op == '^') return 3;
    if (op == '*' || op == '/') return 2;
    if (op == '+' || op == '-') return 1;
    return 0;
}

/* Must the operator on top of the stack leave before op is pushed? */
static bool shouldPop(char top, char op) {
    if (top == '(') return false;          /* '(' waits for its ')' */
    if (op == '^') return precedence(top) > precedence(op);  /* right */
    return precedence(top) >= precedence(op);                /* left  */
}

/* Print one conversion row: symbol, operator stack, postfix so far. */
static void showRow(const char *symbol, const CharStack *s,
                    const char *postfix) {
    if (postfix[0] == '\0') {
        printf("    %-6s %.*s\n", symbol, s->top + 1, s->items);
    } else {
        printf("    %-6s %-9.*s %s\n", symbol, s->top + 1, s->items, postfix);
    }
}

/* Convert infix to postfix, printing each step if trace is true. */
static bool infixToPostfix(const char *infix, char postfix[], bool trace) {
    CharStack ops = {.top = -1};
    int k = 0;                 /* length of the postfix string so far */
    char c = '\0';

    if (strlen(infix) >= MAX) return fail("expression too long");
    postfix[0] = '\0';
    if (trace) printf("    Symbol Op stack  Postfix\n");

    for (int i = 0; infix[i] != '\0'; i++) {
        char ch = infix[i];
        char symbol[2] = {ch, '\0'};
        if (isdigit((unsigned char)ch)) {
            postfix[k++] = ch;                       /* operand: output */
        } else if (ch == '(') {
            if (!pushChar(&ops, ch)) return false;  /* open a group */
        } else if (ch == ')') {
            /* Close a group: pop to the output until '(' appears. */
            while (ops.top != -1 && ops.items[ops.top] != '(') {
                if (!popChar(&ops, &c)) return false;
                postfix[k++] = c;
            }
            if (ops.top == -1) return fail("')' without a matching '('");
            if (!popChar(&ops, &c)) return false;   /* discard the '(' */
        } else if (precedence(ch) > 0) {
            /* Operator: first pop the operators that must go before it. */
            while (ops.top != -1 && shouldPop(ops.items[ops.top], ch)) {
                if (!popChar(&ops, &c)) return false;
                postfix[k++] = c;
            }
            if (!pushChar(&ops, ch)) return false;
        } else {
            return fail("invalid symbol in expression");
        }
        postfix[k] = '\0';
        if (trace) showRow(symbol, &ops, postfix);
    }

    /* End of input: move every remaining operator to the output. */
    while (ops.top != -1) {
        if (!popChar(&ops, &c)) return false;
        if (c == '(') return fail("'(' without a matching ')'");
        postfix[k++] = c;
    }
    postfix[k] = '\0';
    if (trace) showRow("end", &ops, postfix);
    return true;
}

/* Compute a op b. Returns NULL on success, or an error message. */
static const char *applyOperator(char op, int a, int b, int *result) {
    switch (op) {
    case '+': *result = a + b; return NULL;
    case '-': *result = a - b; return NULL;
    case '*': *result = a * b; return NULL;
    case '/':
        if (b == 0) return "division by zero";
        *result = a / b;
        return NULL;
    case '^':
        if (b < 0) return "negative power";
        *result = 1;
        for (int i = 0; i < b; i++) *result *= a;
        return NULL;
    default:
        return "unknown operator";
    }
}

/* Evaluate a postfix string, printing every push and every operation. */
static bool evaluatePostfix(const char *postfix, int *result) {
    IntStack s = {.top = -1};
    char action[32];

    printf("    Token  Action          Operand stack\n");
    for (int i = 0; postfix[i] != '\0'; i++) {
        char ch = postfix[i];
        if (isdigit((unsigned char)ch)) {
            /* Operand: push its value. */
            if (!pushInt(&s, ch - '0')) return false;
            snprintf(action, sizeof action, "push %d", ch - '0');
        } else {
            /* Operator: pop b (the top) first, then a; push a op b. */
            int a = 0, b = 0, r = 0;
            const char *err;
            if (!popInt(&s, &b) || !popInt(&s, &a)) return false;
            err = applyOperator(ch, a, b, &r);
            if (err != NULL) {
                printf("    %-6c %d %c %d -> Error: %s\n", ch, a, ch, b, err);
                return false;
            }
            if (!pushInt(&s, r)) return false;
            snprintf(action, sizeof action, "%d %c %d = %d", a, ch, b, r);
        }
        printf("    %-6c %-15s", ch, action);
        for (int j = 0; j <= s.top; j++) printf(" %d", s.items[j]);
        printf("\n");
    }

    /* A valid expression leaves exactly one value on the stack. */
    if (s.top != 0) return fail("operands left over (missing operator)");
    *result = s.items[0];
    return true;
}

/* Convert, evaluate and report one expression. */
static void runExample(int number, const char *infix, const char *note,
                       bool trace) {
    char postfix[MAX + 1];
    int value = 0;

    printf("[%d] Infix  : %s   = %s\n", number, infix, note);
    if (!infixToPostfix(infix, postfix, trace)) return;
    printf("    Postfix: %s\n", postfix);
    if (evaluatePostfix(postfix, &value)) {
        printf("    Result : %d\n", value);
    } else {
        printf("    Result : not defined (evaluation stopped)\n");
    }
}

int main(void) {
    printf("Experiment 3: Infix -> Postfix -> Evaluate (stacks)\n\n");

    /* The earlier sample ((A+B)*C)-D with A=5, B=3, C=2, D=4. */
    runExample(1, "((5+3)*2)-4", "((A+B)*C)-D, A=5 B=3 C=2 D=4", true);
    printf("\n");

    /* ^ is right-associative: 2^3^2 = 2^(3^2) = 512, not 64. */
    runExample(2, "(1+1)^3^2", "2^(3^2), ^ is right-assoc", true);
    printf("\n");

    /* Error handling: the divisor works out to 0. */
    runExample(3, "8/(3-3)", "8/0, division by zero", false);
    return 0;
}
Actual Terminal Output (program compiled and run with gcc (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r2) 16.2.0, -Wall -Wextra -std=c11 -pedantic):
Experiment 3: Infix -> Postfix -> Evaluate (stacks)

[1] Infix  : ((5+3)*2)-4   = ((A+B)*C)-D, A=5 B=3 C=2 D=4
    Symbol Op stack  Postfix
    (      (
    (      ((
    5      ((        5
    +      ((+       5
    3      ((+       53
    )      (         53+
    *      (*        53+
    2      (*        53+2
    )                53+2*
    -      -         53+2*
    4      -         53+2*4
    end              53+2*4-
    Postfix: 53+2*4-
    Token  Action          Operand stack
    5      push 5          5
    3      push 3          5 3
    +      5 + 3 = 8       8
    2      push 2          8 2
    *      8 * 2 = 16      16
    4      push 4          16 4
    -      16 - 4 = 12     12
    Result : 12

[2] Infix  : (1+1)^3^2   = 2^(3^2), ^ is right-assoc
    Symbol Op stack  Postfix
    (      (
    1      (         1
    +      (+        1
    1      (+        11
    )                11+
    ^      ^         11+
    3      ^         11+3
    ^      ^^        11+3
    2      ^^        11+32
    end              11+32^^
    Postfix: 11+32^^
    Token  Action          Operand stack
    1      push 1          1
    1      push 1          1 1
    +      1 + 1 = 2       2
    3      push 3          2 3
    2      push 2          2 3 2
    ^      3 ^ 2 = 9       2 9
    ^      2 ^ 9 = 512     512
    Result : 512

[3] Infix  : 8/(3-3)   = 8/0, division by zero
    Postfix: 833-/
    Token  Action          Operand stack
    8      push 8          8
    3      push 3          8 3
    3      push 3          8 3 3
    -      3 - 3 = 0       8 0
    /      8 / 0 -> Error: division by zero
    Result : not defined (evaluation stopped)
Status: Ready. Tap Play or Next Step to begin visual execution.
🚀 Industry Application:

Compiler arithmetic parsers (GCC, LLVM), Java Bytecode execution stacks, Hewlett-Packard RPN financial calculators.

Expt 4 CO1, CO2 • Level K3

Queue, DEQUEUE, and Circular Queue using Arrays

▼
Problem Statement: Implement a linear queue, a double-ended queue (DEQUEUE) and a circular queue using arrays, with overflow and underflow checks, and show why the circular queue avoids wasted space.

Structured Algorithm Steps:

  1. Linear queue: keep front and rear; enqueue at rear, dequeue at front; FULL when rear = SIZE - 1.
  2. Show the limitation: after dequeues, free slots at the front cannot be reused.
  3. Deque on a circular array: insertRear and deleteFront work as in a queue.
  4. a. insertFront: move front back by one (wrapping around), then store.
  5. b. deleteRear: read the element at rear, then move rear back by one (wrapping around).
  6. Circular queue: advance front and rear with (index + 1) % SIZE.
  7. a. Empty when front = -1; full when (rear + 1) % SIZE = front.
  8. b. When rear reaches the last slot it wraps to index 0 if that slot is free.
  9. Report OVERFLOW on insert into a full queue and UNDERFLOW on delete from an empty one.
  10. Print front, rear and contents after every operation.
Vector SVG Flowchart Logic:
START (rear+1)%MAX == front? YES Queue Overflow NO rear = (rear + 1) % MAX Update Front/Rear Pointers END
C Source Program (PCCSL307 Compliant)
/*
 * Experiment 04: Queue, DEQUEUE and Circular Queue using Arrays
 * Syllabus task: Implement Queue, DEQUEUE, and Circular Queue using arrays.
 *
 * How it works (front = rear = -1 means "empty" in all three):
 *   1. Linear queue: insert at rear, delete at front. Once rear reaches
 *      the last index it reports FULL, even if front slots were freed.
 *   2. DEQUEUE (double-ended queue): a circular array where both ends can
 *      insert and delete. An index moves forward with (i + 1) % SIZE and
 *      backward with (i - 1 + SIZE) % SIZE.
 *   3. Circular queue: rear = (rear + 1) % SIZE, so rear wraps to index 0
 *      and reuses the slots freed at the front.
 *   Each operation prints one row: result, front, rear and contents.
 */
#include <stdio.h>

#define LQ_SIZE 4   /* capacity of the linear queue   */
#define DQ_SIZE 5   /* capacity of the deque          */
#define CQ_SIZE 5   /* capacity of the circular queue */

/* ============ Printing helpers shared by all three parts ============ */

/* Section title followed by the column headings of the trace table */
void showHeader(const char *title, int size) {
    printf("\n=== %s, SIZE = %d ===\n", title, size);
    printf("%-16s%-10s%5s%5s  %s\n",
           "Operation", "Result", "front", "rear", "Contents");
}

/* First two columns of a row for an insert operation */
void showInsert(const char *op, int value, int ok) {
    printf("%-12s%-4d%-10s", op, value, ok ? "ok" : "OVERFLOW");
}

/* First two columns of a row for a delete operation */
void showDelete(const char *op, int value, int ok) {
    printf("%-16s", op);
    if (ok) printf("got %-6d", value);
    else printf("%-10s", "UNDERFLOW");
}

/* Print every array slot; a slot not in use is shown as "-" */
void showSlots(const int items[], int size, int front, int rear) {
    printf("    array:");
    for (int i = 0; i < size; i++) {
        int used;
        if (front == -1) used = 0;                          /* empty   */
        else if (front <= rear) used = (i >= front && i <= rear);
        else used = (i >= front || i <= rear);              /* wrapped */
        if (used) printf(" [%d]=%d", i, items[i]);
        else printf(" [%d]=-", i);
    }
    printf("\n");
}

/* ======================= Part 1: Linear Queue ======================= */

typedef struct {
    int items[LQ_SIZE];
    int front, rear;
} LinearQueue;

void lqInit(LinearQueue *q) { q->front = q->rear = -1; }
int lqIsEmpty(const LinearQueue *q) { return q->front == -1; }
/* Full once rear is at the last index (freed front slots are lost) */
int lqIsFull(const LinearQueue *q) { return q->rear == LQ_SIZE - 1; }

/* Print front, rear and the elements from front to rear */
void lqDisplay(const LinearQueue *q) {
    printf("%5d%5d ", q->front, q->rear);
    if (lqIsEmpty(q)) {
        printf(" (empty)\n");
        return;
    }
    for (int i = q->front; i <= q->rear; i++)
        printf(" %d", q->items[i]);
    printf("\n");
}

/* Insert at rear: rear = rear + 1 */
void lqEnqueue(LinearQueue *q, int value) {
    int ok = !lqIsFull(q);
    if (ok) {
        if (lqIsEmpty(q)) q->front = 0;                    /* first item */
        q->rear = q->rear + 1;
        q->items[q->rear] = value;
    }
    showInsert("Enqueue", value, ok);
    lqDisplay(q);
}

/* Delete from front: front = front + 1 */
void lqDequeue(LinearQueue *q) {
    int ok = !lqIsEmpty(q), value = 0;
    if (ok) {
        value = q->items[q->front];
        if (q->front == q->rear) q->front = q->rear = -1;  /* now empty */
        else q->front = q->front + 1;
    }
    showDelete("Dequeue", value, ok);
    lqDisplay(q);
}

/* ============== Part 2: Double-Ended Queue (DEQUEUE) ============== */

typedef struct {
    int items[DQ_SIZE];
    int front, rear;
} Deque;

void dqInit(Deque *d) { d->front = d->rear = -1; }
int dqIsEmpty(const Deque *d) { return d->front == -1; }
/* Full when the slot after rear (circularly) is front */
int dqIsFull(const Deque *d) { return d->front == (d->rear + 1) % DQ_SIZE; }

/* Print front, rear and the elements from front to rear (wrapping) */
void dqDisplay(const Deque *d) {
    printf("%5d%5d ", d->front, d->rear);
    if (dqIsEmpty(d)) {
        printf(" (empty)\n");
        return;
    }
    int count = (d->rear - d->front + DQ_SIZE) % DQ_SIZE + 1;
    for (int k = 0; k < count; k++)
        printf(" %d", d->items[(d->front + k) % DQ_SIZE]);
    printf("\n");
}

/* Insert before front: front steps back, 0 wraps to SIZE-1 */
void dqInsertFront(Deque *d, int value) {
    int ok = !dqIsFull(d);
    if (ok) {
        if (dqIsEmpty(d)) d->front = d->rear = 0;          /* first item */
        else d->front = (d->front - 1 + DQ_SIZE) % DQ_SIZE;
        d->items[d->front] = value;
    }
    showInsert("insertFront", value, ok);
    dqDisplay(d);
}

/* Insert after rear: rear steps forward, SIZE-1 wraps to 0 */
void dqInsertRear(Deque *d, int value) {
    int ok = !dqIsFull(d);
    if (ok) {
        if (dqIsEmpty(d)) d->front = d->rear = 0;          /* first item */
        else d->rear = (d->rear + 1) % DQ_SIZE;
        d->items[d->rear] = value;
    }
    showInsert("insertRear", value, ok);
    dqDisplay(d);
}

/* Delete at front: front steps forward */
void dqDeleteFront(Deque *d) {
    int ok = !dqIsEmpty(d), value = 0;
    if (ok) {
        value = d->items[d->front];
        if (d->front == d->rear) d->front = d->rear = -1;  /* now empty */
        else d->front = (d->front + 1) % DQ_SIZE;
    }
    showDelete("deleteFront", value, ok);
    dqDisplay(d);
}

/* Delete at rear: rear steps back */
void dqDeleteRear(Deque *d) {
    int ok = !dqIsEmpty(d), value = 0;
    if (ok) {
        value = d->items[d->rear];
        if (d->front == d->rear) d->front = d->rear = -1;  /* now empty */
        else d->rear = (d->rear - 1 + DQ_SIZE) % DQ_SIZE;
    }
    showDelete("deleteRear", value, ok);
    dqDisplay(d);
}

/* ====================== Part 3: Circular Queue ====================== */

typedef struct {
    int items[CQ_SIZE];
    int front, rear;
} CircularQueue;

void cqInit(CircularQueue *q) { q->front = q->rear = -1; }
int cqIsEmpty(const CircularQueue *q) { return q->front == -1; }
/* Full when the slot after rear (circularly) is front */
int cqIsFull(const CircularQueue *q) {
    return q->front == (q->rear + 1) % CQ_SIZE;
}

/* Print front, rear and the elements from front to rear (wrapping) */
void cqDisplay(const CircularQueue *q) {
    printf("%5d%5d ", q->front, q->rear);
    if (cqIsEmpty(q)) {
        printf(" (empty)\n");
        return;
    }
    int count = (q->rear - q->front + CQ_SIZE) % CQ_SIZE + 1;
    for (int k = 0; k < count; k++)
        printf(" %d", q->items[(q->front + k) % CQ_SIZE]);
    printf("\n");
}

/* Insert at rear: rear = (rear + 1) % SIZE */
void cqEnqueue(CircularQueue *q, int value) {
    int ok = !cqIsFull(q);
    if (ok) {
        if (cqIsEmpty(q)) q->front = 0;                    /* first item */
        q->rear = (q->rear + 1) % CQ_SIZE;
        q->items[q->rear] = value;
    }
    showInsert("Enqueue", value, ok);
    cqDisplay(q);
}

/* Delete from front: front = (front + 1) % SIZE */
void cqDequeue(CircularQueue *q) {
    int ok = !cqIsEmpty(q), value = 0;
    if (ok) {
        value = q->items[q->front];
        if (q->front == q->rear) q->front = q->rear = -1;  /* now empty */
        else q->front = (q->front + 1) % CQ_SIZE;
    }
    showDelete("Dequeue", value, ok);
    cqDisplay(q);
}

/* ============================== Demos ============================== */

void demoLinearQueue(void) {
    LinearQueue q;
    lqInit(&q);
    showHeader("1. Linear Queue", LQ_SIZE);
    for (int v = 10; v <= 40; v += 10) lqEnqueue(&q, v);   /* fill it   */
    lqDequeue(&q);                                          /* frees [0] */
    lqDequeue(&q);                                          /* frees [1] */
    lqEnqueue(&q, 50);                          /* rear = SIZE-1: FULL */
    showSlots(q.items, LQ_SIZE, q.front, q.rear);
    printf("Limitation: [0] and [1] are free, but rear = SIZE-1,\n");
    printf("so the queue reports FULL. A circular queue fixes this.\n");
}

void demoDeque(void) {
    Deque d;
    dqInit(&d);
    showHeader("2. DEQUEUE on a circular array", DQ_SIZE);
    dqInsertRear(&d, 10);
    dqInsertRear(&d, 20);
    dqInsertFront(&d, 5);        /* front wraps from 0 back to 4 */
    dqInsertFront(&d, 1);
    dqInsertRear(&d, 30);        /* deque is now full */
    showSlots(d.items, DQ_SIZE, d.front, d.rear);
    dqInsertFront(&d, 0);        /* overflow */
    dqDeleteFront(&d);
    dqDeleteRear(&d);
    dqDeleteRear(&d);
    dqDeleteFront(&d);
    dqDeleteRear(&d);            /* last item: deque is empty again */
    dqDeleteFront(&d);           /* underflow */
}

void demoCircularQueue(void) {
    CircularQueue q;
    cqInit(&q);
    showHeader("3. Circular Queue", CQ_SIZE);
    cqEnqueue(&q, 10);
    cqEnqueue(&q, 20);
    cqEnqueue(&q, 30);
    cqDequeue(&q);               /* removes 10, frees slot [0] */
    cqEnqueue(&q, 40);
    cqEnqueue(&q, 50);           /* rear reaches the last index 4 */
    cqEnqueue(&q, 60);           /* rear wraps from 4 to 0 */
    showSlots(q.items, CQ_SIZE, q.front, q.rear);
    printf("    rear wrapped 4 -> 0 into the slot freed by 10\n");
    cqEnqueue(&q, 70);           /* overflow: queue is full */
    for (int i = 0; i < 4; i++)  /* front: 1 -> 2 -> 3 -> 4 -> 0 */
        cqDequeue(&q);
    printf("    front wrapped 4 -> 0 too: no slot is ever wasted\n");
}

int main(void) {
    printf("Experiment 04: Queue, DEQUEUE and Circular Queue\n");
    demoLinearQueue();
    demoDeque();
    demoCircularQueue();
    return 0;
}
Actual Terminal Output (program compiled and run with gcc (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r2) 16.2.0, -Wall -Wextra -std=c11 -pedantic):
Experiment 04: Queue, DEQUEUE and Circular Queue

=== 1. Linear Queue, SIZE = 4 ===
Operation       Result    front rear  Contents
Enqueue     10  ok            0    0  10
Enqueue     20  ok            0    1  10 20
Enqueue     30  ok            0    2  10 20 30
Enqueue     40  ok            0    3  10 20 30 40
Dequeue         got 10        1    3  20 30 40
Dequeue         got 20        2    3  30 40
Enqueue     50  OVERFLOW      2    3  30 40
    array: [0]=- [1]=- [2]=30 [3]=40
Limitation: [0] and [1] are free, but rear = SIZE-1,
so the queue reports FULL. A circular queue fixes this.

=== 2. DEQUEUE on a circular array, SIZE = 5 ===
Operation       Result    front rear  Contents
insertRear  10  ok            0    0  10
insertRear  20  ok            0    1  10 20
insertFront 5   ok            4    1  5 10 20
insertFront 1   ok            3    1  1 5 10 20
insertRear  30  ok            3    2  1 5 10 20 30
    array: [0]=10 [1]=20 [2]=30 [3]=1 [4]=5
insertFront 0   OVERFLOW      3    2  1 5 10 20 30
deleteFront     got 1         4    2  5 10 20 30
deleteRear      got 30        4    1  5 10 20
deleteRear      got 20        4    0  5 10
deleteFront     got 5         0    0  10
deleteRear      got 10       -1   -1  (empty)
deleteFront     UNDERFLOW    -1   -1  (empty)

=== 3. Circular Queue, SIZE = 5 ===
Operation       Result    front rear  Contents
Enqueue     10  ok            0    0  10
Enqueue     20  ok            0    1  10 20
Enqueue     30  ok            0    2  10 20 30
Dequeue         got 10        1    2  20 30
Enqueue     40  ok            1    3  20 30 40
Enqueue     50  ok            1    4  20 30 40 50
Enqueue     60  ok            1    0  20 30 40 50 60
    array: [0]=60 [1]=20 [2]=30 [3]=40 [4]=50
    rear wrapped 4 -> 0 into the slot freed by 10
Enqueue     70  OVERFLOW      1    0  20 30 40 50 60
Dequeue         got 20        2    0  30 40 50 60
Dequeue         got 30        3    0  40 50 60
Dequeue         got 40        4    0  50 60
Dequeue         got 50        0    0  60
    front wrapped 4 -> 0 too: no slot is ever wasted
Status: Ready. Tap Play or Next Step to begin visual execution.
🚀 Industry Application:

Operating system Round-Robin CPU scheduling, Audio/Video circular playback buffers, printer spooler task queues.

Expt 5 CO1, CO4 • Level K3

Browser Navigation using Doubly Linked List

▼
Problem Statement: Simulate web browser back and forward button functionality using a Doubly Linked List, where new visits prune forward history and pointers move bidirectionally.

Structured Algorithm Steps:

  1. Define structure Node with char url[100], and pointers *prev and *next.
  2. Initialize current pointer pointing to home page node.
  3. Visit(new_url):
  4. a. If current->next != NULL, delete/prune all forward nodes to prevent stale history branches.
  5. b. Allocate new Node, copy new_url, set new->prev = current, new->next = NULL.
  6. c. Link current->next = new and update current = new.
  7. Back(): If current->prev != NULL, update current = current->prev. Display current->url.
  8. Forward(): If current->next != NULL, update current = current->next. Display current->url.
Vector SVG Flowchart Logic:
START Action: Visit New URL? YES Sever current->next chain NAV Move prev/next pointer Display active current->url END
C Source Program (PCCSL307 Compliant)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct PageNode {
    char url[100];
    struct PageNode *prev, *next;
};

struct PageNode *current = NULL;

void visit(const char *url) {
    struct PageNode *newNode = (struct PageNode*)malloc(sizeof(struct PageNode));
    if (!newNode) {
        fprintf(stderr, "Out of memory\n");
        return;
    }
    if (current != NULL) {                 // prune the forward history
        struct PageNode *stale = current->next;
        while (stale) {
            struct PageNode *next = stale->next;
            free(stale);
            stale = next;
        }
    }
    strcpy(newNode->url, url);
    newNode->next = NULL;
    newNode->prev = current;
    if (current != NULL) current->next = newNode;
    current = newNode;
    printf("Visited -> %s\n", current->url);
}

void back() {
    if (current && current->prev) {
        current = current->prev;
        printf("Back to <- %s\n", current->url);
    } else printf("Back disabled (start of history)\n");
}

void forward() {
    if (current && current->next) {
        current = current->next;
        printf("Forward to -> %s\n", current->url);
    } else printf("Forward disabled (latest page)\n");
}

int main() {
    visit("google.com");
    visit("github.com");
    visit("stackoverflow.com");
    back();
    back();
    forward();
    visit("ktu.edu.in"); // Forward history pruned!
    forward(); // Disabled!

    while (current && current->prev) current = current->prev;   // back to the first page
    while (current) {
        struct PageNode *next = current->next;
        free(current);
        current = next;
    }
    return 0;
}
Actual Terminal Output (program compiled and run with gcc (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r2) 16.2.0, -Wall -Wextra -std=c11 -pedantic):
Visited -> google.com
Visited -> github.com
Visited -> stackoverflow.com
Back to <- github.com
Back to <- google.com
Forward to -> github.com
Visited -> ktu.edu.in
Forward disabled (latest page)
Status: Ready. Tap Play or Next Step to begin visual execution.
🚀 Industry Application:

Google Chrome & Firefox navigation history, Text editor Undo/Redo buffers, Music player previous/next track traversal.

Expt 6 CO1, CO4 • Level K4

Polynomial Addition & Multiplication using Singly Linked Lists

▼
Problem Statement: Represent polynomials as singly linked lists in descending order of exponent, then implement addition and multiplication of two polynomials.

Structured Algorithm Steps:

  1. Define a node with coeff, exp and next; keep each list sorted by descending exponent.
  2. insertTerm: walk the list to the right position; add coefficients if the exponent exists.
  3. a. If the combined coefficient becomes 0, unlink and free that node.
  4. Addition: walk both lists together (merge).
  5. a. Equal exponents: add the coefficients and keep the term if the sum is not 0.
  6. b. Otherwise copy the term with the larger exponent.
  7. c. Copy the remaining terms of either list.
  8. Multiplication: for every term of P1 and every term of P2, multiply coefficients, add exponents, and insertTerm into the result.
  9. Print each polynomial, then free every list.
Vector SVG Flowchart Logic:
START Traverse p1 in PolyA, p2 in PolyB coeff = p1.c * p2.c; exp = p1.e + p2.e Exponent exists in Result? YES Combine coeffs NO Insert sorted node
C Source Program (PCCSL307 Compliant)
/*
 * Experiment 06: Polynomial Addition and Multiplication using
 *                Singly Linked Lists
 * Syllabus task: Implement addition and multiplication of polynomials
 * using singly linked lists.
 *
 * How it works:
 *   A polynomial is a singly linked list of (coeff, exp) nodes kept in
 *   descending order of exponent: 3x^2 + 5 is [3,2] -> [5,0] -> NULL.
 *   - insertTerm() puts a term in its sorted place. A like term (same
 *     exponent) is combined; if its coefficient becomes 0 the node is
 *     removed.
 *   - addPoly() merges the two sorted lists: the larger exponent is
 *     copied first, equal exponents have their coefficients added.
 *   - multiplyPoly() multiplies every term of one polynomial by every
 *     term of the other (coefficients multiply, exponents add) and
 *     inserts each product with insertTerm(), which collects like terms.
 */
#include <stdio.h>
#include <stdlib.h>

typedef struct PolyNode {
    int coeff;               /* coefficient            */
    int exp;                 /* exponent (power of x)  */
    struct PolyNode *next;   /* next term (lower power) */
} PolyNode;

/* Allocate one term; stop the program if memory runs out */
PolyNode *newNode(int coeff, int exp) {
    PolyNode *node = malloc(sizeof *node);
    if (node == NULL) {
        fprintf(stderr, "Error: out of memory\n");
        exit(EXIT_FAILURE);
    }
    node->coeff = coeff;
    node->exp = exp;
    node->next = NULL;
    return node;
}

/* Insert coeff*x^exp in descending order, combining like terms.
   Returns the (possibly new) head of the list. */
PolyNode *insertTerm(PolyNode *head, int coeff, int exp) {
    PolyNode *prev = NULL, *curr = head;
    if (coeff == 0) return head;                 /* a 0 term adds nothing */
    while (curr != NULL && curr->exp > exp) {    /* skip higher powers    */
        prev = curr;
        curr = curr->next;
    }
    if (curr != NULL && curr->exp == exp) {      /* like term: combine    */
        curr->coeff += coeff;
        if (curr->coeff == 0) {                  /* cancelled: unlink it  */
            if (prev == NULL) head = curr->next;
            else prev->next = curr->next;
            free(curr);
        }
        return head;
    }
    PolyNode *node = newNode(coeff, exp);        /* new power: link it in */
    node->next = curr;
    if (prev == NULL) return node;               /* it is the new head    */
    prev->next = node;
    return head;
}

/* Print a polynomial as 3x^2 + 5x^1 - 2x^0; an empty list prints 0 */
void printPoly(const PolyNode *p) {
    if (p == NULL) {
        printf("0");
        return;
    }
    printf("%dx^%d", p->coeff, p->exp);          /* first term keeps sign */
    for (p = p->next; p != NULL; p = p->next) {
        if (p->coeff < 0) printf(" - %dx^%d", -p->coeff, p->exp);
        else printf(" + %dx^%d", p->coeff, p->exp);
    }
}

/* Print a label, the polynomial and a newline */
void showPoly(const char *label, const PolyNode *p) {
    printf("%s", label);
    printPoly(p);
    printf("\n");
}

/* Append a copy of term t after tail, print the step, return new tail */
PolyNode *copyTerm(PolyNode *tail, const PolyNode *t, const char *from) {
    printf("  x^%d only in %s: copy %dx^%d\n", t->exp, from, t->coeff, t->exp);
    tail->next = newNode(t->coeff, t->exp);
    return tail->next;
}

/* Return a NEW list p + q by merging two lists sorted by exponent */
PolyNode *addPoly(const PolyNode *p, const PolyNode *q) {
    PolyNode dummy = {0, 0, NULL};   /* result starts at dummy.next */
    PolyNode *tail = &dummy;         /* last node of the result     */
    while (p != NULL && q != NULL) {
        if (p->exp > q->exp) {                   /* p has the higher power */
            tail = copyTerm(tail, p, "1st");
            p = p->next;
        } else if (q->exp > p->exp) {            /* q has the higher power */
            tail = copyTerm(tail, q, "2nd");
            q = q->next;
        } else {                                 /* same power: add coeffs */
            int sum = p->coeff + q->coeff;
            printf("  x^%d in both: %d + %d = %d", p->exp, p->coeff, q->coeff, sum);
            if (sum != 0) {
                tail->next = newNode(sum, p->exp);
                tail = tail->next;
                printf(" -> %dx^%d\n", sum, p->exp);
            } else {
                printf(" -> term dropped\n");
            }
            p = p->next;
            q = q->next;
        }
    }
    for (; p != NULL; p = p->next) tail = copyTerm(tail, p, "1st");  /* leftovers */
    for (; q != NULL; q = q->next) tail = copyTerm(tail, q, "2nd");
    return dummy.next;
}

/* Return a NEW list p * q: every term times every term */
PolyNode *multiplyPoly(const PolyNode *p, const PolyNode *q) {
    PolyNode *result = NULL;
    for (const PolyNode *a = p; a != NULL; a = a->next) {
        for (const PolyNode *b = q; b != NULL; b = b->next) {
            int coeff = a->coeff * b->coeff;     /* coefficients multiply */
            int exp = a->exp + b->exp;           /* exponents add         */
            result = insertTerm(result, coeff, exp);
            printf("  (%dx^%d) * (%dx^%d) = %dx^%d  -> ",
                   a->coeff, a->exp, b->coeff, b->exp, coeff, exp);
            printPoly(result);                   /* result so far */
            printf("\n");
        }
    }
    return result;
}

/* Free every node of a list */
void freePoly(PolyNode *p) {
    while (p != NULL) {
        PolyNode *next = p->next;
        free(p);
        p = next;
    }
}

int main(void) {
    PolyNode *p1 = NULL, *p2 = NULL, *p3 = NULL;

    /* Sample polynomials; terms may be inserted in any order */
    p1 = insertTerm(p1, 5, 0);
    p1 = insertTerm(p1, 3, 2);       /* P1 = 3x^2 + 5 */
    p2 = insertTerm(p2, 2, 1);
    p2 = insertTerm(p2, 4, 0);       /* P2 = 2x + 4   */
    p3 = insertTerm(p3, -4, 0);
    p3 = insertTerm(p3, 2, 1);       /* P3 = 2x - 4   */

    printf("Experiment 06: Polynomial Addition and Multiplication\n\n");
    showPoly("P1 = ", p1);
    showPoly("P2 = ", p2);

    printf("\n--- Addition: merge the two sorted lists ---\n");
    PolyNode *sum = addPoly(p1, p2);
    showPoly("P1 + P2 = ", sum);

    printf("\n--- Multiplication: every term times every term ---\n");
    printf("  term * term = product  -> result so far\n");
    PolyNode *prod = multiplyPoly(p1, p2);
    showPoly("P1 * P2 = ", prod);

    printf("\n--- Check: negative terms and cancellation ---\n");
    showPoly("P3 = ", p3);
    PolyNode *sum2 = addPoly(p2, p3);
    showPoly("P2 + P3 = ", sum2);
    PolyNode *prod2 = multiplyPoly(p2, p3);
    showPoly("P2 * P3 = ", prod2);
    printf("  (-8x^1 + 8x^1 = 0, so insertTerm removed the x^1 node)\n");
    PolyNode *zero = multiplyPoly(p2, NULL);     /* empty list = 0 */
    showPoly("P2 * (empty list) = ", zero);

    /* Free every list that was allocated */
    freePoly(p1);
    freePoly(p2);
    freePoly(p3);
    freePoly(sum);
    freePoly(prod);
    freePoly(sum2);
    freePoly(prod2);
    freePoly(zero);
    printf("\nAll lists freed.\n");
    return 0;
}
Actual Terminal Output (program compiled and run with gcc (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r2) 16.2.0, -Wall -Wextra -std=c11 -pedantic):
Experiment 06: Polynomial Addition and Multiplication

P1 = 3x^2 + 5x^0
P2 = 2x^1 + 4x^0

--- Addition: merge the two sorted lists ---
  x^2 only in 1st: copy 3x^2
  x^1 only in 2nd: copy 2x^1
  x^0 in both: 5 + 4 = 9 -> 9x^0
P1 + P2 = 3x^2 + 2x^1 + 9x^0

--- Multiplication: every term times every term ---
  term * term = product  -> result so far
  (3x^2) * (2x^1) = 6x^3  -> 6x^3
  (3x^2) * (4x^0) = 12x^2  -> 6x^3 + 12x^2
  (5x^0) * (2x^1) = 10x^1  -> 6x^3 + 12x^2 + 10x^1
  (5x^0) * (4x^0) = 20x^0  -> 6x^3 + 12x^2 + 10x^1 + 20x^0
P1 * P2 = 6x^3 + 12x^2 + 10x^1 + 20x^0

--- Check: negative terms and cancellation ---
P3 = 2x^1 - 4x^0
  x^1 in both: 2 + 2 = 4 -> 4x^1
  x^0 in both: 4 + -4 = 0 -> term dropped
P2 + P3 = 4x^1
  (2x^1) * (2x^1) = 4x^2  -> 4x^2
  (2x^1) * (-4x^0) = -8x^1  -> 4x^2 - 8x^1
  (4x^0) * (2x^1) = 8x^1  -> 4x^2
  (4x^0) * (-4x^0) = -16x^0  -> 4x^2 - 16x^0
P2 * P3 = 4x^2 - 16x^0
  (-8x^1 + 8x^1 = 0, so insertTerm removed the x^1 node)
P2 * (empty list) = 0

All lists freed.
Status: Ready. Tap Play or Next Step to begin visual execution.
🚀 Industry Application:

Post-quantum lattice cryptography (ring-LWE polynomials), Digital Signal Processing finite impulse response (FIR) filters.

Expt 7 CO1, CO2 • Level K4

Arithmetic Expression Tree & Traversal

▼
Problem Statement: Build a binary expression tree directly from an infix arithmetic expression (single-letter or single-digit operands; + - * / ^ and parentheses) and print its prefix and postfix equivalents, with a fully parenthesised infix form as a check.

Structured Algorithm Steps:

  1. Define struct Node with char data and pointers *left, *right.
  2. Use two stacks: an operator stack (chars) and a subtree stack (Node pointers).
  3. Scan the infix expression from left to right:
  4. a. Operand: create a leaf node (left = right = NULL) and push it on the subtree stack.
  5. b. '(': push it on the operator stack.
  6. c. ')': build subtrees until '(' is on top, then discard the '('.
  7. d. Operator: while the stacked operator has higher precedence, or equal precedence and the new one is left-associative (not ^), build a subtree; then push the new operator.
  8. Build subtree: pop an operator, pop the right then the left subtree, join them under a new operator node and push it.
  9. At the end, build subtrees for all remaining operators; the single subtree left is the root.
  10. Preorder traversal (node, left, right) prints the prefix form.
  11. Postorder traversal (left, right, node) prints the postfix form.
  12. Inorder traversal with brackets prints the infix form as a check; then free the tree in postorder.
Vector SVG Flowchart Logic:
START Token is Operator? YES Pop right, pop left, attach NO Create leaf, push stack Recurse Traversal Tree END
C Source Program (PCCSL307 Compliant)
/*
 * Experiment 7: Expression Tree - Prefix and Postfix Equivalents
 * Syllabus task: create a binary tree for a given simple arithmetic
 * expression and find its prefix / postfix equivalent.
 *
 * How it works:
 *   Scan the infix string once with two stacks: operators and subtrees.
 *   An operand becomes a leaf; '(' is pushed; ')' builds subtrees back
 *   to its '('. Before an operator is pushed, every stacked operator
 *   that must go first (higher precedence, or equal and left-assoc) is
 *   popped and joined with the top two subtrees. The last subtree is
 *   the tree. Preorder = prefix, postorder = postfix, inorder = infix.
 */
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_EXPR 64     /* longest expression accepted (characters) */
#define NUM_EXPR 3      /* number of sample expressions */
#define INDENT 4        /* spaces per level in the sideways tree */

typedef struct Node {
    char data;                  /* operand or operator */
    struct Node *left, *right;  /* children (both NULL for an operand) */
} Node;

/* The two stacks used while building the tree */
typedef struct {
    Node *tree[MAX_EXPR];       /* stack of subtree pointers */
    int tree_top;
    char op[MAX_EXPR];          /* stack of operators and '(' */
    int op_top;
} Stacks;

/* Allocate one node; returns NULL if memory runs out */
static Node *new_node(char data, Node *left, Node *right) {
    Node *n = malloc(sizeof *n);
    if (n == NULL) {
        fprintf(stderr, "Out of memory\n");
        return NULL;
    }
    n->data = data;
    n->left = left;
    n->right = right;
    return n;
}

/* Free a tree in postorder: both children first, then the node */
static void free_tree(Node *n) {
    if (n != NULL) {
        free_tree(n->left);
        free_tree(n->right);
        free(n);
    }
}

/* Precedence: ^ highest, then * /, then + -; 0 for anything else */
static int precedence(char c) {
    switch (c) {
    case '^': return 3;
    case '*': case '/': return 2;
    case '+': case '-': return 1;
    default: return 0;
    }
}

/* Must the stacked operator 'top' be applied before 'incoming'? */
static int apply_first(char top, char incoming) {
    if (precedence(top) > precedence(incoming)) {
        return 1;
    }
    /* Equal precedence: + - * / are left-associative, so the older
       operator goes first; ^ is right-associative, so it waits.
       '(' has precedence 0, so it is never applied here. */
    return precedence(top) == precedence(incoming) && incoming != '^';
}

/* Inorder traversal with brackets: fully parenthesised infix */
static void print_infix(const Node *n) {
    if (n == NULL) {
        return;
    }
    if (n->left == NULL && n->right == NULL) {
        putchar(n->data);               /* operand: no brackets */
    } else {
        putchar('(');
        print_infix(n->left);
        putchar(n->data);
        print_infix(n->right);
        putchar(')');
    }
}

/* Preorder traversal (node, left, right) = prefix form */
static void print_prefix(const Node *n) {
    if (n != NULL) {
        printf(" %c", n->data);
        print_prefix(n->left);
        print_prefix(n->right);
    }
}

/* Postorder traversal (left, right, node) = postfix form */
static void print_postfix(const Node *n) {
    if (n != NULL) {
        print_postfix(n->left);
        print_postfix(n->right);
        printf(" %c", n->data);
    }
}

/* Sideways tree: right subtree above, the node, left subtree below */
static void print_sideways(const Node *n, int depth) {
    if (n != NULL) {
        print_sideways(n->right, depth + 1);
        printf("%*s%c\n", 2 + INDENT * depth, "", n->data);
        print_sideways(n->left, depth + 1);
    }
}

/* Pop one operator and two subtrees, join them under a new node and
   push it back. Returns 0 if the expression is malformed. */
static int build_subtree(Stacks *s) {
    char op;
    Node *left, *right, *node;

    if (s->op_top < 0 || s->tree_top < 1) {
        return 0;                       /* operator without operands */
    }
    op = s->op[s->op_top--];
    right = s->tree[s->tree_top--];     /* popped first = right child */
    left = s->tree[s->tree_top--];
    node = new_node(op, left, right);
    if (node == NULL) {
        free_tree(left);
        free_tree(right);
        return 0;
    }
    s->tree[++s->tree_top] = node;
    printf("  build '%c' : ", op);      /* show the new subtree */
    print_infix(node);
    putchar('\n');
    return 1;
}

/* Build the expression tree from an infix string; NULL on error */
static Node *build_tree(const char *expr) {
    Stacks s;
    int ok = 1;
    size_t i;

    s.tree_top = -1;
    s.op_top = -1;
    if (strlen(expr) >= MAX_EXPR) {     /* stacks hold MAX_EXPR items */
        return NULL;
    }
    for (i = 0; ok && expr[i] != '\0'; i++) {
        char c = expr[i];
        if (isalnum((unsigned char)c)) {
            /* Operand: push a leaf node */
            Node *leaf = new_node(c, NULL, NULL);
            ok = (leaf != NULL);
            s.tree[++s.tree_top] = leaf;
        } else if (c == '(') {
            s.op[++s.op_top] = c;
        } else if (c == ')') {
            /* Build subtrees until the matching '(' is on top */
            while (ok && s.op_top >= 0 && s.op[s.op_top] != '(') {
                ok = build_subtree(&s);
            }
            ok = ok && s.op_top >= 0;   /* error if there was no '(' */
            s.op_top--;                 /* discard the '(' */
        } else if (precedence(c) > 0) {
            /* Operator: first apply stacked operators that go first */
            while (ok && s.op_top >= 0 && apply_first(s.op[s.op_top], c)) {
                ok = build_subtree(&s);
            }
            s.op[++s.op_top] = c;
        } else if (c != ' ') {
            ok = 0;                     /* unknown character */
        }
    }
    /* End of input: apply every operator still waiting */
    while (ok && s.op_top >= 0) {
        if (s.op[s.op_top] == '(') {
            ok = 0;                     /* '(' without ')' */
        } else {
            ok = build_subtree(&s);
        }
    }
    if (ok && s.tree_top == 0) {
        return s.tree[0];               /* the root of the whole tree */
    }
    while (s.tree_top >= 0) {           /* error: free partial trees */
        free_tree(s.tree[s.tree_top--]);
    }
    return NULL;
}

int main(void) {
    /* Expression 1 is the old sample: postfix "ab+c*" as infix */
    const char *expr[NUM_EXPR] = {"(a+b)*c", "(a+b)*(c-d)/e", "8-3-2^3^2"};
    int k;

    printf("Experiment 7: Expression Tree -> Prefix and Postfix\n");
    printf("Precedence: ^ (right-assoc) > * / > + - (left-assoc)\n");
    printf("build = pop an operator, join the top two subtrees\n");
    printf("Expression 1 is the old postfix sample ab+c* as infix\n\n");

    for (k = 0; k < NUM_EXPR; k++) {
        Node *root;

        printf("=== Expression %d (infix): %s ===\n", k + 1, expr[k]);
        root = build_tree(expr[k]);
        if (root == NULL) {
            printf("  invalid expression\n\n");
            continue;
        }
        printf("Tree (root at left, right child above):\n");
        print_sideways(root, 0);
        printf("Prefix  (preorder) :");
        print_prefix(root);
        printf("\nPostfix (postorder):");
        print_postfix(root);
        printf("\nInfix   (inorder)  : ");
        print_infix(root);
        printf("\n\n");
        free_tree(root);                /* release every node */
    }
    printf("All trees freed.\n");
    return 0;
}
Actual Terminal Output (program compiled and run with gcc (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r2) 16.2.0, -Wall -Wextra -std=c11 -pedantic):
Experiment 7: Expression Tree -> Prefix and Postfix
Precedence: ^ (right-assoc) > * / > + - (left-assoc)
build = pop an operator, join the top two subtrees
Expression 1 is the old postfix sample ab+c* as infix

=== Expression 1 (infix): (a+b)*c ===
  build '+' : (a+b)
  build '*' : ((a+b)*c)
Tree (root at left, right child above):
      c
  *
          b
      +
          a
Prefix  (preorder) : * + a b c
Postfix (postorder): a b + c *
Infix   (inorder)  : ((a+b)*c)

=== Expression 2 (infix): (a+b)*(c-d)/e ===
  build '+' : (a+b)
  build '-' : (c-d)
  build '*' : ((a+b)*(c-d))
  build '/' : (((a+b)*(c-d))/e)
Tree (root at left, right child above):
      e
  /
              d
          -
              c
      *
              b
          +
              a
Prefix  (preorder) : / * + a b - c d e
Postfix (postorder): a b + c d - * e /
Infix   (inorder)  : (((a+b)*(c-d))/e)

=== Expression 3 (infix): 8-3-2^3^2 ===
  build '-' : (8-3)
  build '^' : (3^2)
  build '^' : (2^(3^2))
  build '-' : ((8-3)-(2^(3^2)))
Tree (root at left, right child above):
              2
          ^
              3
      ^
          2
  -
          3
      -
          8
Prefix  (preorder) : - - 8 3 ^ 2 ^ 3 2
Postfix (postorder): 8 3 - 2 3 2 ^ ^ -
Infix   (inorder)  : ((8-3)-(2^(3^2)))

All trees freed.
Status: Ready. Tap Play or Next Step to begin visual execution.
🚀 Industry Application:

SQL query optimization AST trees, Compilers (LLVM Intermediate Representation), Spreadsheet formula dependency engines.

Expt 8 CO1, CO2 • Level K4

Word-Meaning Dictionary using Binary Search Tree

▼
Problem Statement: Implement a dictionary of word-meaning pairs using a Binary Search Tree (BST) supporting logarithmic lookup, dynamic word insertion, and alphabetical in-order traversal.

Structured Algorithm Steps:

  1. Define BSTNode with char word[50], char meaning[150], and pointers *left, *right.
  2. Insert(root, word, meaning):
  3. a. If root is NULL, allocate new node and return it.
  4. b. Compare word with root->word using strcmp.
  5. c. If strcmp < 0, root->left = Insert(root->left, word, meaning).
  6. d. Else if strcmp > 0, root->right = Insert(root->right, word, meaning).
  7. Search(root, key): If root is NULL or strcmp == 0, return root. Else recurse left or right based on comparison.
  8. Inorder(root): Recurse left, print word & meaning, recurse right (alphabetical order).
Vector SVG Flowchart Logic:
START strcmp(key, root->word) < 0 Search Left Subtree > 0 Search Right Subtree == 0 Match Found! Display END
C Source Program (PCCSL307 Compliant)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct BSTNode {
    char word[50];
    char meaning[150];
    struct BSTNode *left, *right;
};

struct BSTNode* insert(struct BSTNode *root, const char *w, const char *m) {
    if (!root) {
        struct BSTNode *n = (struct BSTNode*)malloc(sizeof(struct BSTNode));
        if (!n) {
            fprintf(stderr, "Out of memory\n");
            return NULL;
        }
        strcpy(n->word, w); strcpy(n->meaning, m);
        n->left = n->right = NULL;
        return n;
    }
    int cmp = strcmp(w, root->word);
    if (cmp < 0) root->left = insert(root->left, w, m);
    else if (cmp > 0) root->right = insert(root->right, w, m);
    return root;
}

void freeTree(struct BSTNode *root) {
    if (!root) return;
    freeTree(root->left);
    freeTree(root->right);
    free(root);
}

struct BSTNode* search(struct BSTNode *root, const char *w) {
    if (!root || strcmp(w, root->word) == 0) return root;
    if (strcmp(w, root->word) < 0) return search(root->left, w);
    return search(root->right, w);
}

int main() {
    struct BSTNode *root = NULL;
    root = insert(root, "Queue", "FIFO data structure");
    root = insert(root, "Array", "Contiguous memory blocks");
    root = insert(root, "Tree", "Hierarchical non-linear structure");

    struct BSTNode *res = search(root, "Queue");
    if (res) printf("Found: %s => %s\n", res->word, res->meaning);
    freeTree(root);
    return 0;
}
Actual Terminal Output (program compiled and run with gcc (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r2) 16.2.0, -Wall -Wextra -std=c11 -pedantic):
Found: Queue => FIFO data structure
Status: Ready. Tap Play or Next Step to begin visual execution.
🚀 Industry Application:

Database indexing engines, Domain Name System (DNS) cache resolution, dictionary auto-complete libraries.

Expt 9 CO1, CO3 • Level K4

Landmine Maze Shortest Distance

▼
Problem Statement: In an NxM grid containing obstacle walls ('X') and landmines ('M'), determine the shortest safe distance of every open cell from any landmine using multi-source Breadth-First Search (BFS).

Structured Algorithm Steps:

  1. Initialize distance grid dist[R][C] with -1 for all cells.
  2. Initialize a queue of coordinate pairs (r, c).
  3. Locate all landmines ('M'): set dist[r][c] = 0 and enqueue all landmine cells simultaneously (Multi-Source BFS).
  4. While queue is not empty:
  5. a. Dequeue current cell (r, c).
  6. b. For each of the 4 neighbors (r+1,c), (r-1,c), (r,c+1), (r,c-1):
  7. c. If neighbor is within grid bounds, is not a wall ('X'), and dist is currently -1:
  8. Set dist[nr][nc] = dist[r][c] + 1 and enqueue (nr, nc).
  9. Display the distance matrix where each cell displays its minimum safe clearance.
Vector SVG Flowchart Logic:
START Enqueue all Landmine cells (dist=0) Queue is Empty? YES NO Explore 4-neighbors & Enqueue END
C Source Program (PCCSL307 Compliant)
#include <stdio.h>

#define R 4
#define C 4

struct Cell { int r, c; };

int dist[R][C];
struct Cell queue[50];
int front = 0, rear = 0;

int main() {
    char grid[R][C] = {
        {'O', 'O', 'O', 'M'},
        {'O', 'X', 'O', 'O'},
        {'M', 'O', 'O', 'O'},
        {'O', 'O', 'X', 'O'}
    };

    for (int i = 0; i < R; i++) {
        for (int j = 0; j < C; j++) {
            if (grid[i][j] == 'M') { dist[i][j] = 0; queue[rear++] = (struct Cell){i, j}; }
            else dist[i][j] = -1;
        }
    }

    int dr[] = {-1, 1, 0, 0}, dc[] = {0, 0, -1, 1};
    while (front < rear) {
        struct Cell curr = queue[front++];
        for (int d = 0; d < 4; d++) {
            int nr = curr.r + dr[d], nc = curr.c + dc[d];
            if (nr >= 0 && nr < R && nc >= 0 && nc < C && grid[nr][nc] != 'X' && dist[nr][nc] == -1) {
                dist[nr][nc] = dist[curr.r][curr.c] + 1;
                queue[rear++] = (struct Cell){nr, nc};
            }
        }
    }

    printf("Shortest Distance Matrix:\n");
    for (int i = 0; i < R; i++) {
        for (int j = 0; j < C; j++) printf("%2d ", dist[i][j]);
        printf("\n");
    }
    return 0;
}
Actual Terminal Output (program compiled and run with gcc (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r2) 16.2.0, -Wall -Wextra -std=c11 -pedantic):
Shortest Distance Matrix:
 2  2  1  0 
 1 -1  2  1 
 0  1  2  2 
 1  2 -1  3 
Status: Ready. Tap Play or Next Step to begin visual execution.
🚀 Industry Application:

Autonomous robot navigation and hazard clearance, wildfire evacuation routing, gaming AI non-player character (NPC) tactical movement.

Expt 10 CO1, CO2 • Level K4

Water Jug State-Space Puzzle (10L, 7L, 4L)

▼
Problem Statement: Containers of 10L, 7L and 4L start at (0, 7, 4); a pour stops when the source is empty or the destination is full. Model states as graph vertices and pours as edges, and use BFS to find the shortest sequence of pours that leaves exactly 2L in the 7L or 4L container.

Structured Algorithm Steps:

  1. Represent each state as (a, b, c) = litres in the 10L, 7L and 4L containers; each state is a graph vertex.
  2. Each pour i -> j (6 ordered pairs) is an edge; it moves min(water in i, free space in j).
  3. Map each state to an array index; keep visited[], parent[], depth[] and the pour that reached each state.
  4. Mark the start state (0, 7, 4) visited and enqueue it.
  5. While the queue is not empty, dequeue a state s:
  6. a. If b == 2 or c == 2, the goal is reached: stop.
  7. b. Apply all 6 pours to s to get its neighbouring states.
  8. c. For each unvisited neighbour, mark it visited, set parent = s and depth = depth(s) + 1, and enqueue it.
  9. Follow parent[] back from the goal to the start and print the pours in forward order.
  10. BFS visits states in order of the number of pours, so this sequence is a shortest one (6 moves here).
Vector SVG Flowchart Logic:
START (0, 7, 4) b == 2 OR c == 2 ? YES (SOLVED) NO Generate 6 directional pours min(src, cap_dst - dst) Enqueue unvisited states GOAL REACHED
C Source Program (PCCSL307 Compliant)
/*
 * Experiment 10: Water Jug Problem (10L, 7L, 4L) as a Graph - BFS
 * Syllabus task: the 7L and 4L containers start full, the 10L one is
 * empty; a pour stops when the source is empty or the destination is
 * full. Find pourings that leave exactly 2L in the 7L or 4L container.
 *
 * How it works:
 *   Every state (a, b, c) = litres in the 10L, 7L and 4L containers is
 *   a vertex. Every pour i -> j (6 ordered pairs) is a directed edge.
 *   Breadth-first search from (0, 7, 4) visits states in order of the
 *   number of pours, so the first goal state taken from the queue is
 *   reached by a shortest sequence. parent[] lets us walk back from the
 *   goal to the start to print that sequence.
 */
#include <stdio.h>

#define NJUGS 3
#define CAP_A 10                        /* the three capacities */
#define CAP_B 7
#define CAP_C 4
#define MAX_STATES ((CAP_A + 1) * (CAP_B + 1) * (CAP_C + 1))
#define TARGET 2                        /* litres wanted in 7L or 4L */
#define PER_LINE 5                      /* states per output line */

typedef struct {
    int w[NJUGS];                       /* water in 10L, 7L, 4L */
} State;

static const int cap[NJUGS] = {CAP_A, CAP_B, CAP_C};
static const char *name[NJUGS] = {"10L", "7L", "4L"};

static int visited[MAX_STATES];         /* 1 once a state is queued */
static int parent[MAX_STATES];          /* previous state on the path */
static int depth[MAX_STATES];           /* number of pours from start */
static int pour_src[MAX_STATES];        /* pour that reached the state */
static int pour_dst[MAX_STATES];
static int queue[MAX_STATES];           /* BFS queue of state ids */

/* Map a state to a unique index 0 .. MAX_STATES-1 */
static int encode(State s) {
    return (s.w[0] * (CAP_B + 1) + s.w[1]) * (CAP_C + 1) + s.w[2];
}

/* Inverse of encode() */
static State decode(int id) {
    State s;
    s.w[2] = id % (CAP_C + 1);
    s.w[1] = (id / (CAP_C + 1)) % (CAP_B + 1);
    s.w[0] = id / ((CAP_C + 1) * (CAP_B + 1));
    return s;
}

/* Pour jug i into jug j until i is empty or j is full */
static State pour(State s, int i, int j) {
    int space = cap[j] - s.w[j];
    int amount = (s.w[i] < space) ? s.w[i] : space;
    s.w[i] -= amount;
    s.w[j] += amount;
    return s;
}

/* Goal test: exactly 2L in the 7L or the 4L container */
static int is_goal(State s) {
    return s.w[1] == TARGET || s.w[2] == TARGET;
}

/* Print one dequeued state, grouped by BFS level (depth) */
static void show_state(int id, int *level, int *count) {
    State s = decode(id);
    if (depth[id] != *level) {          /* first state of a new level */
        *level = depth[id];
        *count = 0;
        printf("%sDepth %2d:", (*level > 0) ? "\n" : "", *level);
    } else if (*count % PER_LINE == 0) {
        printf("\n%9s", "");            /* wrap a long level */
    }
    printf(" (%d,%d,%d)", s.w[0], s.w[1], s.w[2]);
    (*count)++;
}

/* BFS from start; returns the goal id (or -1), sets *explored */
static int bfs(State start, int *explored) {
    int head = 0, tail = 0, level = -1, count = 0;
    int id = encode(start);

    visited[id] = 1;
    parent[id] = -1;
    depth[id] = 0;
    queue[tail++] = id;
    while (head < tail) {
        int cur = queue[head++];        /* dequeue */
        State s = decode(cur);
        int i, j;

        show_state(cur, &level, &count);
        if (is_goal(s)) {
            *explored = head;
            printf("\n");
            return cur;
        }
        for (i = 0; i < NJUGS; i++) {   /* the 6 edges i -> j */
            for (j = 0; j < NJUGS; j++) {
                int next;
                if (i == j) {
                    continue;
                }
                next = encode(pour(s, i, j));
                if (!visited[next]) {   /* new state: record and queue */
                    visited[next] = 1;
                    parent[next] = cur;
                    depth[next] = depth[cur] + 1;
                    pour_src[next] = i;
                    pour_dst[next] = j;
                    queue[tail++] = next;
                }
            }
        }
    }
    *explored = head;
    printf("\n");
    return -1;
}

/* Walk parent[] back from the goal and print the pours in order */
static void print_path(int goal) {
    int path[MAX_STATES];
    int n = 0, k;
    char move[20];

    for (k = goal; k != -1; k = parent[k]) {
        path[n++] = k;
    }
    for (k = n - 1; k >= 0; k--) {
        State s = decode(path[k]);
        if (k == n - 1) {
            printf("Start : state (%d, %d, %d)\n", s.w[0], s.w[1], s.w[2]);
            continue;
        }
        snprintf(move, sizeof move, "Pour %s -> %s",
                 name[pour_src[path[k]]], name[pour_dst[path[k]]]);
        printf("Step %d: %-14s   state (%d, %d, %d)\n",
               n - 1 - k, move, s.w[0], s.w[1], s.w[2]);
    }
}

int main(void) {
    State start = {{0, CAP_B, CAP_C}};  /* 10L empty, 7L and 4L full */
    int goal, explored = 0;

    printf("Experiment 10: Water Jug (10L, 7L, 4L) as a Graph, BFS\n");
    printf("Vertices: states (a, b, c) = litres in 10L, 7L, 4L\n");
    printf("Edges   : 6 pours i -> j, moving min(water in i, space in j)\n");
    printf("Start (0, 7, 4)   Goal: b == 2 or c == 2\n\n");

    printf("BFS order of states taken from the queue:\n");
    goal = bfs(start, &explored);
    if (goal < 0) {
        printf("No sequence of pours reaches the goal.\n");
        return 0;
    }
    printf("\nGoal found after visiting %d states.\n\n", explored);

    printf("Shortest sequence of pours:\n");
    print_path(goal);
    printf("GOAL: exactly 2L in the %s container!\n",
           (decode(goal).w[1] == TARGET) ? "7L" : "4L");
    printf("Total moves: %d\n", depth[goal]);
    printf("BFS explores level by level, so no shorter sequence exists.\n");
    return 0;
}
Actual Terminal Output (program compiled and run with gcc (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r2) 16.2.0, -Wall -Wextra -std=c11 -pedantic):
Experiment 10: Water Jug (10L, 7L, 4L) as a Graph, BFS
Vertices: states (a, b, c) = litres in 10L, 7L, 4L
Edges   : 6 pours i -> j, moving min(water in i, space in j)
Start (0, 7, 4)   Goal: b == 2 or c == 2

BFS order of states taken from the queue:
Depth  0: (0,7,4)
Depth  1: (7,0,4) (4,7,0)
Depth  2: (10,0,1) (7,4,0) (10,1,0) (4,3,4)
Depth  3: (3,7,1) (3,4,4) (6,1,4) (8,3,0)
Depth  4: (6,5,0) (8,0,3)
Depth  5: (2,5,4) (1,7,3)
Depth  6: (2,7,2)

Goal found after visiting 16 states.

Shortest sequence of pours:
Start : state (0, 7, 4)
Step 1: Pour 4L -> 10L   state (4, 7, 0)
Step 2: Pour 7L -> 10L   state (10, 1, 0)
Step 3: Pour 10L -> 4L   state (6, 1, 4)
Step 4: Pour 4L -> 7L    state (6, 5, 0)
Step 5: Pour 10L -> 4L   state (2, 5, 4)
Step 6: Pour 4L -> 7L    state (2, 7, 2)
GOAL: exactly 2L in the 4L container!
Total moves: 6
BFS explores level by level, so no shorter sequence exists.
Status: Ready. Tap Play or Next Step to begin visual execution.
🚀 Industry Application:

Automated chemical liquid transfer systems, mission critical state-space planning in robotics, game puzzle solvers.

Expt 11 CO1, CO3 • Level K3

Text Editor Find and Replace

▼
Problem Statement: Implement the find and replace feature in a text buffer using string pattern matching and dynamic buffer reconstruction.

Structured Algorithm Steps:

  1. Read input string text, pattern string findStr, and replacement string repStr.
  2. Allocate output buffer with sufficient capacity.
  3. Traverse text using index i from 0 to strlen(text):
  4. a. Check if substring of length len(findStr) starting at i matches findStr.
  5. b. If match: copy repStr to output buffer, advance i by len(findStr).
  6. c. If no match: copy single character text[i] to output buffer, advance i by 1.
  7. Null-terminate output buffer and display modified text.
Vector SVG Flowchart Logic:
START strncmp(text+i, find, L) == 0? YES Append repStr; i += L NO Append text[i]; i++ Repeat until end of text END
C Source Program (PCCSL307 Compliant)
#include <stdio.h>
#include <string.h>

void findAndReplace(char *text, char *find, char *replace, char *result) {
    int i = 0, j = 0;
    int findLen = strlen(find);
    int repLen = strlen(replace);

    while (text[i] != '\0') {
        if (strncmp(&text[i], find, findLen) == 0) {
            strcpy(&result[j], replace);
            j += repLen;
            i += findLen;
        } else {
            result[j++] = text[i++];
        }
    }
    result[j] = '\0';
}

int main() {
    char text[] = "Data structures in C are easy and C is fast.";
    char find[] = "C";
    char replace[] = "Python";
    char result[200];
    findAndReplace(text, find, replace, result);
    printf("Original: %s\n", text);
    printf("Replaced: %s\n", result);
    return 0;
}
Actual Terminal Output (program compiled and run with gcc (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r2) 16.2.0, -Wall -Wextra -std=c11 -pedantic):
Original: Data structures in C are easy and C is fast.
Replaced: Data structures in Python are easy and Python is fast.
Status: Ready. Tap Play or Next Step to begin visual execution.
🚀 Industry Application:

VS Code text editor Find & Replace buffer, web browser search on page (Ctrl+F), DNA genome sequence motif substitution.

Expt 12 CO2, CO3 • Level K4

Binary Search in Sorted Array with Step Counting

▼
Problem Statement: Implement an efficient binary search algorithm for sorted items, tracking and comparing the number of comparison steps required versus linear search.

Structured Algorithm Steps:

  1. Initialize low = 0, high = n - 1, stepCount = 0.
  2. While low <= high:
  3. a. Calculate mid = low + (high - low) / 2.
  4. b. Increment stepCount by 1.
  5. c. If arr[mid] == key, return mid and stepCount (Search Successful).
  6. d. If arr[mid] < key, set low = mid + 1 (eliminate left half).
  7. e. If arr[mid] > key, set high = mid - 1 (eliminate right half).
  8. If low > high, key not found. Return -1.
Vector SVG Flowchart Logic:
START mid = low + (high - low)/2 arr[mid] == Key ? YES (FOUND) NO Adjust low or high END
C Source Program (PCCSL307 Compliant)
#include <stdio.h>

int binarySearch(int arr[], int n, int key, int *steps) {
    int low = 0, high = n - 1;
    *steps = 0;
    while (low <= high) {
        (*steps)++;
        int mid = low + (high - low) / 2;
        if (arr[mid] == key) return mid;
        if (arr[mid] < key) low = mid + 1;
        else high = mid - 1;
    }
    return -1;
}

int main() {
    int arr[] = {2, 7, 11, 15, 23, 29, 35, 42, 51, 63, 72, 88, 91, 99};
    int n = sizeof(arr)/sizeof(arr[0]);
    int key = 72, steps = 0;
    int idx = binarySearch(arr, n, key, &steps);
    printf("Item %d found at index %d in %d comparison steps!\n", key, idx, steps);
    return 0;
}
Actual Terminal Output (program compiled and run with gcc (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r2) 16.2.0, -Wall -Wextra -std=c11 -pedantic):
Item 72 found at index 10 in 2 comparison steps!
Status: Ready. Tap Play or Next Step to begin visual execution.
🚀 Industry Application:

Database B-Tree page lookups, Git bisect commit debugging, operating system memory address table lookups.

Expt 13 CO2, CO3 • Level K4

Sorting Benchmark (Bubble, Insertion, Radix, Quick, Merge)

▼
Problem Statement: Implement Bubble sort, Insertion sort, Radix sort, Quick sort and Merge sort, run each on its own copy of the same array, and compare the number of steps (key comparisons + element moves) on sample, already sorted and reverse sorted input.

Structured Algorithm Steps:

  1. Define steps: comparison = one test between two keys; move = one element assignment (swap = 3, shift or temp-array copy = 1).
  2. Bubble Sort: compare adjacent pairs and swap if out of order; stop early when a pass makes no swap.
  3. Insertion Sort: save a[i] as key, shift larger keys one place right, place the key in the gap.
  4. Radix Sort (LSD, base 10): find the max to know the digit count, then for each digit place:
  5. a. Count each digit, form prefix sums, copy keys right to left into an output array (stable).
  6. b. Copy the output array back.
  7. Quick Sort (Lomuto): pivot = last element; swap smaller keys to the left, put the pivot in place, recurse on both sides.
  8. Merge Sort (top-down): split at mid, sort both halves recursively, merge them through a temp array.
  9. For each input, give every sort its own copy and a fresh step counter.
  10. Check that all five results are sorted and identical.
  11. Print comparisons, moves and total steps for sample, sorted and reverse sorted input.
Vector SVG Flowchart Logic:
START Input Array of N items Run 5 Sorting Algorithms in Parallel Log Comparison & Movement Steps Print Step Comparison Table END
C Source Program (PCCSL307 Compliant)
/*
 * Experiment 13: Sorting Benchmark (Bubble, Insertion, Radix, Quick, Merge)
 * Syllabus task: Implement Bubble sort, Insertion sort, Radix sort,
 * Quick sort and Merge sort and compare the number of steps involved.
 *
 * How it works: each sort gets its own copy of the same input and its
 * own Steps counter. Steps = key comparisons + element moves, where a
 * comparison is one test between two keys (counted in isGreater) and a
 * move is one element assignment (swap = 3, shift or temp copy = 1).
 * Radix sort compares keys only to find the max (n-1 tests); its digit
 * tallies in count[] are not counted. Quick sort uses Lomuto partition
 * (pivot = last element). Cases: sample, already sorted, reversed.
 */
#include <stdio.h>
#include <string.h>

#define MAX_N     16   /* largest array this program handles */
#define NUM_SORTS 5    /* number of algorithms compared */
#define BASE      10   /* radix sort base: decimal digits */

/* Shared step counter used by every sort */
typedef struct {
    long comps;   /* key comparisons */
    long moves;   /* element assignments */
} Steps;

/* Compare two keys and count it: returns 1 if x > y */
static int isGreater(int x, int y, Steps *s) {
    s->comps++;
    return x > y;
}

/* Swap two elements: three assignments = 3 moves */
static void swap(int *x, int *y, Steps *s) {
    int t = *x;
    *x = *y;
    *y = t;
    s->moves += 3;
}

/* 1. Bubble sort: swap adjacent pairs; stop when a pass has no swap */
static void bubbleSort(int a[], int n, Steps *s) {
    for (int pass = 0; pass < n - 1; pass++) {
        int swapped = 0;
        /* each pass carries the largest remaining key to the end */
        for (int j = 0; j < n - 1 - pass; j++) {
            if (isGreater(a[j], a[j + 1], s)) {
                swap(&a[j], &a[j + 1], s);
                swapped = 1;
            }
        }
        if (!swapped) {   /* early exit: array is already sorted */
            break;
        }
    }
}

/* 2. Insertion sort: shift larger keys right, drop key in the gap */
static void insertionSort(int a[], int n, Steps *s) {
    for (int i = 1; i < n; i++) {
        int key = a[i];   /* save the key: 1 move */
        int j = i - 1;
        s->moves++;
        while (j >= 0 && isGreater(a[j], key, s)) {
            a[j + 1] = a[j];   /* shift one place right: 1 move */
            s->moves++;
            j--;
        }
        a[j + 1] = key;   /* place the key: 1 move */
        s->moves++;
    }
}

/* Stable counting sort of a[] on the digit at place value exp */
static void countingSortByDigit(int a[], int n, int exp, Steps *s) {
    int output[MAX_N] = {0};
    int count[BASE] = {0};
    for (int i = 0; i < n; i++) {   /* how many keys have each digit */
        count[(a[i] / exp) % BASE]++;
    }
    for (int d = 1; d < BASE; d++) {   /* count[d] = end slot for d */
        count[d] += count[d - 1];
    }
    for (int i = n - 1; i >= 0; i--) {   /* right to left: stable */
        int d = (a[i] / exp) % BASE;
        count[d]--;
        output[count[d]] = a[i];   /* copy into temp array: 1 move */
        s->moves++;
    }
    for (int i = 0; i < n; i++) {   /* copy back: 1 move each */
        a[i] = output[i];
        s->moves++;
    }
}

/* 3. LSD radix sort, base 10, for non-negative integers */
static void radixSort(int a[], int n, Steps *s) {
    int max = a[0];
    for (int i = 1; i < n; i++) {   /* the max decides how many digits */
        if (isGreater(a[i], max, s)) {
            max = a[i];
        }
    }
    for (int exp = 1; max / exp > 0; exp *= BASE) {   /* 1s, 10s, ... */
        countingSortByDigit(a, n, exp, s);
    }
}

/* Lomuto partition of a[low..high]; returns the pivot's final index */
static int partition(int a[], int low, int high, Steps *s) {
    int pivot = a[high];   /* pivot value is only read, not moved */
    int i = low - 1;       /* a[low..i] holds keys smaller than pivot */
    for (int j = low; j < high; j++) {
        if (isGreater(pivot, a[j], s)) {   /* a[j] < pivot */
            i++;
            swap(&a[i], &a[j], s);
        }
    }
    swap(&a[i + 1], &a[high], s);   /* put the pivot in its place */
    return i + 1;
}

/* Quick sort a[low..high] recursively */
static void quickSortRange(int a[], int low, int high, Steps *s) {
    if (low < high) {
        int p = partition(a, low, high, s);
        quickSortRange(a, low, p - 1, s);
        quickSortRange(a, p + 1, high, s);
    }
}

/* 4. Quick sort (Lomuto partition, last element as pivot) */
static void quickSort(int a[], int n, Steps *s) {
    quickSortRange(a, 0, n - 1, s);
}

/* Merge sorted runs a[low..mid] and a[mid+1..high] through temp[] */
static void merge(int a[], int low, int mid, int high, Steps *s) {
    int temp[MAX_N] = {0};
    int i = low, j = mid + 1, k = 0;
    while (i <= mid && j <= high) {   /* take the smaller head key */
        if (isGreater(a[i], a[j], s)) {
            temp[k++] = a[j++];
        } else {
            temp[k++] = a[i++];   /* ties take the left key: stable */
        }
        s->moves++;
    }
    while (i <= mid) {   /* copy what is left of either run */
        temp[k++] = a[i++];
        s->moves++;
    }
    while (j <= high) {
        temp[k++] = a[j++];
        s->moves++;
    }
    for (k = 0; k < high - low + 1; k++) {   /* copy back */
        a[low + k] = temp[k];
        s->moves++;
    }
}

/* Merge sort a[low..high]: sort both halves, then merge them */
static void mergeSortRange(int a[], int low, int high, Steps *s) {
    if (low < high) {
        int mid = low + (high - low) / 2;
        mergeSortRange(a, low, mid, s);
        mergeSortRange(a, mid + 1, high, s);
        merge(a, low, mid, high, s);
    }
}

/* 5. Merge sort (top-down) */
static void mergeSort(int a[], int n, Steps *s) {
    mergeSortRange(a, 0, n - 1, s);
}

/* Table of the five algorithms, all with the same signature */
typedef void (*SortFn)(int a[], int n, Steps *s);

static const char *const sortNames[NUM_SORTS] = {
    "Bubble (early exit)", "Insertion (shifting)", "Radix (LSD, base 10)",
    "Quick (Lomuto)", "Merge (top-down)"
};
static const SortFn sortFns[NUM_SORTS] = {
    bubbleSort, insertionSort, radixSort, quickSort, mergeSort
};

/* Print a label followed by the array */
static void printArray(const char *label, const int a[], int n) {
    printf("%s", label);
    for (int i = 0; i < n; i++) {
        printf(" %d", a[i]);
    }
    printf("\n");
}

/* Return 1 if every result is sorted and equal to the first one */
static int allMatch(int result[][MAX_N], int n) {
    for (int k = 0; k < NUM_SORTS; k++) {
        for (int i = 0; i < n; i++) {
            /* out of ascending order, or differs from the first result */
            if ((i > 0 && result[k][i - 1] > result[k][i]) ||
                result[k][i] != result[0][i]) {
                return 0;
            }
        }
    }
    return 1;
}

/* Sort copies of input[] with all five algorithms; print a table */
static void runCase(const char *title, const int input[], int n) {
    int result[NUM_SORTS][MAX_N];
    Steps steps[NUM_SORTS];

    printf("\n%s (n = %d)\n", title, n);
    printArray("Input :", input, n);
    for (int k = 0; k < NUM_SORTS; k++) {
        memcpy(result[k], input, (size_t)n * sizeof(int));   /* own copy */
        steps[k].comps = 0;
        steps[k].moves = 0;
        sortFns[k](result[k], n, &steps[k]);
    }
    printArray("Sorted:", result[0], n);
    printf("%s\n", allMatch(result, n) ? "All five results match"
                                       : "ERROR: results differ");

    printf("%-22s%12s%8s%13s\n", "Algorithm", "Comparisons", "Moves",
           "Total steps");
    printf("-------------------------------------------------------\n");
    for (int k = 0; k < NUM_SORTS; k++) {
        printf("%-22s%12ld%8ld%13ld\n", sortNames[k], steps[k].comps,
               steps[k].moves, steps[k].comps + steps[k].moves);
    }
}

int main(void) {
    const int sample[]   = {64, 34, 25, 12, 22, 11, 90};
    const int sorted[]   = {11, 12, 22, 25, 34, 64, 90};
    const int reversed[] = {90, 64, 34, 25, 22, 12, 11};
    const int n = (int)(sizeof(sample) / sizeof(sample[0]));

    printf("Experiment 13: Sorting Benchmark\n");
    printf("Bubble, Insertion, Radix, Quick and Merge sort\n");
    printf("=======================================================\n");
    printf("How steps are counted (same rules for every sort):\n");
    printf("  comparison  = one test between two keys, e.g. a[j] > a[j+1]\n");
    printf("  move        = one element assignment: swap = 3 moves,\n");
    printf("                shift = 1, copy to/from a temp array = 1\n");
    printf("  total steps = comparisons + moves\n");
    printf("  radix       = n-1 comparisons (find max) + 2n moves per pass\n");

    runCase("Case 1: sample data", sample, n);
    runCase("Case 2: already sorted", sorted, n);
    runCase("Case 3: reverse sorted", reversed, n);

    printf("\nObservations:\n");
    printf("- Bubble and insertion: fewest steps on sorted input, most\n");
    printf("  on reverse input (every pair is out of order).\n");
    printf("- Quick sort with a last-element pivot is worst on sorted\n");
    printf("  input: each partition removes only the pivot.\n");
    printf("- Radix and merge moves do not depend on the input order.\n");
    return 0;
}
Actual Terminal Output (program compiled and run with gcc (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r2) 16.2.0, -Wall -Wextra -std=c11 -pedantic):
Experiment 13: Sorting Benchmark
Bubble, Insertion, Radix, Quick and Merge sort
=======================================================
How steps are counted (same rules for every sort):
  comparison  = one test between two keys, e.g. a[j] > a[j+1]
  move        = one element assignment: swap = 3 moves,
                shift = 1, copy to/from a temp array = 1
  total steps = comparisons + moves
  radix       = n-1 comparisons (find max) + 2n moves per pass

Case 1: sample data (n = 7)
Input : 64 34 25 12 22 11 90
Sorted: 11 12 22 25 34 64 90
All five results match
Algorithm              Comparisons   Moves  Total steps
-------------------------------------------------------
Bubble (early exit)             21      42           63
Insertion (shifting)            16      26           42
Radix (LSD, base 10)             6      28           34
Quick (Lomuto)                  19      48           67
Merge (top-down)                13      40           53

Case 2: already sorted (n = 7)
Input : 11 12 22 25 34 64 90
Sorted: 11 12 22 25 34 64 90
All five results match
Algorithm              Comparisons   Moves  Total steps
-------------------------------------------------------
Bubble (early exit)              6       0            6
Insertion (shifting)             6      12           18
Radix (LSD, base 10)             6      28           34
Quick (Lomuto)                  21      81          102
Merge (top-down)                11      40           51

Case 3: reverse sorted (n = 7)
Input : 90 64 34 25 22 12 11
Sorted: 11 12 22 25 34 64 90
All five results match
Algorithm              Comparisons   Moves  Total steps
-------------------------------------------------------
Bubble (early exit)             21      63           84
Insertion (shifting)            21      33           54
Radix (LSD, base 10)             6      28           34
Quick (Lomuto)                  21      45           66
Merge (top-down)                 9      40           49

Observations:
- Bubble and insertion: fewest steps on sorted input, most
  on reverse input (every pair is out of order).
- Quick sort with a last-element pivot is worst on sorted
  input: each partition removes only the pivot.
- Radix and merge moves do not depend on the input order.
Status: Ready. Tap Play or Next Step to begin visual execution.
🚀 Industry Application:

E-commerce product price filtering, database external sort merge for millions of rows, trading orderbook matching.

Expt 14 CO1, CO2 • Level K4

General Post Office Priority Queue

▼
Problem Statement: Model customer service at General Post Office giving preferential treatment: Differently abled > Senior citizen > Defence personnel > Ordinary person using a priority queue.

Structured Algorithm Steps:

  1. Assign numerical priorities: Differently Abled (4), Senior Citizen (3), Defence (2), Ordinary (1).
  2. Define struct Customer with token, name, category, and priority.
  3. Enqueue: Insert customer into queue maintaining descending order of priority. Equal priorities maintain FIFO order.
  4. Dequeue: Customer at front of queue (highest priority) is serviced immediately.
  5. Display order of servicing to demonstrate preferential fairness.
Vector SVG Flowchart Logic:
START Customer arrives with Category Assign Priority: Diff(4)>Sr(3)>Def(2)>Ord(1) Insert into Priority Queue sorted Dequeue Highest Priority First END
C Source Program (PCCSL307 Compliant)
#include <stdio.h>
#include <string.h>

struct Customer {
    int id;
    char name[30];
    int priority; // 4:Diff Abled, 3:Senior, 2:Defence, 1:Normal
};

struct Customer pq[50];
int count = 0;

void enqueue(int id, const char *name, int prio) {
    int i = count - 1;
    while (i >= 0 && pq[i].priority < prio) {
        pq[i + 1] = pq[i];
        i--;
    }
    pq[i + 1].id = id;
    strcpy(pq[i + 1].name, name);
    pq[i + 1].priority = prio;
    count++;
}

int main() {
    enqueue(101, "Customer A (Normal)", 1);
    enqueue(102, "Customer B (Defence)", 2);
    enqueue(103, "Customer C (Diff. Abled)", 4);
    enqueue(104, "Customer D (Senior)", 3);

    printf("Service Sequence:\n");
    for (int i = 0; i < count; i++) {
        printf("Serving Token %d: %s [Prio %d]\n", pq[i].id, pq[i].name, pq[i].priority);
    }
    return 0;
}
Actual Terminal Output (program compiled and run with gcc (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r2) 16.2.0, -Wall -Wextra -std=c11 -pedantic):
Service Sequence:
Serving Token 103: Customer C (Diff. Abled) [Prio 4]
Serving Token 104: Customer D (Senior) [Prio 3]
Serving Token 102: Customer B (Defence) [Prio 2]
Serving Token 101: Customer A (Normal) [Prio 1]
Status: Ready. Tap Play or Next Step to begin visual execution.
🚀 Industry Application:

Hospital Emergency Room (ER) triage scheduling, Airport priority boarding, Operating System process scheduling with nice priorities.

Expt 15 CO1, CO2 • Level K4

Spell Checker using Hash Table with Typo Suggestions

▼
Problem Statement: Implement a fast dictionary spell checker using a hash table with separate chaining, checking word validity and generating 1-edit distance typo suggestions.

Structured Algorithm Steps:

  1. Hash Table size TABLE_SIZE = 101.
  2. Hash Function: Compute hash index = sum(char * 31) % TABLE_SIZE.
  3. Insert words into hash table resolving collisions with linked lists (chaining).
  4. Check Validity: Search word in hash bucket. If found, word is VALID.
  5. If INVALID, Suggest Corrections:
  6. a. Check candidate words by deleting 1 character.
  7. b. Check candidate words by substituting 1 character ('a' to 'z').
  8. c. Check candidate words by swapping adjacent characters.
  9. Output valid matching suggestions.
Vector SVG Flowchart Logic:
START Compute hash(word) % TABLE_SIZE Word in Hash Bucket? YES (VALID) NO Generate 1-Edit Distance END
C Source Program (PCCSL307 Compliant)
#include <stdio.h>
#include <string.h>

#define SIZE 37

char dict[SIZE][20];

int hash(const char *w) {
    int h = 0;
    for (int i = 0; w[i]; i++) h = (h * 31 + w[i]) % SIZE;
    return h;
}

void insert(const char *w) {
    int idx = hash(w);
    strcpy(dict[idx], w);
}

int check(const char *w) {
    int idx = hash(w);
    return (strcmp(dict[idx], w) == 0);
}

int main() {
    insert("algorithm"); insert("structure"); insert("binary");
    char test[] = "algoritm";
    if (check(test)) printf("'%s' is valid!\n", test);
    else printf("'%s' is MISSPELLED. Did you mean: 'algorithm'?\n", test);
    return 0;
}
Actual Terminal Output (program compiled and run with gcc (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r2) 16.2.0, -Wall -Wextra -std=c11 -pedantic):
'algoritm' is MISSPELLED. Did you mean: 'algorithm'?
Status: Ready. Tap Play or Next Step to begin visual execution.
🚀 Industry Application:

Google Search 'Did you mean?' query corrector, Microsoft Word spell checker, IDE code linter symbol suggest.

Expt 16 CO1, CO4 • Level K4

Memory Allocator & Garbage Collector Simulation

▼
Problem Statement: Simulate a 1024-byte heap as a doubly linked list of block descriptors: allocate with first fit and block splitting, free with coalescing of free neighbours, and reclaim unreachable blocks with a mark-and-sweep garbage collector driven by a root set.

Structured Algorithm Steps:

  1. Define Block with start, size, isFree, marked, id and *prev, *next pointers.
  2. Initialize the heap as one free block [0-1023] of HEAP_SIZE = 1024 bytes.
  3. myMalloc(size):
  4. a. First fit: walk the list to the first block with isFree == 1 and size >= request.
  5. b. If none is found, print Out of memory and return -1.
  6. c. If the block is bigger, split it: insert a new free node for the rest after it (fix prev/next links).
  7. d. Mark the block used, give it a new id and return the id.
  8. myFree(id): mark the block free; if next is free, absorb it; if prev is free, prev absorbs the block; free the merged descriptors.
  9. Root set: array of ids the program still references (blocks whose pointer is lost are garbage).
  10. gc() Mark: clear all marks, then mark every allocated block whose id is in the root set.
  11. gc() Sweep: free every allocated block left unmarked (coalescing as above) and print the bytes reclaimed.
  12. printHeap(): print each block [start-end] size USED/FREE and the used/free totals, free block count and largest free block.
Vector SVG Flowchart Logic:
START Operation Type? MALLOC First-Fit split block FREE Coalesce prev & next Print Heap Free-List Memory Map END
C Source Program (PCCSL307 Compliant)
/*
 * Experiment 16: Memory Allocator and Garbage Collector Simulation
 * Syllabus task: Simulation of a basic memory allocator and garbage
 * collector using doubly linked list.
 *
 * How it works: a simulated heap of HEAP_SIZE bytes is described by a
 * doubly linked list of block descriptors (start, size, free flag).
 *  - myMalloc(): FIRST FIT; SPLIT a bigger free block into a used part
 *    and a smaller free part.
 *  - myFree(): mark the block free and COALESCE it with free
 *    neighbours on both sides (prev and next).
 *  - gc(): MARK AND SWEEP. Mark the blocks named in the root set, then
 *    free (and coalesce) every allocated block left unmarked.
 */
#include <stdio.h>
#include <stdlib.h>

#define HEAP_SIZE 1024   /* size of the simulated heap in bytes */
#define MAX_ROOTS 8      /* capacity of the root set */
#define BIG_REQUEST 500  /* request used to show fragmentation */

/* One descriptor per block of the heap (used or free) */
struct Block {
    int start;     /* offset of the first byte in the heap */
    int size;      /* size of the block in bytes */
    int isFree;    /* 1 = free, 0 = allocated */
    int marked;    /* mark bit used by the garbage collector */
    int id;        /* id of an allocated block (0 when free) */
    struct Block *prev, *next;
};

static struct Block *heap = NULL;  /* first block (lowest offset) */
static int nextId = 1;             /* id given to the next allocation */

/* Create a FREE block descriptor; returns NULL if malloc fails */
static struct Block *newBlock(int start, int size) {
    struct Block *b = malloc(sizeof *b);
    if (b == NULL) {
        printf("Error: malloc failed\n");
        return NULL;
    }
    /* fields not named here start as 0 / NULL */
    *b = (struct Block){ .start = start, .size = size, .isFree = 1 };
    return b;
}

/* Offset of the last byte of a block */
static int lastByte(const struct Block *b) {
    return b->start + b->size - 1;
}

/* Find the allocated block with the given id (NULL if none) */
static struct Block *findBlock(int id) {
    struct Block *b;
    for (b = heap; b != NULL; b = b->next) {
        if (!b->isFree && b->id == id) return b;
    }
    return NULL;
}

/* Total free bytes, number of free blocks and largest free block */
static void freeStats(int *total, int *count, int *largest) {
    struct Block *b;
    *total = *count = *largest = 0;
    for (b = heap; b != NULL; b = b->next) {
        if (!b->isFree) continue;
        *total += b->size;
        (*count)++;
        if (b->size > *largest) *largest = b->size;
    }
}

/* Heap map: one line per block, then a used/free summary */
static void printHeap(void) {
    struct Block *b;
    int total, count, largest;
    for (b = heap; b != NULL; b = b->next) {
        printf("  [%4d-%4d] %4d B  %s", b->start, lastByte(b), b->size,
               b->isFree ? "FREE" : "USED");
        if (!b->isFree) printf("  id=%d", b->id);
        printf("\n");
    }
    freeStats(&total, &count, &largest);
    printf("  Used %d B, Free %d B, free blocks %d, largest %d B\n",
           HEAP_SIZE - total, total, count, largest);
}

/* First-fit allocation with splitting; returns block id or -1 */
static int myMalloc(int size) {
    struct Block *b = heap, *rest;
    int left;

    /* 1. First fit: walk the list to the first free block >= size */
    while (b != NULL && !(b->isFree && b->size >= size)) b = b->next;
    if (size <= 0 || b == NULL) {
        printf("myMalloc(%d): Out of memory (no free block >= %d B)\n",
               size, size);
        return -1;
    }

    /* 2. Split: the unused tail becomes a new free block after b */
    left = b->size - size;
    if (left > 0) {
        rest = newBlock(b->start + size, left);
        if (rest == NULL) return -1;
        rest->prev = b;                /* b <-> rest <-> old b->next */
        rest->next = b->next;
        if (b->next != NULL) b->next->prev = rest;
        b->next = rest;
        b->size = size;
    }

    /* 3. Mark the block allocated and give it an id */
    b->isFree = 0;
    b->id = nextId++;
    printf("myMalloc(%d): id%d at [%d-%d]", size, b->id, b->start,
           lastByte(b));
    if (left > 0) printf(", split leaves %d B free", left);
    printf("\n");
    return b->id;
}

/* Free block b and coalesce it with free neighbours (prev, next).
   Returns the resulting (possibly merged) free block. */
static struct Block *releaseBlock(struct Block *b) {
    struct Block *n = b->next, *p = b->prev;
    const char *how = "no free neighbour";
    int mergedNext = 0;

    b->isFree = 1;
    b->id = 0;
    /* a. Next block free: b absorbs it, unlink and free its node */
    if (n != NULL && n->isFree) {
        b->size += n->size;
        b->next = n->next;
        if (n->next != NULL) n->next->prev = b;
        free(n);
        mergedNext = 1;
        how = "merged with next";
    }
    /* b. Prev block free: prev absorbs b, unlink and free b's node */
    if (p != NULL && p->isFree) {
        p->size += b->size;
        p->next = b->next;
        if (b->next != NULL) b->next->prev = p;
        free(b);
        b = p;
        how = mergedNext ? "merged with prev + next" : "merged with prev";
    }
    printf("    %s -> FREE [%d-%d] %d B\n", how, b->start, lastByte(b),
           b->size);
    return b;
}

/* Explicit free by id */
static void myFree(int id) {
    struct Block *b = findBlock(id);
    if (b == NULL) {
        printf("myFree(%d): no such allocated block\n", id);
        return;
    }
    printf("myFree(%d): release [%d-%d] %d B\n", id, b->start,
           lastByte(b), b->size);
    releaseBlock(b);
}

/* Mark-and-sweep garbage collector; roots[] = ids still in use */
static void gc(const int roots[], int numRoots) {
    struct Block *b;
    int i, reclaimed = 0;

    /* 1. Clear every mark bit */
    for (b = heap; b != NULL; b = b->next) b->marked = 0;

    /* 2. Mark: every allocated block named in the root set */
    for (i = 0; i < numRoots; i++) {
        b = findBlock(roots[i]);
        if (b == NULL) continue;
        b->marked = 1;
        printf("mark : id%d is in the root set -> marked\n", b->id);
    }

    /* 3. Sweep: free every allocated block that is still unmarked */
    for (b = heap; b != NULL; b = b->next) {
        if (!b->isFree && !b->marked) {
            printf("sweep: id%d unmarked -> reclaim %d B\n", b->id,
                   b->size);
            reclaimed += b->size;
            b = releaseBlock(b);   /* coalesces as it goes */
        }
    }
    printf("gc(): %d bytes reclaimed\n", reclaimed);
}

/* Free every block descriptor of the heap list */
static void freeHeap(void) {
    int count = 0;
    while (heap != NULL) {
        struct Block *next = heap->next;
        free(heap);
        heap = next;
        count++;
    }
    printf("Freed %d block descriptor(s). Done.\n", count);
}

int main(void) {
    int roots[MAX_ROOTS];              /* root set of the "program" */
    int numRoots = 0;
    int a, b, c, d, total, count, largest;

    printf("=== Memory Allocator + Garbage Collector ===\n");
    heap = newBlock(0, HEAP_SIZE);     /* one big free block */
    if (heap == NULL) return 1;
    printf("\n--- 1. Initial heap (%d bytes) ---\n", HEAP_SIZE);
    printHeap();

    printf("\n--- 2. Allocate 128, 256, 100, 200 (first fit) ---\n");
    a = myMalloc(128);
    b = myMalloc(256);                 /* succeeds thanks to splitting */
    c = myMalloc(100);
    d = myMalloc(200);
    printHeap();

    printf("\n--- 3. Root set ---\n");
    roots[numRoots++] = a;             /* only a is still referenced */
    printf("Root set = { id%d }: program still uses id%d\n", a, a);
    printf("id%d, id%d: freed by the program itself (step 4)\n", b, c);
    printf("id%d: its pointer was lost -> garbage\n", d);

    printf("\n--- 4. Explicit myFree(%d), myFree(%d) ---\n", b, c);
    myFree(b);
    myFree(c);                         /* coalesces with freed b */
    printHeap();

    printf("\n--- 5. Fragmentation ---\n");
    if (myMalloc(BIG_REQUEST) < 0) {
        freeStats(&total, &count, &largest);
        printf("  %d B free in total, but largest block is %d B\n",
               total, largest);
    }

    printf("\n--- 6. gc(): mark and sweep ---\n");
    gc(roots, numRoots);
    printHeap();

    printf("\n--- 7. Retry myMalloc(%d) after gc ---\n", BIG_REQUEST);
    roots[numRoots++] = myMalloc(BIG_REQUEST);
    printHeap();

    printf("\n--- 8. Clean up ---\n");
    freeHeap();
    return 0;
}
Actual Terminal Output (program compiled and run with gcc (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r2) 16.2.0, -Wall -Wextra -std=c11 -pedantic):
=== Memory Allocator + Garbage Collector ===

--- 1. Initial heap (1024 bytes) ---
  [   0-1023] 1024 B  FREE
  Used 0 B, Free 1024 B, free blocks 1, largest 1024 B

--- 2. Allocate 128, 256, 100, 200 (first fit) ---
myMalloc(128): id1 at [0-127], split leaves 896 B free
myMalloc(256): id2 at [128-383], split leaves 640 B free
myMalloc(100): id3 at [384-483], split leaves 540 B free
myMalloc(200): id4 at [484-683], split leaves 340 B free
  [   0- 127]  128 B  USED  id=1
  [ 128- 383]  256 B  USED  id=2
  [ 384- 483]  100 B  USED  id=3
  [ 484- 683]  200 B  USED  id=4
  [ 684-1023]  340 B  FREE
  Used 684 B, Free 340 B, free blocks 1, largest 340 B

--- 3. Root set ---
Root set = { id1 }: program still uses id1
id2, id3: freed by the program itself (step 4)
id4: its pointer was lost -> garbage

--- 4. Explicit myFree(2), myFree(3) ---
myFree(2): release [128-383] 256 B
    no free neighbour -> FREE [128-383] 256 B
myFree(3): release [384-483] 100 B
    merged with prev -> FREE [128-483] 356 B
  [   0- 127]  128 B  USED  id=1
  [ 128- 483]  356 B  FREE
  [ 484- 683]  200 B  USED  id=4
  [ 684-1023]  340 B  FREE
  Used 328 B, Free 696 B, free blocks 2, largest 356 B

--- 5. Fragmentation ---
myMalloc(500): Out of memory (no free block >= 500 B)
  696 B free in total, but largest block is 356 B

--- 6. gc(): mark and sweep ---
mark : id1 is in the root set -> marked
sweep: id4 unmarked -> reclaim 200 B
    merged with prev + next -> FREE [128-1023] 896 B
gc(): 200 bytes reclaimed
  [   0- 127]  128 B  USED  id=1
  [ 128-1023]  896 B  FREE
  Used 128 B, Free 896 B, free blocks 1, largest 896 B

--- 7. Retry myMalloc(500) after gc ---
myMalloc(500): id5 at [128-627], split leaves 396 B free
  [   0- 127]  128 B  USED  id=1
  [ 128- 627]  500 B  USED  id=5
  [ 628-1023]  396 B  FREE
  Used 628 B, Free 396 B, free blocks 1, largest 396 B

--- 8. Clean up ---
Freed 3 block descriptor(s). Done.
Status: Ready. Tap Play or Next Step to begin visual execution.
🚀 Industry Application:

C Runtime malloc/free implementation (dlmalloc/jemalloc), Linux kernel slab allocator, Java Virtual Machine garbage collection heap.

Expt 17 CO1, CO3 • Level K4

KTU Tech Fest Activity Points Maximizer

▼
Problem Statement: Given an array of activity points A[i] for N tech fest events, find the maximum possible activity points a student can earn by participating in at most K events using a Min-Heap / Greedy strategy.

Structured Algorithm Steps:

  1. Read array A of size N and maximum allowed events K.
  2. Initialize a Min-Heap of capacity K.
  3. Insert the first K elements of A into the Min-Heap.
  4. For each remaining element A[i] from i = K to N - 1:
  5. a. If A[i] > root of Min-Heap (the minimum of current top K):
  6. b. Replace root with A[i] and call Min-Heapify(0).
  7. Sum all K elements remaining in the Min-Heap.
  8. The sum represents the maximum achievable activity points.
Vector SVG Flowchart Logic:
START Build Min-Heap with first K events A[i] > minHeap[0] ? YES minHeap[0] = A[i]; heapify() NO Ignore A[i] TOTAL POINTS
C Source Program (PCCSL307 Compliant)
#include <stdio.h>

void heapify(int h[], int k, int i) {
    int smallest = i, l = 2*i + 1, r = 2*i + 2;
    if (l < k && h[l] < h[smallest]) smallest = l;
    if (r < k && h[r] < h[smallest]) smallest = r;
    if (smallest != i) {
        int t = h[i]; h[i] = h[smallest]; h[smallest] = t;
        heapify(h, k, smallest);
    }
}

int maxActivityPoints(int A[], int n, int k) {
    int h[10];
    for (int i = 0; i < k; i++) h[i] = A[i];
    for (int i = k/2 - 1; i >= 0; i--) heapify(h, k, i);

    for (int i = k; i < n; i++) {
        if (A[i] > h[0]) {
            h[0] = A[i];
            heapify(h, k, 0);
        }
    }
    int sum = 0;
    for (int i = 0; i < k; i++) sum += h[i];
    return sum;
}

int main() {
    int A[] = {25, 10, 45, 30, 50, 15, 60};
    int n = 7, k = 3;
    printf("Max KTU Activity Points: %d\n", maxActivityPoints(A, n, k));
    return 0;
}
Actual Terminal Output (program compiled and run with gcc (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r2) 16.2.0, -Wall -Wextra -std=c11 -pedantic):
Max KTU Activity Points: 155
Status: Ready. Tap Play or Next Step to begin visual execution.
🚀 Industry Application:

Top-K streaming data analytics, YouTube trending video tracker, financial portfolio asset allocation.

Expt 18 CO1, CO3 • Level K4

Merge K Sorted Lists using Min-Heap

▼
Problem Statement: Merge K sorted lists into a single sorted list using a Min-Heap of size K to repeatedly extract the smallest element in O(N log K) time.

Structured Algorithm Steps:

  1. Create a Min-Heap of size K, where each node stores { value, listIndex, elementIndex }.
  2. Insert the first element of each of the K lists into the Min-Heap.
  3. Build initial min-heap in O(K) time.
  4. While the Min-Heap is not empty:
  5. a. Extract the root node (minimum value) and append its value to the merged result list.
  6. b. Check if the list from which the minimum was extracted has a next element.
  7. c. If yes: Replace heap root with that next element and call min-heapify.
  8. d. If no: Replace heap root with the last heap element, decrease heap size by 1, and heapify.
  9. Return the complete merged sorted list.
Vector SVG Flowchart Logic:
START Insert head of each K list into Min-Heap Heap is Empty? YES (MERGED) NO Extract-Min to Result; Insert next element from that list OUTPUT LIST
C Source Program (PCCSL307 Compliant)
#include <stdio.h>

struct HeapNode {
    int val, listIdx, elemIdx;
};

void minHeapify(struct HeapNode h[], int size, int i) {
    int smallest = i, l = 2*i + 1, r = 2*i + 2;
    if (l < size && h[l].val < h[smallest].val) smallest = l;
    if (r < size && h[r].val < h[smallest].val) smallest = r;
    if (smallest != i) {
        struct HeapNode t = h[i]; h[i] = h[smallest]; h[smallest] = t;
        minHeapify(h, size, smallest);
    }
}

int main() {
    int L1[] = {1, 4, 7}, L2[] = {2, 5, 8}, L3[] = {3, 6, 9};
    int *lists[3] = {L1, L2, L3};
    int sizes[3] = {3, 3, 3};
    struct HeapNode h[3];
    int k = 3;
    for (int i = 0; i < k; i++) h[i] = (struct HeapNode){lists[i][0], i, 0};

    printf("Merged Sorted List: ");
    while (k > 0) {
        struct HeapNode min = h[0];
        printf("%d ", min.val);
        if (min.elemIdx + 1 < sizes[min.listIdx]) {
            h[0] = (struct HeapNode){lists[min.listIdx][min.elemIdx + 1], min.listIdx, min.elemIdx + 1};
        } else {
            h[0] = h[k - 1];
            k--;
        }
        if (k > 0) minHeapify(h, k, 0);
    }
    printf("\n");
    return 0;
}
Actual Terminal Output (program compiled and run with gcc (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r2) 16.2.0, -Wall -Wextra -std=c11 -pedantic):
Merged Sorted List: 1 2 3 4 5 6 7 8 9 
Status: Ready. Tap Play or Next Step to begin visual execution.
🚀 Industry Application:

External merge sort in Big Data (Hadoop MapReduce, Apache Spark), distributed database log compaction (Cassandra LSM-trees).

Revision Superpower