about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <dylan.dpc@gmail.com>2020-10-17 03:27:15 +0200
committerGitHub <noreply@github.com>2020-10-17 03:27:15 +0200
commitf40ecff9644e723752732d9d1f6723260ee7bb23 (patch)
tree2d3f23e4f6d40e73c8f09809616bb115b120d851
parent496e2feed684afc3eb43a3e4ac933fc61a5a8305 (diff)
parentc7a787a3276cadad7ee51577f65158b4888c058c (diff)
downloadrust-f40ecff9644e723752732d9d1f6723260ee7bb23.tar.gz
rust-f40ecff9644e723752732d9d1f6723260ee7bb23.zip
Rollup merge of #77751 - vojtechkral:vecdeque-binary-search, r=scottmcm,dtolnay
liballoc: VecDeque: Add binary search functions

I am submitting rust-lang/rfcs#2997 as a PR as suggested by @scottmcm

I haven't yet created a tracking issue - if there's a favorable feedback I'll create one and update the issue links in the unstable attribs.
-rw-r--r--library/alloc/src/collections/vec_deque.rs139
-rw-r--r--library/alloc/tests/lib.rs1
-rw-r--r--library/alloc/tests/vec_deque.rs39
3 files changed, 178 insertions, 1 deletions
diff --git a/library/alloc/src/collections/vec_deque.rs b/library/alloc/src/collections/vec_deque.rs
index ff9b1553bf2..94dac1cd176 100644
--- a/library/alloc/src/collections/vec_deque.rs
+++ b/library/alloc/src/collections/vec_deque.rs
@@ -2181,7 +2181,7 @@ impl<T> VecDeque<T> {
     ///
     /// This method does not allocate and does not change the order of the
     /// inserted elements. As it returns a mutable slice, this can be used to
-    /// sort or binary search a deque.
+    /// sort a deque.
     ///
     /// Once the internal storage is contiguous, the [`as_slices`] and
     /// [`as_mut_slices`] methods will return the entire contents of the
@@ -2430,6 +2430,143 @@ impl<T> VecDeque<T> {
             self.wrap_copy(self.tail, self.head, k);
         }
     }
+
+    /// Binary searches this sorted `VecDeque` for a given element.
+    ///
+    /// If the value is found then [`Result::Ok`] is returned, containing the
+    /// index of the matching element. If there are multiple matches, then any
+    /// one of the matches could be returned. If the value is not found then
+    /// [`Result::Err`] is returned, containing the index where a matching
+    /// element could be inserted while maintaining sorted order.
+    ///
+    /// # Examples
+    ///
+    /// Looks up a series of four elements. The first is found, with a
+    /// uniquely determined position; the second and third are not
+    /// found; the fourth could match any position in `[1, 4]`.
+    ///
+    /// ```
+    /// #![feature(vecdeque_binary_search)]
+    /// use std::collections::VecDeque;
+    ///
+    /// let deque: VecDeque<_> = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55].into();
+    ///
+    /// assert_eq!(deque.binary_search(&13),  Ok(9));
+    /// assert_eq!(deque.binary_search(&4),   Err(7));
+    /// assert_eq!(deque.binary_search(&100), Err(13));
+    /// let r = deque.binary_search(&1);
+    /// assert!(matches!(r, Ok(1..=4)));
+    /// ```
+    ///
+    /// If you want to insert an item to a sorted `VecDeque`, while maintaining
+    /// sort order:
+    ///
+    /// ```
+    /// #![feature(vecdeque_binary_search)]
+    /// use std::collections::VecDeque;
+    ///
+    /// let mut deque: VecDeque<_> = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55].into();
+    /// let num = 42;
+    /// let idx = deque.binary_search(&num).unwrap_or_else(|x| x);
+    /// deque.insert(idx, num);
+    /// assert_eq!(deque, &[0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]);
+    /// ```
+    #[unstable(feature = "vecdeque_binary_search", issue = "78021")]
+    #[inline]
+    pub fn binary_search(&self, x: &T) -> Result<usize, usize>
+    where
+        T: Ord,
+    {
+        self.binary_search_by(|e| e.cmp(x))
+    }
+
+    /// Binary searches this sorted `VecDeque` with a comparator function.
+    ///
+    /// The comparator function should implement an order consistent
+    /// with the sort order of the underlying `VecDeque`, returning an
+    /// order code that indicates whether its argument is `Less`,
+    /// `Equal` or `Greater` than the desired target.
+    ///
+    /// If the value is found then [`Result::Ok`] is returned, containing the
+    /// index of the matching element. If there are multiple matches, then any
+    /// one of the matches could be returned. If the value is not found then
+    /// [`Result::Err`] is returned, containing the index where a matching
+    /// element could be inserted while maintaining sorted order.
+    ///
+    /// # Examples
+    ///
+    /// Looks up a series of four elements. The first is found, with a
+    /// uniquely determined position; the second and third are not
+    /// found; the fourth could match any position in `[1, 4]`.
+    ///
+    /// ```
+    /// #![feature(vecdeque_binary_search)]
+    /// use std::collections::VecDeque;
+    ///
+    /// let deque: VecDeque<_> = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55].into();
+    ///
+    /// assert_eq!(deque.binary_search_by(|x| x.cmp(&13)),  Ok(9));
+    /// assert_eq!(deque.binary_search_by(|x| x.cmp(&4)),   Err(7));
+    /// assert_eq!(deque.binary_search_by(|x| x.cmp(&100)), Err(13));
+    /// let r = deque.binary_search_by(|x| x.cmp(&1));
+    /// assert!(matches!(r, Ok(1..=4)));
+    /// ```
+    #[unstable(feature = "vecdeque_binary_search", issue = "78021")]
+    pub fn binary_search_by<'a, F>(&'a self, mut f: F) -> Result<usize, usize>
+    where
+        F: FnMut(&'a T) -> Ordering,
+    {
+        let (front, back) = self.as_slices();
+
+        if let Some(Ordering::Less | Ordering::Equal) = back.first().map(|elem| f(elem)) {
+            back.binary_search_by(f).map(|idx| idx + front.len()).map_err(|idx| idx + front.len())
+        } else {
+            front.binary_search_by(f)
+        }
+    }
+
+    /// Binary searches this sorted `VecDeque` with a key extraction function.
+    ///
+    /// Assumes that the `VecDeque` is sorted by the key, for instance with
+    /// [`make_contiguous().sort_by_key()`](#method.make_contiguous) using the same
+    /// key extraction function.
+    ///
+    /// If the value is found then [`Result::Ok`] is returned, containing the
+    /// index of the matching element. If there are multiple matches, then any
+    /// one of the matches could be returned. If the value is not found then
+    /// [`Result::Err`] is returned, containing the index where a matching
+    /// element could be inserted while maintaining sorted order.
+    ///
+    /// # Examples
+    ///
+    /// Looks up a series of four elements in a slice of pairs sorted by
+    /// their second elements. The first is found, with a uniquely
+    /// determined position; the second and third are not found; the
+    /// fourth could match any position in `[1, 4]`.
+    ///
+    /// ```
+    /// #![feature(vecdeque_binary_search)]
+    /// use std::collections::VecDeque;
+    ///
+    /// let deque: VecDeque<_> = vec![(0, 0), (2, 1), (4, 1), (5, 1),
+    ///          (3, 1), (1, 2), (2, 3), (4, 5), (5, 8), (3, 13),
+    ///          (1, 21), (2, 34), (4, 55)].into();
+    ///
+    /// assert_eq!(deque.binary_search_by_key(&13, |&(a,b)| b),  Ok(9));
+    /// assert_eq!(deque.binary_search_by_key(&4, |&(a,b)| b),   Err(7));
+    /// assert_eq!(deque.binary_search_by_key(&100, |&(a,b)| b), Err(13));
+    /// let r = deque.binary_search_by_key(&1, |&(a,b)| b);
+    /// assert!(matches!(r, Ok(1..=4)));
+    /// ```
+    #[unstable(feature = "vecdeque_binary_search", issue = "78021")]
+    #[inline]
+    pub fn binary_search_by_key<'a, B, F>(&'a self, b: &B, mut f: F) -> Result<usize, usize>
+    where
+        F: FnMut(&'a T) -> B,
+        B: Ord,
+    {
+        self.binary_search_by(|k| f(k).cmp(b))
+    }
 }
 
 impl<T: Clone> VecDeque<T> {
diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs
index cff8ff9ac7a..b7cc03f8eb9 100644
--- a/library/alloc/tests/lib.rs
+++ b/library/alloc/tests/lib.rs
@@ -20,6 +20,7 @@
 #![feature(inplace_iteration)]
 #![feature(iter_map_while)]
 #![feature(int_bits_const)]
+#![feature(vecdeque_binary_search)]
 
 use std::collections::hash_map::DefaultHasher;
 use std::hash::{Hash, Hasher};
diff --git a/library/alloc/tests/vec_deque.rs b/library/alloc/tests/vec_deque.rs
index 46d8a3c4cb4..05cb3a2c03d 100644
--- a/library/alloc/tests/vec_deque.rs
+++ b/library/alloc/tests/vec_deque.rs
@@ -1659,3 +1659,42 @@ fn test_drain_leak() {
     drop(v);
     assert_eq!(unsafe { DROPS }, 7);
 }
+
+#[test]
+fn test_binary_search() {
+    // Contiguous (front only) search:
+    let deque: VecDeque<_> = vec![1, 2, 3, 5, 6].into();
+    assert!(deque.as_slices().1.is_empty());
+    assert_eq!(deque.binary_search(&3), Ok(2));
+    assert_eq!(deque.binary_search(&4), Err(3));
+
+    // Split search (both front & back non-empty):
+    let mut deque: VecDeque<_> = vec![5, 6].into();
+    deque.push_front(3);
+    deque.push_front(2);
+    deque.push_front(1);
+    deque.push_back(10);
+    assert!(!deque.as_slices().0.is_empty());
+    assert!(!deque.as_slices().1.is_empty());
+    assert_eq!(deque.binary_search(&0), Err(0));
+    assert_eq!(deque.binary_search(&1), Ok(0));
+    assert_eq!(deque.binary_search(&5), Ok(3));
+    assert_eq!(deque.binary_search(&7), Err(5));
+    assert_eq!(deque.binary_search(&20), Err(6));
+}
+
+#[test]
+fn test_binary_search_by() {
+    let deque: VecDeque<_> = vec![(1,), (2,), (3,), (5,), (6,)].into();
+
+    assert_eq!(deque.binary_search_by(|&(v,)| v.cmp(&3)), Ok(2));
+    assert_eq!(deque.binary_search_by(|&(v,)| v.cmp(&4)), Err(3));
+}
+
+#[test]
+fn test_binary_search_by_key() {
+    let deque: VecDeque<_> = vec![(1,), (2,), (3,), (5,), (6,)].into();
+
+    assert_eq!(deque.binary_search_by_key(&3, |&(v,)| v), Ok(2));
+    assert_eq!(deque.binary_search_by_key(&4, |&(v,)| v), Err(3));
+}