๐Ÿฆ€ Functional Rust

1032: VecDeque Rotation

Difficulty: Beginner Category: Data Structures Concept: Ring buffer deque with O(1) operations at both ends and built-in rotation Key Insight: Rust's `VecDeque` is a ring buffer with `rotate_left`/`rotate_right` built in โ€” OCaml has no stdlib equivalent and must use a two-list deque or manual rotation.
// 1032: VecDeque Rotation โ€” Efficient Front/Back Operations
// VecDeque is a ring buffer: O(1) push/pop at both ends

use std::collections::VecDeque;

/// Basic front/back operations
fn basic_deque() {
    let mut dq = VecDeque::new();
    dq.push_back(1);
    dq.push_back(2);
    dq.push_back(3);
    dq.push_front(0);

    assert_eq!(dq.pop_front(), Some(0));
    assert_eq!(dq.pop_front(), Some(1));
    assert_eq!(dq.pop_back(), Some(3));
    assert_eq!(dq.pop_back(), Some(2));
    assert!(dq.is_empty());
}

/// Rotation using VecDeque::rotate_left/rotate_right
fn rotation() {
    let mut dq: VecDeque<_> = [1, 2, 3, 4, 5].into_iter().collect();

    // Rotate left by 2: [3, 4, 5, 1, 2]
    dq.rotate_left(2);
    assert_eq!(dq, [3, 4, 5, 1, 2].into_iter().collect::<VecDeque<_>>());

    // Rotate right by 2: back to [1, 2, 3, 4, 5]
    dq.rotate_right(2);
    assert_eq!(dq, [1, 2, 3, 4, 5].into_iter().collect::<VecDeque<_>>());
}

/// Using VecDeque as a sliding window
fn sliding_window() {
    let data = vec![1, 2, 3, 4, 5, 6, 7];
    let window_size = 3;
    let mut window: VecDeque<i32> = VecDeque::with_capacity(window_size);
    let mut sums = Vec::new();

    for &val in &data {
        window.push_back(val);
        if window.len() > window_size {
            window.pop_front();
        }
        if window.len() == window_size {
            let sum: i32 = window.iter().sum();
            sums.push(sum);
        }
    }

    assert_eq!(sums, vec![6, 9, 12, 15, 18]); // 1+2+3, 2+3+4, ...
}

/// VecDeque to Vec and back
fn conversions() {
    let v = vec![1, 2, 3, 4, 5];
    let mut dq: VecDeque<_> = v.into_iter().collect();

    dq.push_front(0);
    // make_contiguous ensures internal buffer is contiguous
    dq.make_contiguous();

    let v: Vec<_> = dq.into_iter().collect();
    assert_eq!(v, vec![0, 1, 2, 3, 4, 5]);
}

fn main() {
    basic_deque();
    rotation();
    sliding_window();
    conversions();
    println!("โœ“ All tests passed");
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_basic_deque() { basic_deque(); }

    #[test]
    fn test_rotation() { rotation(); }

    #[test]
    fn test_sliding_window() { sliding_window(); }

    #[test]
    fn test_conversions() { conversions(); }

    #[test]
    fn test_indexed_access() {
        let dq: VecDeque<_> = [10, 20, 30].into_iter().collect();
        assert_eq!(dq[0], 10);
        assert_eq!(dq[1], 20);
        assert_eq!(dq[2], 30);
    }
}
(* 1032: VecDeque Rotation โ€” Efficient Front/Back Operations *)
(* OCaml uses lists (efficient at front) or Queue module *)

(* Approach 1: List as a deque โ€” efficient front, O(n) back *)
let list_deque () =
  let q = [1; 2; 3; 4; 5] in
  (* Push front: O(1) *)
  let q = 0 :: q in
  assert (q = [0; 1; 2; 3; 4; 5]);
  (* Pop front: O(1) *)
  let (front, q) = (List.hd q, List.tl q) in
  assert (front = 0);
  assert (q = [1; 2; 3; 4; 5]);
  (* Push back: O(n) *)
  let q = q @ [6] in
  assert (q = [1; 2; 3; 4; 5; 6])

(* Approach 2: Two-list queue for amortized O(1) both ends *)
type 'a deque = { front: 'a list; back: 'a list }

let empty = { front = []; back = [] }
let push_front x d = { d with front = x :: d.front }
let push_back x d = { d with back = x :: d.back }

let pop_front d =
  match d.front with
  | x :: rest -> Some (x, { d with front = rest })
  | [] ->
    match List.rev d.back with
    | x :: rest -> Some (x, { front = rest; back = [] })
    | [] -> None

let pop_back d =
  match d.back with
  | x :: rest -> Some (x, { d with back = rest })
  | [] ->
    match List.rev d.front with
    | x :: rest -> Some (x, { front = []; back = rest })
    | [] -> None

let two_list_deque () =
  let d = empty in
  let d = push_back 1 d in
  let d = push_back 2 d in
  let d = push_back 3 d in
  let d = push_front 0 d in
  let (v, d) = Option.get (pop_front d) in
  assert (v = 0);
  let (v, d) = Option.get (pop_front d) in
  assert (v = 1);
  let (v, _d) = Option.get (pop_back d) in
  assert (v = 3)

(* Approach 3: Rotation *)
let rotate_left lst n =
  let len = List.length lst in
  let n = n mod len in
  let rec take_drop i = function
    | [] -> ([], [])
    | x :: xs when i > 0 ->
      let (taken, rest) = take_drop (i - 1) xs in
      (x :: taken, rest)
    | xs -> ([], xs)
  in
  let (front, back) = take_drop n lst in
  back @ front

let rotation_test () =
  let lst = [1; 2; 3; 4; 5] in
  assert (rotate_left lst 2 = [3; 4; 5; 1; 2]);
  assert (rotate_left lst 0 = [1; 2; 3; 4; 5])

let () =
  list_deque ();
  two_list_deque ();
  rotation_test ();
  Printf.printf "โœ“ All tests passed\n"

๐Ÿ“Š Detailed Comparison

VecDeque Rotation โ€” Comparison

Core Insight

Rust provides a purpose-built ring buffer (`VecDeque`) with O(1) operations at both ends and built-in rotation. OCaml's standard library lacks this; the common workaround is a two-list queue (amortized O(1)) or a simple list (O(1) front, O(n) back).

OCaml Approach

  • List: O(1) push/pop front, O(n) back operations
  • Two-list deque: `{ front; back }` with lazy reversal for amortized O(1)
  • Manual rotation: split list at index, append halves
  • `Queue` module exists but is mutable and not a deque

Rust Approach

  • `VecDeque<T>`: ring buffer backed by contiguous array
  • O(1) `push_front`, `push_back`, `pop_front`, `pop_back`
  • `rotate_left(n)` / `rotate_right(n)`: built-in rotation
  • Indexed access via `dq[i]`
  • `make_contiguous()` to linearize internal buffer
  • Excellent for sliding windows

Comparison Table

FeatureOCaml (list/two-list)Rust (`VecDeque`)
Push frontO(1)O(1)
Push backO(n) list / amortized O(1)O(1)
Pop frontO(1)O(1)
Pop backO(n) list / amortized O(1)O(1)
RotationManual O(n)Built-in O(n)
Random accessO(n)O(1)
Memory layoutLinked nodesContiguous ring buffer
Cache friendlyNoYes