#include <bits/stdc++.h> // Includes necessary headers like iostream
using namespace std; // Uses the standard namespace
// Custom Deque implementation using a fixed-size circular array
int *arr; // Pointer to the dynamic array
int size; // Maximum capacity of the deque
int front; // Index of the front element
int rear; // Index of the rear (back) element. Points to the last inserted item.
// Constructor: Initializes the Deque
size = S; // Set the maximum size
arr = new int[size]; // Allocate memory for the array
front = -1; // Initialize front to -1 (indicates empty deque)
rear = -1; // Initialize rear to -1 (indicates empty deque)
// Pushes an element to the front of the deque
void push_front(int val) {
// Overflow Condition (Deque is full)
// Case 1: front is at 0 and rear is at size-1 (linear full)
// Case 2: front is not 0, and rear is directly behind front (wrapped around full)
// (front-1 + size) % size ensures correct positive modulo for wrapping
if((front == 0 && rear == size-1) || (front != -1 && rear == (front - 1 + size) % size)) {
cout << "Deque Overflow!" << endl;
// Case 1: Deque is empty (first element being pushed)
front = rear = 0; // Set both to 0
// Case 2: front is at the beginning (0), but space is available at the end (size-1)
front = size-1; // Wrap front to the end of the array
// Case 3: Normal push_front: decrement front
arr[front] = val; // Insert the value at the calculated front position
// Pushes an element to the back of the deque
void push_back(int val) {
// Overflow Condition (Same as push_front for consistency)
if((front == 0 && rear == size-1) || (front != -1 && rear == (front - 1 + size) % size)) {
cout << "Deque overflow!" << endl;
// Case 1: Deque is empty (first element being pushed)
front = rear = 0; // Set both to 0
// Case 2: rear is at the end (size-1), but space is available at the beginning (0)
else if(rear == size-1) {
rear = 0; // Wrap rear to the beginning of the array
// Case 3: Normal push_back: increment rear
arr[rear] = val; // Insert the value at the calculated rear position
// Removes an element from the front of the deque
// Underflow Condition (Deque is empty)
cout << "Deque underflow!" << endl;
// Case 1: Only one element left in the deque (deque becomes empty after pop)
front = rear = -1; // Reset to empty state
// Case 2: front is at the end (size-1), so wrap it to 0
else if(front == size-1) {
front = 0; // Wrap front to the beginning
// Case 3: Normal pop_front: increment front
// Removes an element from the back of the deque
// Underflow Condition (Deque is empty)
cout << "Deque underflow!" << endl;
// Case 1: Only one element left in the deque (deque becomes empty after pop)
front = rear = -1; // Reset to empty state
// Case 2: rear is at the beginning (0), so wrap it to size-1
rear = size-1; // Wrap rear to the end
// Case 3: Normal pop_back: decrement rear
// Checks if the deque is empty
return (front == -1); // Deque is empty if front is -1
// Returns the element at the front of the deque without removing it
return (this->empty()) ? -1 : arr[front]; // Return -1 if empty, else value at front
// Returns the element at the back of the deque without removing it
// Corrected: 'rear' points to the last inserted element, so arr[rear] is the back element.
// The original arr[rear-1] would be incorrect, especially when rear is 0.
return (this->empty()) ? -1 : arr[rear]; // Return -1 if empty, else value at rear
// Returns the current number of elements in the deque
// This size calculation is incorrect for a circular array when elements wrap.
// It only works if elements are in a linear segment (front <= rear).
// Correct calculation for circular buffer:
if (front == -1) return 0; // Deque is truly empty
return rear - front + 1; // Linear segment: count from front to rear
// Wrapped segment: (elements from front to end) + (elements from 0 to rear)
return (size - front) + (rear + 1);
// Destructor to free dynamically allocated memory
Deque d(5); // Create a deque with a maximum size of 5
// Test push_front and push_back
d.push_front(11); // Deque: [11, _, _, _, _], f=0, r=0
cout << "Push front 11. Front: " << d.frontElement() << ", Back: " << d.backElement() << endl;
d.push_back(15); // Deque: [11, 15, _, _, _], f=0, r=1
cout << "Push back 15. Front: " << d.frontElement() << ", Back: " << d.backElement() << endl;
d.push_back(23); // Deque: [11, 15, 23, _, _], f=0, r=2
d.push_front(30); // Deque: [30, 11, 15, 23, _], f=4, r=2
cout << "Push back 23, push front 30. Front: " << d.frontElement() << ", Back: " << d.backElement() << endl;
cout << "Size of deque : " << d.queueSize() << endl; // Should be 4
// Test overflow (filling up)
d.push_front(40); // Deque: [30, 11, 15, 23, 40], f=3, r=2
cout << "Push front 40. Front: " << d.frontElement() << ", Back: " << d.backElement() << endl;
cout << "Size of deque : " << d.queueSize() << endl; // Should be 5
d.push_back(50); // This should cause an overflow, as deque is full
cout << "Attempt push back 50. Front: " << d.frontElement() << ", Back: " << d.backElement() << endl;
cout << endl << "Front before pop : " << d.frontElement() << endl; // 40
cout << "Back before pop : " << d.backElement() << endl; // 23
d.pop_front(); // Pop 40. Deque: [30, 11, 15, 23, _], f=4 (wraps to 0), r=2
cout << "Pop front. Front: " << d.frontElement() << ", Back: " << d.backElement() << endl; // Front: 30, Back: 23
d.pop_back(); // Pop 23. Deque: [30, 11, 15, _, _], f=4, r=1
d.pop_back(); // Pop 15. Deque: [30, 11, _, _, _], f=4, r=0
cout << "Pop back twice. Front: " << d.frontElement() << ", Back: " << d.backElement() << endl; // Front: 30, Back: 11
cout << "Size of deque : " << d.queueSize() << endl; // Should be 2 (30, 11)
d.pop_front(); // Pop 30. Deque: [_, 11, _, _, _], f=0 (wraps), r=0
cout << "Pop front. Front: " << d.frontElement() << ", Back: " << d.backElement() << endl; // Front: 11, Back: 11
d.pop_front(); // Pop 11. Deque: [_, _, _, _, _], f=-1, r=-1 (empty)
cout << "Deque is empty!" << endl;
cout << "Deque is not empty!" << endl;
d.pop_front(); // Try to pop from empty deque (underflow)