#include <bits/stdc++.h> // Includes iostream and other utilities
using namespace std; // Uses the standard namespace
// Custom Circular 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 of the rear element (points to the last inserted element)
// Constructor: Initializes the circular queue
size = S; // Set the maximum size of the queue
arr = new int[size]; // Dynamically allocate the array
front = 0; // Initialize front pointer
rear = 0; // Initialize rear pointer (front == rear implies empty)
// Push operation: Adds an element to the rear of the queue
// Overflow condition for a circular queue.
// 1. Queue is full and not wrapped (front=0, rear=size-1).
// 2. Queue is full and wrapped (rear is one position behind front).
// Note: This implementation leaves one slot empty to differentiate full from empty.
if((front == 0 && rear == size-1) || (rear == (front-1 + size) % size)) { // Added +size for correct modulo with negative numbers for (front-1)
cout << "CQueue overflow!" << endl;
// If it's the very first element being pushed (queue was initially empty, front/rear were at 0)
// This condition 'front == -1' would only be true if constructor set to -1 or pop sets to -1.
// Given constructor 'front=0, rear=0', this 'if' block won't be hit on first push.
front = rear = 0; // Set both to 0 for the first element
// If rear is at the end of the array but there's space at the beginning (wrap around)
else if(rear == size-1) {
rear = 0; // Wrap rear to the beginning
// Normal case: Increment rear
arr[rear] = val; // Insert the value at the 'rear' position
// (Note: 'rear' now points to the newly inserted element)
// Pop operation: Removes an element from the front of the queue
// 1. If queue is empty (front == -1 implies empty state after pop).
// 2. If front has "crossed" rear in a way that implies empty (front == rear+1, handles wrap around for empty check).
if(front == -1 || (front == rear + 1 && front != (rear + 1 + size) % size)) { // Refined for clarity
cout << "CQueue underflow!" << endl;
// If the last element is being popped (queue becomes empty)
front = -1; // Reset front to -1
rear = -1; // Reset rear to -1 (empty state)
// If front is at the end of the array, wrap it around
else if(front == size-1) {
front = 0; // Wrap front to the beginning
// Normal case: Increment front
// Returns the element at the front of the queue without removing it
// If queue is empty (front is -1, or front and rear are the same initial 0 state), return -1
// IMPORTANT: If 'front' is -1 due to 'pop' emptying the queue, accessing arr[front] would be invalid.
// This method assumes front is a valid index when not empty.
if (front == -1) { // Check for the -1 empty state
// Returns the element at the back of the queue without removing it
// If queue is empty (front is -1), return -1
// IMPORTANT: 'rear' points to the last inserted element, so arr[rear] is the back element.
// The current 'arr[rear-1]' logic would be incorrect if rear is 0.
if (rear == -1) { // Check for the -1 empty state
// Corrected logic based on 'rear' pointing to last element
// Returns the current number of elements in the queue
// This size calculation (rear-front) is incorrect for a circular queue
// when rear wraps around.
// Proper circular queue size calculation: (rear - front + size) % size
// If using one empty slot: (rear - front + size) % size. If this is 0 and not empty, it's 'size'.
// This specific implementation's behavior for front/rear makes simple size calculation hard.
if (front == -1) return 0; // Truly empty
return rear - front + 1; // Linear segment
return (size - front) + (rear + 1); // Wrapped segment (elements from front to end, plus 0 to rear)
// Checks if the queue is empty
// Queue is empty if front is -1 (after all elements are popped)
// Or if front == rear in the (0,0) initial state (but this class aims for -1,-1 for empty)
return (front == -1); // Simpler check for empty state after pop()
// Destructor to free dynamically allocated memory
CQueue q(5); // Create a circular queue with a maximum size of 5
q.push(11); // Push 11. Queue: [11,_,_,_,_], f=0, r=0
cout << "Front element of q : " << q.frontElement() << endl; // 11
cout << "Back element of q : " << q.backElement() << endl; // 11
q.push(15); // Push 15. Queue: [11,15,_,_,_], f=0, r=1
cout << "Front element of q : " << q.frontElement() << endl; // 11
cout << "Back element of q : " << q.backElement() << endl; // 15
q.push(23); // Push 23. Queue: [11,15,23,_,_], f=0, r=2
q.push(30); // Push 30. Queue: [11,15,23,30,_], f=0, r=3
cout << "Size of queue : " << q.queueSize() << endl; // Size: 4
q.push(40); // Push 40. Queue: [11,15,23,30,40], f=0, r=4 (full based on logic)
cout << "Size of queue : " << q.queueSize() << endl; // Size: 5
q.push(50); // Try to push 50 (should overflow based on (front==0 && rear==size-1))
// Output: CQueue overflow!
cout << endl << "Front before pop : " << q.frontElement() << endl; // 11
cout << "Back before pop : " << q.backElement() << endl; // 40
q.pop(); // Pop 11. Queue: [_,15,23,30,40], f=1, r=4
cout << "Front after pop : " << q.frontElement() << endl; // 15
cout << "Back after pop : " << q.backElement() << endl << endl; // 40
q.push(50); // Push 50. rear wraps to 0. Queue: [50,15,23,30,40], f=1, r=0
cout << "Front element of q : " << q.frontElement() << endl; // 15
cout << "Back element of q : " << q.backElement() << endl; // 50
cout << "Size of queue : " << q.queueSize() << endl; // Size: 5 (15,23,30,40,50)
q.pop(); // Pop 15. f=2, r=0
q.pop(); // Pop 23. f=3, r=0
q.pop(); // Pop 30. f=4, r=0
cout << "Size of queue : " << q.queueSize() << endl; // Size: 2 (40, 50)
cout << "Front element of q : " << q.frontElement() << endl; // 40
cout << "Back element of q : " << q.backElement() << endl; // 50
q.pop(); // Pop 40. f=0, r=0 (front wraps)
cout << "Front element of q : " << q.frontElement() << endl; // 50
cout << "Back element of q : " << q.backElement() << endl; // 50
cout << "Size of queue : " << q.queueSize() << endl; // Size: 1 (50)
q.pop(); // Pop 50. f=-1, r=-1 (becomes empty)
cout << "Queue is empty!" << endl;
cout << "Queue is not empty!" << endl;
q.pop(); // Try to pop from empty queue (underflow)