Skip to content

Commit

Permalink
Auto merge of #56842 - scottmcm:vecdeque-rotate, r=alexcrichton
Browse files Browse the repository at this point in the history
Add unstable VecDeque::rotate_{left|right}

Like the ones on slices, but more efficient because vecdeque is a circular buffer.

Issue that proposed this: #56686

~~:bomb: Please someone look very carefully at the `unsafe` in this!  The `wrap_copy` seems to be exactly what this method needs, and the `len` passed to it is never more than half the length of the deque, but I haven't managed to prove to myself that it's correct :bomb:~~ I think I proved that this code meets the requirement of the unsafe code it's calling; please double-check, of course.
  • Loading branch information
bors committed Dec 22, 2018
2 parents 24667aa + cbe9abb commit 9689ada
Show file tree
Hide file tree
Showing 3 changed files with 248 additions and 1 deletion.
112 changes: 112 additions & 0 deletions src/liballoc/collections/vec_deque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1927,6 +1927,118 @@ impl<T> VecDeque<T> {
self.truncate(new_len);
}
}

/// Rotates the double-ended queue `mid` places to the left.
///
/// Equivalently,
/// - Rotates item `mid` into the first position.
/// - Pops the first `mid` items and pushes them to the end.
/// - Rotates `len() - mid` places to the right.
///
/// # Panics
///
/// If `mid` is greater than `len()`. Note that `mid == len()`
/// does _not_ panic and is a no-op rotation.
///
/// # Complexity
///
/// Takes `O(min(mid, len() - mid))` time and no extra space.
///
/// # Examples
///
/// ```
/// #![feature(vecdeque_rotate)]
///
/// use std::collections::VecDeque;
///
/// let mut buf: VecDeque<_> = (0..10).collect();
///
/// buf.rotate_left(3);
/// assert_eq!(buf, [3, 4, 5, 6, 7, 8, 9, 0, 1, 2]);
///
/// for i in 1..10 {
/// assert_eq!(i * 3 % 10, buf[0]);
/// buf.rotate_left(3);
/// }
/// assert_eq!(buf, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
/// ```
#[unstable(feature = "vecdeque_rotate", issue = "56686")]
pub fn rotate_left(&mut self, mid: usize) {
assert!(mid <= self.len());
let k = self.len() - mid;
if mid <= k {
unsafe { self.rotate_left_inner(mid) }
} else {
unsafe { self.rotate_right_inner(k) }
}
}

/// Rotates the double-ended queue `k` places to the right.
///
/// Equivalently,
/// - Rotates the first item into position `k`.
/// - Pops the last `k` items and pushes them to the front.
/// - Rotates `len() - k` places to the left.
///
/// # Panics
///
/// If `k` is greater than `len()`. Note that `k == len()`
/// does _not_ panic and is a no-op rotation.
///
/// # Complexity
///
/// Takes `O(min(k, len() - k))` time and no extra space.
///
/// # Examples
///
/// ```
/// #![feature(vecdeque_rotate)]
///
/// use std::collections::VecDeque;
///
/// let mut buf: VecDeque<_> = (0..10).collect();
///
/// buf.rotate_right(3);
/// assert_eq!(buf, [7, 8, 9, 0, 1, 2, 3, 4, 5, 6]);
///
/// for i in 1..10 {
/// assert_eq!(0, buf[i * 3 % 10]);
/// buf.rotate_right(3);
/// }
/// assert_eq!(buf, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
/// ```
#[unstable(feature = "vecdeque_rotate", issue = "56686")]
pub fn rotate_right(&mut self, k: usize) {
assert!(k <= self.len());
let mid = self.len() - k;
if k <= mid {
unsafe { self.rotate_right_inner(k) }
} else {
unsafe { self.rotate_left_inner(mid) }
}
}

// Safety: the following two methods require that the rotation amount
// be less than half the length of the deque.
//
// `wrap_copy` requres that `min(x, cap() - x) + copy_len <= cap()`,
// but than `min` is never more than half the capacity, regardless of x,
// so it's sound to call here because we're calling with something
// less than half the length, which is never above half the capacity.

unsafe fn rotate_left_inner(&mut self, mid: usize) {
debug_assert!(mid * 2 <= self.len());
self.wrap_copy(self.head, self.tail, mid);
self.head = self.wrap_add(self.head, mid);
self.tail = self.wrap_add(self.tail, mid);
}

unsafe fn rotate_right_inner(&mut self, k: usize) {
debug_assert!(k * 2 <= self.len());
self.head = self.wrap_sub(self.head, k);
self.tail = self.wrap_sub(self.tail, k);
self.wrap_copy(self.tail, self.head, k);
}
}

impl<T: Clone> VecDeque<T> {
Expand Down
3 changes: 2 additions & 1 deletion src/liballoc/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@
#![feature(drain_filter)]
#![feature(exact_size_is_empty)]
#![feature(pattern)]
#![feature(repeat_generic_slice)]
#![feature(slice_sort_by_cached_key)]
#![feature(str_escape)]
#![feature(try_reserve)]
#![feature(unboxed_closures)]
#![feature(repeat_generic_slice)]
#![feature(vecdeque_rotate)]

extern crate core;
extern crate rand;
Expand Down
134 changes: 134 additions & 0 deletions src/liballoc/tests/vec_deque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1309,3 +1309,137 @@ fn test_try_reserve_exact() {
}

}

#[test]
fn test_rotate_nop() {
let mut v: VecDeque<_> = (0..10).collect();
assert_unchanged(&v);

v.rotate_left(0);
assert_unchanged(&v);

v.rotate_left(10);
assert_unchanged(&v);

v.rotate_right(0);
assert_unchanged(&v);

v.rotate_right(10);
assert_unchanged(&v);

v.rotate_left(3);
v.rotate_right(3);
assert_unchanged(&v);

v.rotate_right(3);
v.rotate_left(3);
assert_unchanged(&v);

v.rotate_left(6);
v.rotate_right(6);
assert_unchanged(&v);

v.rotate_right(6);
v.rotate_left(6);
assert_unchanged(&v);

v.rotate_left(3);
v.rotate_left(7);
assert_unchanged(&v);

v.rotate_right(4);
v.rotate_right(6);
assert_unchanged(&v);

v.rotate_left(1);
v.rotate_left(2);
v.rotate_left(3);
v.rotate_left(4);
assert_unchanged(&v);

v.rotate_right(1);
v.rotate_right(2);
v.rotate_right(3);
v.rotate_right(4);
assert_unchanged(&v);

fn assert_unchanged(v: &VecDeque<i32>) {
assert_eq!(v, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
}
}

#[test]
fn test_rotate_left_parts() {
let mut v: VecDeque<_> = (1..=7).collect();
v.rotate_left(2);
assert_eq!(v.as_slices(), (&[3, 4, 5, 6, 7, 1][..], &[2][..]));
v.rotate_left(2);
assert_eq!(v.as_slices(), (&[5, 6, 7, 1][..], &[2, 3, 4][..]));
v.rotate_left(2);
assert_eq!(v.as_slices(), (&[7, 1][..], &[2, 3, 4, 5, 6][..]));
v.rotate_left(2);
assert_eq!(v.as_slices(), (&[2, 3, 4, 5, 6, 7, 1][..], &[][..]));
v.rotate_left(2);
assert_eq!(v.as_slices(), (&[4, 5, 6, 7, 1, 2][..], &[3][..]));
v.rotate_left(2);
assert_eq!(v.as_slices(), (&[6, 7, 1, 2][..], &[3, 4, 5][..]));
v.rotate_left(2);
assert_eq!(v.as_slices(), (&[1, 2][..], &[3, 4, 5, 6, 7][..]));
}

#[test]
fn test_rotate_right_parts() {
let mut v: VecDeque<_> = (1..=7).collect();
v.rotate_right(2);
assert_eq!(v.as_slices(), (&[6, 7][..], &[1, 2, 3, 4, 5][..]));
v.rotate_right(2);
assert_eq!(v.as_slices(), (&[4, 5, 6, 7][..], &[1, 2, 3][..]));
v.rotate_right(2);
assert_eq!(v.as_slices(), (&[2, 3, 4, 5, 6, 7][..], &[1][..]));
v.rotate_right(2);
assert_eq!(v.as_slices(), (&[7, 1, 2, 3, 4, 5, 6][..], &[][..]));
v.rotate_right(2);
assert_eq!(v.as_slices(), (&[5, 6][..], &[7, 1, 2, 3, 4][..]));
v.rotate_right(2);
assert_eq!(v.as_slices(), (&[3, 4, 5, 6][..], &[7, 1, 2][..]));
v.rotate_right(2);
assert_eq!(v.as_slices(), (&[1, 2, 3, 4, 5, 6][..], &[7][..]));
}

#[test]
fn test_rotate_left_random() {
let shifts = [
6, 1, 0, 11, 12, 1, 11, 7, 9, 3, 6, 1,
4, 0, 5, 1, 3, 1, 12, 8, 3, 1, 11, 11,
9, 4, 12, 3, 12, 9, 11, 1, 7, 9, 7, 2,
];
let n = 12;
let mut v: VecDeque<_> = (0..n).collect();
let mut total_shift = 0;
for shift in shifts.iter().cloned() {
v.rotate_left(shift);
total_shift += shift;
for i in 0..n {
assert_eq!(v[i], (i + total_shift) % n);
}
}
}

#[test]
fn test_rotate_right_random() {
let shifts = [
6, 1, 0, 11, 12, 1, 11, 7, 9, 3, 6, 1,
4, 0, 5, 1, 3, 1, 12, 8, 3, 1, 11, 11,
9, 4, 12, 3, 12, 9, 11, 1, 7, 9, 7, 2,
];
let n = 12;
let mut v: VecDeque<_> = (0..n).collect();
let mut total_shift = 0;
for shift in shifts.iter().cloned() {
v.rotate_right(shift);
total_shift += shift;
for i in 0..n {
assert_eq!(v[(i + total_shift) % n], i);
}
}
}

0 comments on commit 9689ada

Please sign in to comment.