From ffeac1f71fe8d8206c9c83747cdd7816ad9aafc2 Mon Sep 17 00:00:00 2001
From: Stein Somers <git@steinsomers.be>
Date: Sun, 20 Oct 2019 20:06:20 +0200
Subject: [PATCH] proposal for access to BTreeMap/BTreeSet first/last, #62924

---
 src/liballoc/benches/btree/map.rs     |  25 ++++++
 src/liballoc/benches/lib.rs           |   1 +
 src/liballoc/collections/btree/map.rs | 115 ++++++++++++++++++++++++++
 src/liballoc/collections/btree/set.rs | 110 +++++++++++++++++++++---
 src/liballoc/tests/btree/map.rs       |  45 ++++++++++
 src/liballoc/tests/btree/set.rs       |  28 +++++++
 src/liballoc/tests/lib.rs             |   1 +
 7 files changed, 314 insertions(+), 11 deletions(-)

diff --git a/src/liballoc/benches/btree/map.rs b/src/liballoc/benches/btree/map.rs
index 4c17bdc3e9e9d..b541fa94e954e 100644
--- a/src/liballoc/benches/btree/map.rs
+++ b/src/liballoc/benches/btree/map.rs
@@ -145,3 +145,28 @@ pub fn iter_1000(b: &mut Bencher) {
 pub fn iter_100000(b: &mut Bencher) {
     bench_iter(b, 100000);
 }
+
+fn bench_first_and_last(b: &mut Bencher, size: i32) {
+    let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect();
+    b.iter(|| {
+        for _ in 0..10 {
+            black_box(map.first_key_value());
+            black_box(map.last_key_value());
+        }
+    });
+}
+
+#[bench]
+pub fn first_and_last_0(b: &mut Bencher) {
+    bench_first_and_last(b, 0);
+}
+
+#[bench]
+pub fn first_and_last_100(b: &mut Bencher) {
+    bench_first_and_last(b, 100);
+}
+
+#[bench]
+pub fn first_and_last_10k(b: &mut Bencher) {
+    bench_first_and_last(b, 10_000);
+}
diff --git a/src/liballoc/benches/lib.rs b/src/liballoc/benches/lib.rs
index 4bf5ec10c41e7..9acda886064ce 100644
--- a/src/liballoc/benches/lib.rs
+++ b/src/liballoc/benches/lib.rs
@@ -1,3 +1,4 @@
+#![feature(map_first_last)]
 #![feature(repr_simd)]
 #![feature(test)]
 
diff --git a/src/liballoc/collections/btree/map.rs b/src/liballoc/collections/btree/map.rs
index 83fd4485f7321..5b48b594ff907 100644
--- a/src/liballoc/collections/btree/map.rs
+++ b/src/liballoc/collections/btree/map.rs
@@ -598,6 +598,121 @@ impl<K: Ord, V> BTreeMap<K, V> {
         }
     }
 
+    /// Returns the first key-value pair in the map.
+    /// The key in this pair is the minimum key in the map.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// #![feature(map_first_last)]
+    /// use std::collections::BTreeMap;
+    ///
+    /// let mut map = BTreeMap::new();
+    /// assert_eq!(map.first_key_value(), None);
+    /// map.insert(1, "b");
+    /// map.insert(2, "a");
+    /// assert_eq!(map.first_key_value(), Some((&1, &"b")));
+    /// ```
+    #[unstable(feature = "map_first_last", issue = "62924")]
+    pub fn first_key_value<T: ?Sized>(&self) -> Option<(&K, &V)>
+        where T: Ord, K: Borrow<T>
+    {
+        let front = first_leaf_edge(self.root.as_ref());
+        front.right_kv().ok().map(Handle::into_kv)
+    }
+
+    /// Returns the first entry in the map for in-place manipulation.
+    /// The key of this entry is the minimum key in the map.
+    ///
+    /// # Examples
+    ///
+    /// Contrived way to `clear` a map:
+    ///
+    /// ```
+    /// #![feature(map_first_last)]
+    /// use std::collections::BTreeMap;
+    ///
+    /// let mut map = BTreeMap::new();
+    /// map.insert(1, "a");
+    /// map.insert(2, "b");
+    /// while let Some(entry) = map.first_entry() {
+    ///     let (key, val) = entry.remove_entry();
+    ///     assert!(!map.contains_key(&key));
+    /// }
+    /// ```
+    #[unstable(feature = "map_first_last", issue = "62924")]
+    pub fn first_entry<T: ?Sized>(&mut self) -> Option<OccupiedEntry<'_, K, V>>
+        where T: Ord, K: Borrow<T>
+    {
+        match self.length {
+            0 => None,
+            _ => Some(OccupiedEntry {
+                          handle: self.root.as_mut().first_kv(),
+                          length: &mut self.length,
+                          _marker: PhantomData,
+                      }),
+        }
+    }
+
+    /// Returns the last key-value pair in the map.
+    /// The key in this pair is the maximum key in the map.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// #![feature(map_first_last)]
+    /// use std::collections::BTreeMap;
+    ///
+    /// let mut map = BTreeMap::new();
+    /// map.insert(1, "b");
+    /// map.insert(2, "a");
+    /// assert_eq!(map.last_key_value(), Some((&2, &"a")));
+    /// ```
+    #[unstable(feature = "map_first_last", issue = "62924")]
+    pub fn last_key_value<T: ?Sized>(&self) -> Option<(&K, &V)>
+        where T: Ord, K: Borrow<T>
+    {
+        let back = last_leaf_edge(self.root.as_ref());
+        back.left_kv().ok().map(Handle::into_kv)
+    }
+
+    /// Returns the last entry in the map for in-place manipulation.
+    /// The key of this entry is the maximum key in the map.
+    ///
+    /// # Examples
+    ///
+    /// Contrived way to `clear` a map:
+    ///
+    /// ```
+    /// #![feature(map_first_last)]
+    /// use std::collections::BTreeMap;
+    ///
+    /// let mut map = BTreeMap::new();
+    /// map.insert(1, "a");
+    /// map.insert(2, "b");
+    /// while let Some(entry) = map.last_entry() {
+    ///     let (key, val) = entry.remove_entry();
+    ///     assert!(!map.contains_key(&key));
+    /// }
+    /// ```
+    #[unstable(feature = "map_first_last", issue = "62924")]
+    pub fn last_entry<T: ?Sized>(&mut self) -> Option<OccupiedEntry<'_, K, V>>
+        where T: Ord, K: Borrow<T>
+    {
+        match self.length {
+            0 => None,
+            _ => Some(OccupiedEntry {
+                          handle: self.root.as_mut().last_kv(),
+                          length: &mut self.length,
+                          _marker: PhantomData,
+                      }),
+        }
+    }
+
     /// Returns `true` if the map contains a value for the specified key.
     ///
     /// The key may be any borrowed form of the map's key type, but the ordering
diff --git a/src/liballoc/collections/btree/set.rs b/src/liballoc/collections/btree/set.rs
index f0796354e00c3..85b93e0eda45b 100644
--- a/src/liballoc/collections/btree/set.rs
+++ b/src/liballoc/collections/btree/set.rs
@@ -194,16 +194,16 @@ pub struct Difference<'a, T: 'a> {
 #[derive(Debug)]
 enum DifferenceInner<'a, T: 'a> {
     Stitch {
-        // iterate all of self and some of other, spotting matches along the way
+        // iterate all of `self` and some of `other`, spotting matches along the way
         self_iter: Iter<'a, T>,
         other_iter: Peekable<Iter<'a, T>>,
     },
     Search {
-        // iterate a small set, look up in the large set
+        // iterate `self`, look up in `other`
         self_iter: Iter<'a, T>,
         other_set: &'a BTreeSet<T>,
     },
-    Iterate(Iter<'a, T>), // simply stream self's elements
+    Iterate(Iter<'a, T>), // simply produce all values in `self`
 }
 
 #[stable(feature = "collection_debug", since = "1.17.0")]
@@ -356,7 +356,7 @@ impl<T: Ord> BTreeSet<T> {
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn difference<'a>(&'a self, other: &'a BTreeSet<T>) -> Difference<'a, T> {
         let (self_min, self_max) = if let (Some(self_min), Some(self_max)) =
-            (self.iter().next(), self.iter().next_back())
+            (self.first(), self.last())
         {
             (self_min, self_max)
         } else {
@@ -365,7 +365,7 @@ impl<T: Ord> BTreeSet<T> {
             };
         };
         let (other_min, other_max) = if let (Some(other_min), Some(other_max)) =
-            (other.iter().next(), other.iter().next_back())
+            (other.first(), other.last())
         {
             (other_min, other_max)
         } else {
@@ -450,7 +450,7 @@ impl<T: Ord> BTreeSet<T> {
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn intersection<'a>(&'a self, other: &'a BTreeSet<T>) -> Intersection<'a, T> {
         let (self_min, self_max) = if let (Some(self_min), Some(self_max)) =
-            (self.iter().next(), self.iter().next_back())
+            (self.first(), self.last())
         {
             (self_min, self_max)
         } else {
@@ -459,7 +459,7 @@ impl<T: Ord> BTreeSet<T> {
             };
         };
         let (other_min, other_max) = if let (Some(other_min), Some(other_max)) =
-            (other.iter().next(), other.iter().next_back())
+            (other.first(), other.last())
         {
             (other_min, other_max)
         } else {
@@ -625,14 +625,14 @@ impl<T: Ord> BTreeSet<T> {
             return false;
         }
         let (self_min, self_max) = if let (Some(self_min), Some(self_max)) =
-            (self.iter().next(), self.iter().next_back())
+            (self.first(), self.last())
         {
             (self_min, self_max)
         } else {
             return true; // self is empty
         };
         let (other_min, other_max) = if let (Some(other_min), Some(other_max)) =
-            (other.iter().next(), other.iter().next_back())
+            (other.first(), other.last())
         {
             (other_min, other_max)
         } else {
@@ -654,14 +654,12 @@ impl<T: Ord> BTreeSet<T> {
             Less => (),
         }
         if self_iter.len() <= other.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF {
-            // Big difference in number of elements.
             for next in self_iter {
                 if !other.contains(next) {
                     return false;
                 }
             }
         } else {
-            // Self is not much smaller than other set.
             let mut other_iter = other.iter();
             other_iter.next();
             other_iter.next_back();
@@ -702,6 +700,96 @@ impl<T: Ord> BTreeSet<T> {
         other.is_subset(self)
     }
 
+    /// Returns a reference to the first value in the set, if any.
+    /// This value is always the minimum of all values in the set.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// #![feature(map_first_last)]
+    /// use std::collections::BTreeSet;
+    ///
+    /// let mut map = BTreeSet::new();
+    /// assert_eq!(map.first(), None);
+    /// map.insert(1);
+    /// assert_eq!(map.first(), Some(&1));
+    /// map.insert(2);
+    /// assert_eq!(map.first(), Some(&1));
+    /// ```
+    #[unstable(feature = "map_first_last", issue = "62924")]
+    pub fn first(&self) -> Option<&T> {
+        self.map.first_key_value().map(|(k, _)| k)
+    }
+
+    /// Returns a reference to the last value in the set, if any.
+    /// This value is always the maximum of all values in the set.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// #![feature(map_first_last)]
+    /// use std::collections::BTreeSet;
+    ///
+    /// let mut map = BTreeSet::new();
+    /// assert_eq!(map.first(), None);
+    /// map.insert(1);
+    /// assert_eq!(map.last(), Some(&1));
+    /// map.insert(2);
+    /// assert_eq!(map.last(), Some(&2));
+    /// ```
+    #[unstable(feature = "map_first_last", issue = "62924")]
+    pub fn last(&self) -> Option<&T> {
+        self.map.last_key_value().map(|(k, _)| k)
+    }
+
+    /// Removes the first value from the set and returns it, if any.
+    /// The first value is always the minimum value in the set.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(map_first_last)]
+    /// use std::collections::BTreeSet;
+    ///
+    /// let mut set = BTreeSet::new();
+    ///
+    /// set.insert(1);
+    /// while let Some(n) = set.pop_first() {
+    ///     assert_eq!(n, 1);
+    /// }
+    /// assert!(set.is_empty());
+    /// ```
+    #[unstable(feature = "map_first_last", issue = "62924")]
+    pub fn pop_first(&mut self) -> Option<T> {
+        self.map.first_entry().map(|entry| entry.remove_entry().0)
+    }
+
+    /// Removes the last value from the set and returns it, if any.
+    /// The last value is always the maximum value in the set.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(map_first_last)]
+    /// use std::collections::BTreeSet;
+    ///
+    /// let mut set = BTreeSet::new();
+    ///
+    /// set.insert(1);
+    /// while let Some(n) = set.pop_last() {
+    ///     assert_eq!(n, 1);
+    /// }
+    /// assert!(set.is_empty());
+    /// ```
+    #[unstable(feature = "map_first_last", issue = "62924")]
+    pub fn pop_last(&mut self) -> Option<T> {
+        self.map.last_entry().map(|entry| entry.remove_entry().0)
+    }
+
     /// Adds a value to the set.
     ///
     /// If the set did not have this value present, `true` is returned.
diff --git a/src/liballoc/tests/btree/map.rs b/src/liballoc/tests/btree/map.rs
index 266a0d055d5bc..27843aeaeb0c8 100644
--- a/src/liballoc/tests/btree/map.rs
+++ b/src/liballoc/tests/btree/map.rs
@@ -58,15 +58,35 @@ fn test_basic_large() {
 fn test_basic_small() {
     let mut map = BTreeMap::new();
     assert_eq!(map.remove(&1), None);
+    assert_eq!(map.len(), 0);
+    assert_eq!(map.first_key_value(), None);
+    assert_eq!(map.last_key_value(), None);
     assert_eq!(map.get(&1), None);
     assert_eq!(map.insert(1, 1), None);
+    assert_eq!(map.len(), 1);
     assert_eq!(map.get(&1), Some(&1));
+    assert_eq!(map.first_key_value(), Some((&1, &1)));
+    assert_eq!(map.last_key_value(), Some((&1, &1)));
     assert_eq!(map.insert(1, 2), Some(1));
+    assert_eq!(map.len(), 1);
     assert_eq!(map.get(&1), Some(&2));
+    assert_eq!(map.first_key_value(), Some((&1, &2)));
+    assert_eq!(map.last_key_value(), Some((&1, &2)));
     assert_eq!(map.insert(2, 4), None);
+    assert_eq!(map.len(), 2);
     assert_eq!(map.get(&2), Some(&4));
+    assert_eq!(map.first_key_value(), Some((&1, &2)));
+    assert_eq!(map.last_key_value(), Some((&2, &4)));
     assert_eq!(map.remove(&1), Some(2));
+    assert_eq!(map.len(), 1);
+    assert_eq!(map.get(&1), None);
+    assert_eq!(map.get(&2), Some(&4));
+    assert_eq!(map.first_key_value(), Some((&2, &4)));
+    assert_eq!(map.last_key_value(), Some((&2, &4)));
     assert_eq!(map.remove(&2), Some(4));
+    assert_eq!(map.len(), 0);
+    assert_eq!(map.first_key_value(), None);
+    assert_eq!(map.last_key_value(), None);
     assert_eq!(map.remove(&1), None);
 }
 
@@ -605,6 +625,31 @@ fn test_vacant_entry_key() {
     assert_eq!(a[key], value);
 }
 
+#[test]
+fn test_first_last_entry() {
+    let mut a = BTreeMap::new();
+    assert!(a.first_entry().is_none());
+    assert!(a.last_entry().is_none());
+    a.insert(1, 42);
+    assert_eq!(a.first_entry().unwrap().key(), &1);
+    assert_eq!(a.last_entry().unwrap().key(), &1);
+    a.insert(2, 24);
+    assert_eq!(a.first_entry().unwrap().key(), &1);
+    assert_eq!(a.last_entry().unwrap().key(), &2);
+    a.insert(0, 6);
+    assert_eq!(a.first_entry().unwrap().key(), &0);
+    assert_eq!(a.last_entry().unwrap().key(), &2);
+    let (k1, v1) = a.first_entry().unwrap().remove_entry();
+    assert_eq!(k1, 0);
+    assert_eq!(v1, 6);
+    let (k2, v2) = a.last_entry().unwrap().remove_entry();
+    assert_eq!(k2, 2);
+    assert_eq!(v2, 24);
+    assert_eq!(a.first_entry().unwrap().key(), &1);
+    assert_eq!(a.last_entry().unwrap().key(), &1);
+}
+
+
 macro_rules! create_append_test {
     ($name:ident, $len:expr) => {
         #[test]
diff --git a/src/liballoc/tests/btree/set.rs b/src/liballoc/tests/btree/set.rs
index e4883abc8b56c..13cd26280227e 100644
--- a/src/liballoc/tests/btree/set.rs
+++ b/src/liballoc/tests/btree/set.rs
@@ -470,6 +470,34 @@ fn test_append() {
     assert_eq!(a.contains(&5), true);
 }
 
+#[test]
+fn test_first_last() {
+    let mut a = BTreeSet::new();
+    assert_eq!(a.first(), None);
+    assert_eq!(a.last(), None);
+    a.insert(1);
+    assert_eq!(a.first(), Some(&1));
+    assert_eq!(a.last(), Some(&1));
+    a.insert(2);
+    assert_eq!(a.first(), Some(&1));
+    assert_eq!(a.last(), Some(&2));
+    a.insert(3);
+    assert_eq!(a.first(), Some(&1));
+    assert_eq!(a.last(), Some(&3));
+
+    assert_eq!(a.len(), 3);
+    assert_eq!(a.pop_first(), Some(1));
+    assert_eq!(a.len(), 2);
+    assert_eq!(a.pop_last(), Some(3));
+    assert_eq!(a.len(), 1);
+    assert_eq!(a.pop_first(), Some(2));
+    assert_eq!(a.len(), 0);
+    assert_eq!(a.pop_last(), None);
+    assert_eq!(a.len(), 0);
+    assert_eq!(a.pop_first(), None);
+    assert_eq!(a.len(), 0);
+}
+
 fn rand_data(len: usize) -> Vec<u32> {
     let mut rng = DeterministicRng::new();
     Vec::from_iter((0..len).map(|_| rng.next()))
diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs
index 676874c8b27df..ea0f76174e483 100644
--- a/src/liballoc/tests/lib.rs
+++ b/src/liballoc/tests/lib.rs
@@ -2,6 +2,7 @@
 #![feature(box_syntax)]
 #![feature(drain_filter)]
 #![feature(exact_size_is_empty)]
+#![feature(map_first_last)]
 #![feature(new_uninit)]
 #![feature(option_flattening)]
 #![feature(pattern)]