#include <bits/stdc++.h> // Includes iostream and other utilities
using namespace std; // Uses the standard namespace
// Node class: Defined but NOT USED in this specific Queue implementation.
// This class would typically be used for a linked list based queue.
// Custom Queue implementation using a fixed-size array
int *arr; // Pointer to the dynamic array that stores queue elements
int size; // Maximum capacity of the queue
int front; // Index of the front element
int rear; // Index where the next element will be inserted
// Constructor: Initializes the queue
size = S; // Set the maximum size of the queue
arr = new int[size]; // Dynamically allocate the array
front = 0; // Initialize front pointer to 0
rear = 0; // Initialize rear pointer to 0 (queue is empty when front == rear)
// Push operation: Adds an element to the rear of the queue
// Check for overflow: If rear has reached the end of the array (size)
if(rear == size) { // The condition should typically be rear == size for a fixed-size array where 'rear' points to next available
cout << "Queue overflow!" << endl; // Cannot add more elements
arr[rear] = val; // Insert the value at the rear position
rear++; // Increment rear to point to the next available spot
// Pop operation: Removes an element from the front of the queue
// Check for underflow: If front and rear are at the same position, queue is empty
cout << "Queue underflow!" << endl; // Cannot remove from empty queue
front++; // Increment front to logically remove the element
// If front catches up to rear, it means the queue has become empty
// Reset both front and rear to 0 to reuse the array space from start
// NOTE: This basic implementation is a linear queue and can still lead to space issues
// once 'rear' hits 'size' (or size-1 for 0-indexed capacity), even if 'front' has moved significantly.
// A circular queue addresses this by wrapping around indices.
// Returns the element at the front of the queue without removing it
// If queue is empty, return -1 (or throw an exception)
return (front == rear) ? -1 : arr[front];
// Returns the element at the back of the queue without removing it
// If queue is empty, return -1
// Else, return the element just before 'rear' (as rear points to the next available slot)
return (front == rear) ? -1 : arr[rear - 1];
// Returns the current number of elements in the queue
// Size is the difference between rear and front pointers
// Checks if the queue is empty
// Queue is empty if front and rear pointers are at the same position
return (front == rear) ? true : false;
// Destructor to free dynamically allocated memory
Queue q(5); // Create a queue with a maximum size of 5
q.push(11); // Add elements
cout << "Front element of q : " << q.frontElement() << endl; // 11
cout << "Back element of q : " << q.backElement() << endl; // 11
cout << "Front element of q : " << q.frontElement() << endl; // 11
cout << "Back element of q : " << q.backElement() << endl; // 15
q.push(40); // Queue is now [11, 15, 23, 30, 40], front=0, rear=5
cout << "Size of queue : " << q.queueSize() << endl; // Size: 5
q.push(50); // This will cause overflow, as rear == size (5 == 5)
cout << endl << "Front before pop : " << q.frontElement() << endl; // 11
cout << "Back before pop : " << q.backElement() << endl; // 40
q.pop(); // Remove 11. Queue: [_, 15, 23, 30, 40], front=1, rear=5
cout << "Front after pop : " << q.frontElement() << endl; // 15
cout << "Back after pop : " << q.backElement() << endl << endl; // 40
q.pop(); // Remove 15. Queue: [_, _, 23, 30, 40], front=2, rear=5
q.pop(); // Remove 23. Queue: [_, _, _, 30, 40], front=3, rear=5
cout << "Size of queue : " << q.queueSize() << endl; // Size: 2 (30, 40)
q.pop(); // Remove 30. Queue: [_,_,_,_,40], front=4, rear=5
q.pop(); // Remove 40. Queue: [_,_,_,_,_], front=5, rear=5. Then resets to front=0, rear=0
cout << "Size of queue : " << q.queueSize() << endl; // Size: 0 (after reset)
cout << "Queue is empty!" << endl;
cout << "Queue is not empty!" << endl;
q.pop(); // Try to pop from empty queue (underflow)