From e12e19db9cd774e408952ecd08ef7da398a33fb7 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 16 Jun 2025 11:04:34 +0200 Subject: [PATCH 01/22] test: add btreemap_api_conformance --- src/btreemap.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/btreemap.rs b/src/btreemap.rs index 02f1f0f8..3a92a420 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -1360,6 +1360,44 @@ mod test { buf } + #[test] + fn btreemap_api_conformance() { + let mem = make_memory(); + let mut stable = BTreeMap::new(mem); + let mut std = std::collections::BTreeMap::new(); + + // Insert. + let (key, value) = (1_u32, "one".to_string()); + stable.insert(key, value.clone()); + std.insert(key, value.clone()); + + // Get. + // Note: stable returns by value, std returns by reference. + assert_eq!(stable.get(&key), std.get(&key).cloned()); + + // Contains key. + assert_eq!(stable.contains_key(&key), std.contains_key(&key)); + + // Remove. + assert_eq!(stable.remove(&key), std.remove(&key)); + + // Length. + // Note: stable returns u64, std returns usize. + assert_eq!(stable.len(), std.len() as u64); + + // Is empty. + assert_eq!(stable.is_empty(), std.is_empty()); + + // Clear + let (key, value) = (2_u32, "two".to_string()); + stable.insert(key, value.clone()); + std.insert(key, value.clone()); + stable.clear_new(); // Note: clear_new is the new method. + std.clear(); + assert_eq!(stable.len(), std.len() as u64); + assert_eq!(stable.is_empty(), std.is_empty()); + } + #[test] fn test_monotonic_buffer() { let cases: &[(u32, [u8; 4])] = &[ From 521feff5b00bb5f85bc8733238e88267537f7e17 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 16 Jun 2025 11:10:30 +0200 Subject: [PATCH 02/22] . --- src/btreemap.rs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index 3a92a420..994736d8 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -1361,40 +1361,38 @@ mod test { } #[test] - fn btreemap_api_conformance() { + fn api_conformance() { let mem = make_memory(); let mut stable = BTreeMap::new(mem); let mut std = std::collections::BTreeMap::new(); + let n = 10_u32; // Insert. - let (key, value) = (1_u32, "one".to_string()); - stable.insert(key, value.clone()); - std.insert(key, value.clone()); + for i in 0..n { + stable.insert(i, format!("{i}")); + std.insert(i, format!("{i}")); + } // Get. // Note: stable returns by value, std returns by reference. - assert_eq!(stable.get(&key), std.get(&key).cloned()); + assert_eq!(stable.get(&1), std.get(&1).cloned()); // Contains key. - assert_eq!(stable.contains_key(&key), std.contains_key(&key)); + assert_eq!(stable.contains_key(&1), std.contains_key(&1)); // Remove. - assert_eq!(stable.remove(&key), std.remove(&key)); + assert_eq!(stable.remove(&1), std.remove(&1)); // Length. // Note: stable returns u64, std returns usize. assert_eq!(stable.len(), std.len() as u64); - // Is empty. - assert_eq!(stable.is_empty(), std.is_empty()); - // Clear - let (key, value) = (2_u32, "two".to_string()); - stable.insert(key, value.clone()); - std.insert(key, value.clone()); stable.clear_new(); // Note: clear_new is the new method. std.clear(); assert_eq!(stable.len(), std.len() as u64); + + // Is empty. assert_eq!(stable.is_empty(), std.is_empty()); } From 2a07f8ad2ae025516cfd9273eec3bff5c850e7f5 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 16 Jun 2025 11:18:17 +0200 Subject: [PATCH 03/22] . --- src/btreemap.rs | 51 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index 994736d8..f7d49a2e 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -1367,33 +1367,58 @@ mod test { let mut std = std::collections::BTreeMap::new(); let n = 10_u32; - // Insert. + // Insert keys and values. for i in 0..n { - stable.insert(i, format!("{i}")); - std.insert(i, format!("{i}")); + let v = i.to_string(); + stable.insert(i, v.clone()); + std.insert(i, v); } - // Get. - // Note: stable returns by value, std returns by reference. + // Get and contains. assert_eq!(stable.get(&1), std.get(&1).cloned()); - - // Contains key. assert_eq!(stable.contains_key(&1), std.contains_key(&1)); // Remove. assert_eq!(stable.remove(&1), std.remove(&1)); - // Length. - // Note: stable returns u64, std returns usize. + // Length and is_empty. assert_eq!(stable.len(), std.len() as u64); + assert_eq!(stable.is_empty(), std.is_empty()); - // Clear - stable.clear_new(); // Note: clear_new is the new method. + // Clear. + stable.clear_new(); // Custom method for stable std.clear(); assert_eq!(stable.len(), std.len() as u64); - - // Is empty. assert_eq!(stable.is_empty(), std.is_empty()); + + // Re-insert to test iteration-related methods. + for i in 0..n { + let v = i.to_string(); + stable.insert(i, v.clone()); + std.insert(i, v); + } + + // Iterators. + let stable_items: Vec<_> = stable.iter().collect(); + let std_items: Vec<_> = std.iter().map(|(k, v)| (*k, v.clone())).collect(); + assert_eq!(stable_items, std_items); + + let stable_keys: Vec<_> = stable.keys().collect(); + let std_keys: Vec<_> = std.keys().copied().collect(); + assert_eq!(stable_keys, std_keys); + + let stable_values: Vec<_> = stable.values().collect(); + let std_values: Vec<_> = std.values().cloned().collect(); + assert_eq!(stable_values, std_values); + + // First and last. + let stable_first = stable.first_key_value(); + let std_first = std.first_key_value().map(|(k, v)| (*k, v.clone())); + assert_eq!(stable_first, std_first); + + let stable_last = stable.last_key_value(); + let std_last = std.last_key_value().map(|(k, v)| (*k, v.clone())); + assert_eq!(stable_last, std_last); } #[test] From 5904b08f8421a73bc474c2be9509a91422d8d90d Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 16 Jun 2025 11:19:22 +0200 Subject: [PATCH 04/22] . --- src/btreemap.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index f7d49a2e..a6460e20 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -1369,9 +1369,8 @@ mod test { // Insert keys and values. for i in 0..n { - let v = i.to_string(); - stable.insert(i, v.clone()); - std.insert(i, v); + stable.insert(i, format!("{i}")); + std.insert(i, format!("{i}")); } // Get and contains. From 0eb63ce1989c17e734746ad138d20e3dd6bc7354 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 16 Jun 2025 11:42:36 +0200 Subject: [PATCH 05/22] . --- src/btreemap.rs | 75 +++++++++++++++++++++++++++++++++------ src/btreemap/proptests.rs | 4 +-- src/btreeset.rs | 44 ++++++++++++----------- src/btreeset/proptests.rs | 4 +-- 4 files changed, 91 insertions(+), 36 deletions(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index a6460e20..3c0ca561 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -1188,16 +1188,26 @@ where self.range_internal(key_range).into() } - /// Returns an iterator pointing to the first element below the given bound. - /// Returns an empty iterator if there are no keys below the given bound. - pub fn iter_upper_bound(&self, bound: &K) -> Iter { - if let Some((start_key, _)) = self.range(..bound).next_back() { - IterInternal::new_in_range(self, (Bound::Included(start_key), Bound::Unbounded)).into() - } else { - IterInternal::null(self).into() + /// Returns an iterator starting from the largest key strictly less than the given bound. + /// The iterator includes that key and continues forward. + /// + /// Returns an empty iterator if no such key exists. + pub fn iter_from_below(&self, bound: &K) -> Iter { + match self.range(..bound).next_back() { + Some((start_key, _)) => { + IterInternal::new_in_range(self, (Bound::Included(start_key), Bound::Unbounded)) + .into() + } + None => IterInternal::null(self).into(), } } + /// **Deprecated**: use [`iter_from_below`] instead. + #[deprecated(note = "use `iter_from_below` instead")] + pub fn iter_upper_bound(&self, bound: &K) -> Iter { + self.iter_from_below(bound) + } + /// Returns an iterator over the keys of the map. pub fn keys(&self) -> KeysIter { self.iter_internal().into() @@ -1418,6 +1428,49 @@ mod test { let stable_last = stable.last_key_value(); let std_last = std.last_key_value().map(|(k, v)| (*k, v.clone())); assert_eq!(stable_last, std_last); + + // Pop first and last. + let stable_pop_first = stable.pop_first(); + let std_pop_first = std.pop_first(); + assert_eq!(stable_pop_first, std_pop_first); + + let stable_pop_last = stable.pop_last(); + let std_pop_last = std.pop_last(); + assert_eq!(stable_pop_last, std_pop_last); + + // Range. + let range_start = 3; + let range_end = 7; + + let stable_range: Vec<_> = stable.range(range_start..range_end).collect(); + let std_range: Vec<_> = std + .range(range_start..range_end) + .map(|(k, v)| (*k, v.clone())) + .collect(); + assert_eq!(stable_range, std_range); + + // keys_range + let stable_keys_range: Vec<_> = stable.keys_range(range_start..range_end).collect(); + let std_keys_range: Vec<_> = std.range(range_start..range_end).map(|(k, _)| *k).collect(); + assert_eq!(stable_keys_range, std_keys_range); + + // values_range + let stable_values_range: Vec<_> = stable.values_range(range_start..range_end).collect(); + let std_values_range: Vec<_> = std + .range(range_start..range_end) + .map(|(_, v)| v.clone()) + .collect(); + assert_eq!(stable_values_range, std_values_range); + + // iter_from_below + let bound = 5; + let stable_result: Vec<_> = stable.iter_from_below(&bound).collect(); + let std_result: Vec<_> = if let Some((start, _)) = std.range(..bound).next_back() { + std.range(start..).map(|(k, v)| (*k, v.clone())).collect() + } else { + Vec::new() + }; + assert_eq!(stable_result, std_result); } #[test] @@ -3015,28 +3068,28 @@ mod test { } btree_test!(test_bruteforce_range_search, bruteforce_range_search); - fn test_iter_upper_bound() { + fn test_iter_from_below() { let (key, value) = (K::build, V::build); run_btree_test(|mut btree| { for j in 0..100 { btree.insert(key(j), value(j)); for i in 0..=j { assert_eq!( - btree.iter_upper_bound(&key(i + 1)).next(), + btree.iter_from_below(&key(i + 1)).next(), Some((key(i), value(i))), "failed to get an upper bound for key({})", i + 1 ); } assert_eq!( - btree.iter_upper_bound(&key(0)).next(), + btree.iter_from_below(&key(0)).next(), None, "key(0) must not have an upper bound" ); } }); } - btree_test!(test_test_iter_upper_bound, test_iter_upper_bound); + btree_test!(test_test_iter_from_below, test_iter_from_below); // A buggy implementation of storable where the max_size is smaller than the serialized size. #[derive(Clone, Ord, PartialOrd, Eq, PartialEq)] diff --git a/src/btreemap/proptests.rs b/src/btreemap/proptests.rs index 020f1bcb..38d0ae9f 100644 --- a/src/btreemap/proptests.rs +++ b/src/btreemap/proptests.rs @@ -141,12 +141,12 @@ fn map_min_max(#[strategy(pvec(any::(), 10..100))] keys: Vec) { } #[proptest] -fn map_upper_bound_iter(#[strategy(pvec(0u64..u64::MAX -1 , 10..100))] keys: Vec) { +fn map_iter_from_below(#[strategy(pvec(0u64..u64::MAX -1 , 10..100))] keys: Vec) { run_btree_test(|mut map| { for k in keys.iter() { map.insert(*k, ()); - prop_assert_eq!(Some((*k, ())), map.iter_upper_bound(&(k + 1)).next()); + prop_assert_eq!(Some((*k, ())), map.iter_from_below(&(k + 1)).next()); } Ok(()) diff --git a/src/btreeset.rs b/src/btreeset.rs index 8be5ba99..f2486f1b 100644 --- a/src/btreeset.rs +++ b/src/btreeset.rs @@ -510,11 +510,13 @@ where Iter::new(self.map.range(key_range)) } - /// Returns an iterator pointing to the first element strictly below the given bound. - /// Returns an empty iterator if there are no keys strictly below the given bound. + /// Returns an iterator starting from the largest element strictly less than the given bound. + /// The iterator includes that element and continues forward. + /// + /// Returns an empty iterator if there are no elements strictly below the given bound. /// /// # Complexity - /// O(log n) for creating the iterator, where n is the number of elements in the set. + /// O(log n) to find the start point, where n is the number of elements. /// /// # Example /// @@ -528,11 +530,11 @@ where /// set.insert(2); /// set.insert(3); /// - /// let upper_bound: Option = set.iter_upper_bound(&3).next(); - /// assert_eq!(upper_bound, Some(2)); + /// let mut iter = set.iter_from_below(&3); + /// assert_eq!(iter.next(), Some(2)); /// ``` - pub fn iter_upper_bound(&self, bound: &K) -> Iter { - Iter::new(self.map.iter_upper_bound(bound)) + pub fn iter_from_below(&self, bound: &K) -> Iter { + Iter::new(self.map.iter_from_below(bound)) } /// Returns an iterator over the union of this set and another. @@ -1111,24 +1113,24 @@ mod test { } #[test] - fn test_iter_upper_bound() { + fn test_iter_from_below() { let mem = make_memory(); let mut btreeset = BTreeSet::new(mem); for i in 0u32..100 { btreeset.insert(i); - // Test that `iter_upper_bound` returns the largest element strictly below the bound. + // Test that `iter_from_below` returns the largest element strictly below the bound. for j in 1u32..=i { assert_eq!( - btreeset.iter_upper_bound(&(j + 1)).next(), + btreeset.iter_from_below(&(j + 1)).next(), Some(j), "failed to get an upper bound for {}", j + 1 ); } assert_eq!( - btreeset.iter_upper_bound(&0).next(), + btreeset.iter_from_below(&0).next(), None, "0 must not have an upper bound" ); @@ -1235,7 +1237,7 @@ mod test { } #[test] - fn test_iter_upper_bound_large_set() { + fn test_iter_from_below_large_set() { let mem = make_memory(); let mut btreeset: BTreeSet = BTreeSet::new(mem); @@ -1243,9 +1245,9 @@ mod test { btreeset.insert(i); } - assert_eq!(btreeset.iter_upper_bound(&500).next(), Some(499)); - assert_eq!(btreeset.iter_upper_bound(&0).next(), None); - assert_eq!(btreeset.iter_upper_bound(&1000).next(), Some(999)); + assert_eq!(btreeset.iter_from_below(&500).next(), Some(499)); + assert_eq!(btreeset.iter_from_below(&0).next(), None); + assert_eq!(btreeset.iter_from_below(&1000).next(), Some(999)); } #[test] @@ -1305,11 +1307,11 @@ mod test { } #[test] - fn test_iter_upper_bound_empty() { + fn test_iter_from_below_empty() { let mem = make_memory(); let btreeset: BTreeSet = BTreeSet::new(mem); - assert_eq!(btreeset.iter_upper_bound(&42u32).next(), None); + assert_eq!(btreeset.iter_from_below(&42u32).next(), None); } #[test] @@ -1415,7 +1417,7 @@ mod test { } #[test] - fn test_iter_upper_bound_edge_cases() { + fn test_iter_from_below_edge_cases() { let mem = make_memory(); let mut btreeset: BTreeSet = BTreeSet::new(mem); @@ -1423,9 +1425,9 @@ mod test { btreeset.insert(i); } - assert_eq!(btreeset.iter_upper_bound(&1).next(), None); // No element strictly below 1 - assert_eq!(btreeset.iter_upper_bound(&5).next(), Some(4)); // Largest element below 5 - assert_eq!(btreeset.iter_upper_bound(&11).next(), Some(10)); // Largest element below 11 + assert_eq!(btreeset.iter_from_below(&1).next(), None); // No element strictly below 1 + assert_eq!(btreeset.iter_from_below(&5).next(), Some(4)); // Largest element below 5 + assert_eq!(btreeset.iter_from_below(&11).next(), Some(10)); // Largest element below 11 } #[test] diff --git a/src/btreeset/proptests.rs b/src/btreeset/proptests.rs index 0a2b4978..767a9349 100644 --- a/src/btreeset/proptests.rs +++ b/src/btreeset/proptests.rs @@ -68,12 +68,12 @@ fn set_min_max(#[strategy(pvec(any::(), 10..100))] keys: Vec) { } #[proptest] -fn set_upper_bound_iter(#[strategy(pvec(0u64..u64::MAX - 1, 10..100))] keys: Vec) { +fn set_iter_from_below(#[strategy(pvec(0u64..u64::MAX - 1, 10..100))] keys: Vec) { crate::btreeset::test::run_btree_test(|mut set| { for k in keys.iter() { set.insert(*k); - prop_assert_eq!(Some(*k), set.iter_upper_bound(&(k + 1)).next()); + prop_assert_eq!(Some(*k), set.iter_from_below(&(k + 1)).next()); } Ok(()) From 395ff488d9cb23eec451abb44f407ea6c754c959 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 16 Jun 2025 11:44:40 +0200 Subject: [PATCH 06/22] . --- src/btreemap.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index 3c0ca561..d6c1a091 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -1193,12 +1193,10 @@ where /// /// Returns an empty iterator if no such key exists. pub fn iter_from_below(&self, bound: &K) -> Iter { - match self.range(..bound).next_back() { - Some((start_key, _)) => { - IterInternal::new_in_range(self, (Bound::Included(start_key), Bound::Unbounded)) - .into() - } - None => IterInternal::null(self).into(), + if let Some((start_key, _)) = self.range(..bound).next_back() { + IterInternal::new_in_range(self, (Bound::Included(start_key), Bound::Unbounded)).into() + } else { + IterInternal::null(self).into() } } From bc186d460dd8508b1d94b73f85e94af7770ba868 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 16 Jun 2025 11:46:22 +0200 Subject: [PATCH 07/22] . --- src/btreemap.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/btreemap.rs b/src/btreemap.rs index d6c1a091..a9156655 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -1201,6 +1201,10 @@ where } /// **Deprecated**: use [`iter_from_below`] instead. + /// + /// This method is renamed for clarity. The name `iter_upper_bound` was misleading, + /// as it actually starts iterating from the largest element *below* the given bound, + /// not up to it. Use [`iter_from_below`] for a clearer, more accurate name. #[deprecated(note = "use `iter_from_below` instead")] pub fn iter_upper_bound(&self, bound: &K) -> Iter { self.iter_from_below(bound) From 8fe56b1b4cda92192e63a3727cc0f5030a16b06a Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 16 Jun 2025 11:51:41 +0200 Subject: [PATCH 08/22] . --- src/btreemap.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index a9156655..73043fd7 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -1386,18 +1386,22 @@ mod test { } // Get and contains. + // Note: stable.get returns owned value (Option), std.get returns reference (Option<&V>). assert_eq!(stable.get(&1), std.get(&1).cloned()); assert_eq!(stable.contains_key(&1), std.contains_key(&1)); // Remove. + // Same return behavior as get: stable returns owned value, std returns owned too. assert_eq!(stable.remove(&1), std.remove(&1)); // Length and is_empty. + // Note: stable.len() returns u64, std.len() returns usize. assert_eq!(stable.len(), std.len() as u64); assert_eq!(stable.is_empty(), std.is_empty()); // Clear. - stable.clear_new(); // Custom method for stable + // Note: stable uses clear_new(); std uses clear(). + stable.clear_new(); std.clear(); assert_eq!(stable.len(), std.len() as u64); assert_eq!(stable.is_empty(), std.is_empty()); @@ -1410,6 +1414,7 @@ mod test { } // Iterators. + // Note: stable.iter() yields (K, V), std.iter() yields (&K, &V) let stable_items: Vec<_> = stable.iter().collect(); let std_items: Vec<_> = std.iter().map(|(k, v)| (*k, v.clone())).collect(); assert_eq!(stable_items, std_items); @@ -1423,6 +1428,7 @@ mod test { assert_eq!(stable_values, std_values); // First and last. + // Note: stable returns (K, V), std returns (&K, &V) let stable_first = stable.first_key_value(); let std_first = std.first_key_value().map(|(k, v)| (*k, v.clone())); assert_eq!(stable_first, std_first); @@ -1432,6 +1438,7 @@ mod test { assert_eq!(stable_last, std_last); // Pop first and last. + // Note: both stable and std return Option<(K, V)> let stable_pop_first = stable.pop_first(); let std_pop_first = std.pop_first(); assert_eq!(stable_pop_first, std_pop_first); @@ -1452,11 +1459,13 @@ mod test { assert_eq!(stable_range, std_range); // keys_range + // Note: stable has a dedicated keys_range() method; std uses map.range().map(|(k, _)| ...) let stable_keys_range: Vec<_> = stable.keys_range(range_start..range_end).collect(); let std_keys_range: Vec<_> = std.range(range_start..range_end).map(|(k, _)| *k).collect(); assert_eq!(stable_keys_range, std_keys_range); // values_range + // Note: stable has a dedicated values_range() method; std uses map.range().map(|(_, v)| ...) let stable_values_range: Vec<_> = stable.values_range(range_start..range_end).collect(); let std_values_range: Vec<_> = std .range(range_start..range_end) @@ -1465,6 +1474,10 @@ mod test { assert_eq!(stable_values_range, std_values_range); // iter_from_below + // Note: stable.iter_from_below(bound) is a custom method. + // It starts from the largest key strictly less than `bound`, and continues forward. + // To simulate in std: use .range(..bound).next_back() to get the largest < bound, + // then iterate from that key forward using .range(start..). let bound = 5; let stable_result: Vec<_> = stable.iter_from_below(&bound).collect(); let std_result: Vec<_> = if let Some((start, _)) = std.range(..bound).next_back() { From d900d2db975bc0b067e5d28f85b4e3882c7c3903 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 16 Jun 2025 18:21:19 +0200 Subject: [PATCH 09/22] cleanup --- src/btreemap.rs | 24 +++++------ src/btreemap/proptests.rs | 4 +- src/btreeset.rs | 88 --------------------------------------- src/btreeset/proptests.rs | 12 ------ 4 files changed, 14 insertions(+), 114 deletions(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index 73043fd7..792a50b8 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -1192,7 +1192,7 @@ where /// The iterator includes that key and continues forward. /// /// Returns an empty iterator if no such key exists. - pub fn iter_from_below(&self, bound: &K) -> Iter { + pub fn iter_from_prev_key(&self, bound: &K) -> Iter { if let Some((start_key, _)) = self.range(..bound).next_back() { IterInternal::new_in_range(self, (Bound::Included(start_key), Bound::Unbounded)).into() } else { @@ -1200,14 +1200,14 @@ where } } - /// **Deprecated**: use [`iter_from_below`] instead. + /// **Deprecated**: use [`iter_from_prev_key`] instead. /// /// This method is renamed for clarity. The name `iter_upper_bound` was misleading, /// as it actually starts iterating from the largest element *below* the given bound, - /// not up to it. Use [`iter_from_below`] for a clearer, more accurate name. - #[deprecated(note = "use `iter_from_below` instead")] + /// not up to it. Use [`iter_from_prev_key`] for a clearer, more accurate name. + #[deprecated(note = "use `iter_from_prev_key` instead")] pub fn iter_upper_bound(&self, bound: &K) -> Iter { - self.iter_from_below(bound) + self.iter_from_prev_key(bound) } /// Returns an iterator over the keys of the map. @@ -1473,13 +1473,13 @@ mod test { .collect(); assert_eq!(stable_values_range, std_values_range); - // iter_from_below - // Note: stable.iter_from_below(bound) is a custom method. + // iter_from_prev_key + // Note: stable.iter_from_prev_key(bound) is a custom method. // It starts from the largest key strictly less than `bound`, and continues forward. // To simulate in std: use .range(..bound).next_back() to get the largest < bound, // then iterate from that key forward using .range(start..). let bound = 5; - let stable_result: Vec<_> = stable.iter_from_below(&bound).collect(); + let stable_result: Vec<_> = stable.iter_from_prev_key(&bound).collect(); let std_result: Vec<_> = if let Some((start, _)) = std.range(..bound).next_back() { std.range(start..).map(|(k, v)| (*k, v.clone())).collect() } else { @@ -3083,28 +3083,28 @@ mod test { } btree_test!(test_bruteforce_range_search, bruteforce_range_search); - fn test_iter_from_below() { + fn test_iter_from_prev_key() { let (key, value) = (K::build, V::build); run_btree_test(|mut btree| { for j in 0..100 { btree.insert(key(j), value(j)); for i in 0..=j { assert_eq!( - btree.iter_from_below(&key(i + 1)).next(), + btree.iter_from_prev_key(&key(i + 1)).next(), Some((key(i), value(i))), "failed to get an upper bound for key({})", i + 1 ); } assert_eq!( - btree.iter_from_below(&key(0)).next(), + btree.iter_from_prev_key(&key(0)).next(), None, "key(0) must not have an upper bound" ); } }); } - btree_test!(test_test_iter_from_below, test_iter_from_below); + btree_test!(test_test_iter_from_prev_key, test_iter_from_prev_key); // A buggy implementation of storable where the max_size is smaller than the serialized size. #[derive(Clone, Ord, PartialOrd, Eq, PartialEq)] diff --git a/src/btreemap/proptests.rs b/src/btreemap/proptests.rs index 38d0ae9f..16c7cc99 100644 --- a/src/btreemap/proptests.rs +++ b/src/btreemap/proptests.rs @@ -141,12 +141,12 @@ fn map_min_max(#[strategy(pvec(any::(), 10..100))] keys: Vec) { } #[proptest] -fn map_iter_from_below(#[strategy(pvec(0u64..u64::MAX -1 , 10..100))] keys: Vec) { +fn map_iter_from_prev_key(#[strategy(pvec(0u64..u64::MAX -1 , 10..100))] keys: Vec) { run_btree_test(|mut map| { for k in keys.iter() { map.insert(*k, ()); - prop_assert_eq!(Some((*k, ())), map.iter_from_below(&(k + 1)).next()); + prop_assert_eq!(Some((*k, ())), map.iter_from_prev_key(&(k + 1)).next()); } Ok(()) diff --git a/src/btreeset.rs b/src/btreeset.rs index f2486f1b..b356d346 100644 --- a/src/btreeset.rs +++ b/src/btreeset.rs @@ -510,33 +510,6 @@ where Iter::new(self.map.range(key_range)) } - /// Returns an iterator starting from the largest element strictly less than the given bound. - /// The iterator includes that element and continues forward. - /// - /// Returns an empty iterator if there are no elements strictly below the given bound. - /// - /// # Complexity - /// O(log n) to find the start point, where n is the number of elements. - /// - /// # Example - /// - /// ```rust - /// use ic_stable_structures::{BTreeSet, DefaultMemoryImpl}; - /// use ic_stable_structures::memory_manager::{MemoryId, MemoryManager}; - /// - /// let mem_mgr = MemoryManager::init(DefaultMemoryImpl::default()); - /// let mut set: BTreeSet = BTreeSet::new(mem_mgr.get(MemoryId::new(0))); - /// set.insert(1); - /// set.insert(2); - /// set.insert(3); - /// - /// let mut iter = set.iter_from_below(&3); - /// assert_eq!(iter.next(), Some(2)); - /// ``` - pub fn iter_from_below(&self, bound: &K) -> Iter { - Iter::new(self.map.iter_from_below(bound)) - } - /// Returns an iterator over the union of this set and another. /// /// The union of two sets is a set containing all elements that are in either set. @@ -1112,31 +1085,6 @@ mod test { assert!(!btreeset.contains(&1u32)); } - #[test] - fn test_iter_from_below() { - let mem = make_memory(); - let mut btreeset = BTreeSet::new(mem); - - for i in 0u32..100 { - btreeset.insert(i); - - // Test that `iter_from_below` returns the largest element strictly below the bound. - for j in 1u32..=i { - assert_eq!( - btreeset.iter_from_below(&(j + 1)).next(), - Some(j), - "failed to get an upper bound for {}", - j + 1 - ); - } - assert_eq!( - btreeset.iter_from_below(&0).next(), - None, - "0 must not have an upper bound" - ); - } - } - #[test] fn test_iter() { let mem = make_memory(); @@ -1236,20 +1184,6 @@ mod test { assert_eq!(elements[999], 999); } - #[test] - fn test_iter_from_below_large_set() { - let mem = make_memory(); - let mut btreeset: BTreeSet = BTreeSet::new(mem); - - for i in 0u32..1000 { - btreeset.insert(i); - } - - assert_eq!(btreeset.iter_from_below(&500).next(), Some(499)); - assert_eq!(btreeset.iter_from_below(&0).next(), None); - assert_eq!(btreeset.iter_from_below(&1000).next(), Some(999)); - } - #[test] fn test_range_large_set() { let mem = make_memory(); @@ -1306,14 +1240,6 @@ mod test { assert_eq!(btreeset.pop_last(), None); } - #[test] - fn test_iter_from_below_empty() { - let mem = make_memory(); - let btreeset: BTreeSet = BTreeSet::new(mem); - - assert_eq!(btreeset.iter_from_below(&42u32).next(), None); - } - #[test] fn test_range_empty() { let mem = make_memory(); @@ -1416,20 +1342,6 @@ mod test { assert!(btreeset.is_empty()); } - #[test] - fn test_iter_from_below_edge_cases() { - let mem = make_memory(); - let mut btreeset: BTreeSet = BTreeSet::new(mem); - - for i in 1..=10 { - btreeset.insert(i); - } - - assert_eq!(btreeset.iter_from_below(&1).next(), None); // No element strictly below 1 - assert_eq!(btreeset.iter_from_below(&5).next(), Some(4)); // Largest element below 5 - assert_eq!(btreeset.iter_from_below(&11).next(), Some(10)); // Largest element below 11 - } - #[test] fn test_is_disjoint_with_disjoint_sets() { let mem1 = make_memory(); diff --git a/src/btreeset/proptests.rs b/src/btreeset/proptests.rs index 767a9349..c8f70364 100644 --- a/src/btreeset/proptests.rs +++ b/src/btreeset/proptests.rs @@ -67,18 +67,6 @@ fn set_min_max(#[strategy(pvec(any::(), 10..100))] keys: Vec) { }); } -#[proptest] -fn set_iter_from_below(#[strategy(pvec(0u64..u64::MAX - 1, 10..100))] keys: Vec) { - crate::btreeset::test::run_btree_test(|mut set| { - for k in keys.iter() { - set.insert(*k); - - prop_assert_eq!(Some(*k), set.iter_from_below(&(k + 1)).next()); - } - - Ok(()) - }); -} // Given an operation, executes it on the given stable btreeset and standard btreeset, verifying // that the result of the operation is equal in both btrees. From 71a9f51135651b00f43b067c30be5744e456a867 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 16 Jun 2025 18:22:35 +0200 Subject: [PATCH 10/22] comment --- src/btreemap.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index 792a50b8..fcebe175 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -1188,10 +1188,12 @@ where self.range_internal(key_range).into() } - /// Returns an iterator starting from the largest key strictly less than the given bound. - /// The iterator includes that key and continues forward. + /// Returns an iterator starting just before the given key. /// - /// Returns an empty iterator if no such key exists. + /// It finds the largest key strictly less than `bound` and starts iteration from there. + /// Useful for stepping back one element and iterating forward, when `range(bound..)` would skip it. + /// + /// Returns an empty iterator if no smaller key exists. pub fn iter_from_prev_key(&self, bound: &K) -> Iter { if let Some((start_key, _)) = self.range(..bound).next_back() { IterInternal::new_in_range(self, (Bound::Included(start_key), Bound::Unbounded)).into() From 09c2390d6a6911a44207e6861cb6f6b95245a2b5 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 16 Jun 2025 18:24:15 +0200 Subject: [PATCH 11/22] comment --- src/btreemap.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index fcebe175..8940b69c 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -1204,9 +1204,11 @@ where /// **Deprecated**: use [`iter_from_prev_key`] instead. /// - /// This method is renamed for clarity. The name `iter_upper_bound` was misleading, - /// as it actually starts iterating from the largest element *below* the given bound, - /// not up to it. Use [`iter_from_prev_key`] for a clearer, more accurate name. + /// The name `iter_upper_bound` was misleading, it suggested an inclusive upper bound, + /// but the method actually starts from the *largest key strictly less than* the given bound. + /// + /// The new name [`iter_from_prev_key`] clearly reflects this behavior, + /// making the code easier to understand. #[deprecated(note = "use `iter_from_prev_key` instead")] pub fn iter_upper_bound(&self, bound: &K) -> Iter { self.iter_from_prev_key(bound) From 4ef62a4950d27c776fd808150d5884b32c462a2b Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 16 Jun 2025 18:27:14 +0200 Subject: [PATCH 12/22] . --- src/btreeset/proptests.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/btreeset/proptests.rs b/src/btreeset/proptests.rs index c8f70364..a698b9fb 100644 --- a/src/btreeset/proptests.rs +++ b/src/btreeset/proptests.rs @@ -67,7 +67,6 @@ fn set_min_max(#[strategy(pvec(any::(), 10..100))] keys: Vec) { }); } - // Given an operation, executes it on the given stable btreeset and standard btreeset, verifying // that the result of the operation is equal in both btrees. fn execute_operation( From 55f152b49d29de58a4549ec5bf18c475dc9c200b Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 16 Jun 2025 18:30:52 +0200 Subject: [PATCH 13/22] . --- src/btreemap.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index 8940b69c..55ad1adb 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -1190,8 +1190,8 @@ where /// Returns an iterator starting just before the given key. /// - /// It finds the largest key strictly less than `bound` and starts iteration from there. - /// Useful for stepping back one element and iterating forward, when `range(bound..)` would skip it. + /// Finds the largest key strictly less than `bound` and starts from it. + /// Useful when `range(bound..)` skips the previous element. /// /// Returns an empty iterator if no smaller key exists. pub fn iter_from_prev_key(&self, bound: &K) -> Iter { @@ -1204,11 +1204,12 @@ where /// **Deprecated**: use [`iter_from_prev_key`] instead. /// - /// The name `iter_upper_bound` was misleading, it suggested an inclusive upper bound, - /// but the method actually starts from the *largest key strictly less than* the given bound. + /// The name `iter_upper_bound` was misleading — it suggested an inclusive + /// upper bound. In reality, it starts from the largest key strictly less + /// than the given bound. /// - /// The new name [`iter_from_prev_key`] clearly reflects this behavior, - /// making the code easier to understand. + /// The new name, [`iter_from_prev_key`], better reflects this behavior and + /// improves code clarity. #[deprecated(note = "use `iter_from_prev_key` instead")] pub fn iter_upper_bound(&self, bound: &K) -> Iter { self.iter_from_prev_key(bound) From cfe78ec90f2cfa07f34ba1996af696e7919aa56c Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Tue, 17 Jun 2025 09:44:50 +0200 Subject: [PATCH 14/22] add vec api_conformance --- src/vec.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/vec.rs b/src/vec.rs index 7e353a7c..4545e0c4 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -98,3 +98,61 @@ impl fmt::Debug for Vec { self.0.fmt(fmt) } } + +#[cfg(test)] +mod test { + use super::*; + use std::cell::RefCell; + use std::rc::Rc; + + /// Creates a new shared memory instance. + fn make_memory() -> Rc>> { + Rc::new(RefCell::new(std::vec::Vec::new())) + } + + #[test] + fn api_conformance() { + let mem = make_memory(); + let stable = Vec::new(mem).unwrap(); + let mut std = std::vec::Vec::new(); + + let n = 10_u32; + + // Push elements. + for i in 0..n { + stable.push(&i).expect("stable.push failed"); + std.push(i); + } + + // Length and emptiness. + assert_eq!(stable.len(), std.len() as u64); + assert_eq!(stable.is_empty(), std.is_empty()); + + // Get by index. + for i in 0..n { + assert_eq!(stable.get(i as u64), Some(std[i as usize])); + } + + // Set by index (stable.set is &mut self, std Vec uses indexing). + for i in 0..n { + let value = i * 10; + stable.set(i as u64, &value); + std[i as usize] = value; + } + + // Confirm updated values. + for i in 0..n { + assert_eq!(stable.get(i as u64), Some(std[i as usize])); + } + + // Pop elements. + for _ in 0..n { + assert_eq!(stable.pop(), std.pop()); + } + + // After popping everything, both should be empty. + assert_eq!(stable.len(), 0); + assert!(stable.is_empty()); + assert!(std.is_empty()); + } +} From 61d851302ab5d22f705d4c293af2ce4346e7986f Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Tue, 17 Jun 2025 09:47:50 +0200 Subject: [PATCH 15/22] . --- src/vec.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/vec.rs b/src/vec.rs index 4545e0c4..3a4de9c4 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -115,25 +115,27 @@ mod test { let mem = make_memory(); let stable = Vec::new(mem).unwrap(); let mut std = std::vec::Vec::new(); - let n = 10_u32; // Push elements. for i in 0..n { - stable.push(&i).expect("stable.push failed"); + stable.push(&i).expect("push failed"); std.push(i); } - // Length and emptiness. + // Length and is_empty. + // Note: stable.len() returns u64, std.len() returns usize. assert_eq!(stable.len(), std.len() as u64); assert_eq!(stable.is_empty(), std.is_empty()); // Get by index. + // Note: stable.get returns Option<&T>, std returns Option<&T>. for i in 0..n { assert_eq!(stable.get(i as u64), Some(std[i as usize])); } - // Set by index (stable.set is &mut self, std Vec uses indexing). + // Set by index. + // Note: stable.set uses &T, std uses indexing. for i in 0..n { let value = i * 10; stable.set(i as u64, &value); @@ -145,14 +147,21 @@ mod test { assert_eq!(stable.get(i as u64), Some(std[i as usize])); } + // TODO: add Copy trait to iter. + // // Iteration. + // // Note: stable.iter() yields &T, std.iter() yields &T. + // let stable_items: Vec<_> = stable.iter().copied().collect(); + // let std_items: Vec<_> = std.iter().copied().collect(); + // assert_eq!(stable_items, std_items); + // Pop elements. + // Note: stable.pop() and std.pop() both return Option. for _ in 0..n { assert_eq!(stable.pop(), std.pop()); } // After popping everything, both should be empty. - assert_eq!(stable.len(), 0); - assert!(stable.is_empty()); - assert!(std.is_empty()); + assert_eq!(stable.len(), std.len() as u64); + assert_eq!(stable.is_empty(), std.is_empty()); } } From c85cf04cb6755e822e542ef2fb977f3b045cdda9 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Tue, 17 Jun 2025 09:53:11 +0200 Subject: [PATCH 16/22] MinHeap api_conformance --- src/min_heap.rs | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/min_heap.rs b/src/min_heap.rs index ecd50d15..f9fbf8eb 100644 --- a/src/min_heap.rs +++ b/src/min_heap.rs @@ -207,3 +207,65 @@ where self.0.fmt(fmt) } } + +#[cfg(test)] +mod test { + use super::*; + use std::cell::RefCell; + use std::cmp::Reverse; + use std::collections::BinaryHeap; + use std::rc::Rc; + + /// Creates a new shared memory instance. + fn make_memory() -> Rc>> { + Rc::new(RefCell::new(std::vec::Vec::new())) + } + + #[test] + fn api_conformance() { + let mem = make_memory(); + let mut stable = MinHeap::new(mem).unwrap(); + let mut std = BinaryHeap::new(); + let n = 10_u32; + + // Push elements. + for i in 0..n { + stable.push(&i).expect("push failed"); + std.push(Reverse(i)); + } + + // Length and is_empty. + // Note: stable.len() returns u64, std.len() returns usize. + assert_eq!(stable.len(), std.len() as u64); + assert_eq!(stable.is_empty(), std.is_empty()); + + // Peek (min element). + // Note: stable.peek() returns Option, std.peek() returns Option<&Reverse>. + assert_eq!(stable.peek(), std.peek().map(|r| r.0)); + + // TODO: add Copy trait to iter. + // // Iteration (unordered for heap). + // // Note: both yield &T, need to sort to compare content. + // let mut stable_items: Vec<_> = stable.iter().copied().collect(); + // let mut std_items: Vec<_> = std.iter().map(|r| r.0).collect(); + // stable_items.sort(); + // std_items.sort(); + // assert_eq!(stable_items, std_items); + + // Pop all elements, should match in ascending order. + let mut stable_popped = Vec::new(); + let mut std_popped = Vec::new(); + while let Some(v) = stable.pop() { + stable_popped.push(v); + } + while let Some(Reverse(v)) = std.pop() { + std_popped.push(v); + } + assert_eq!(stable_popped, std_popped); + + // After popping everything, both should be empty. + assert_eq!(stable.len(), 0); + assert!(stable.is_empty()); + assert!(std.is_empty()); + } +} From 06cbc8a47b75db1e6032d197fe6d20c542b42f05 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Tue, 17 Jun 2025 09:56:07 +0200 Subject: [PATCH 17/22] BTreeSet --- src/btreeset.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/btreeset.rs b/src/btreeset.rs index b356d346..0b174887 100644 --- a/src/btreeset.rs +++ b/src/btreeset.rs @@ -890,6 +890,57 @@ mod test { f(btree); } + #[test] + fn api_conformance() { + let mem = make_memory(); + let mut stable = BTreeSet::new(mem); + let mut std = std::collections::BTreeSet::new(); + let n = 10_u32; + + // Insert elements. + for i in 0..n { + assert_eq!(stable.insert(i), std.insert(i)); + } + + // Contains. + for i in 0..n { + assert_eq!(stable.contains(&i), std.contains(&i)); + } + + // Length and is_empty. + // Note: stable.len() returns u64, std.len() returns usize. + assert_eq!(stable.len(), std.len() as u64); + assert_eq!(stable.is_empty(), std.is_empty()); + + // Iteration. + // Note: stable.iter() yields T, std.iter() yields &T. + let stable_items: Vec<_> = stable.iter().collect(); + let std_items: Vec<_> = std.iter().copied().collect(); + assert_eq!(stable_items, std_items); + + // First and last elements. + // Note: stable.first()/last() returns Option, std.first()/last() returns Option<&T>. + assert_eq!(stable.first(), std.first().copied()); + assert_eq!(stable.last(), std.last().copied()); + + // Range. + let range_start = 3; + let range_end = 7; + let stable_range: Vec<_> = stable.range(range_start..range_end).collect(); + let std_range: Vec<_> = std.range(range_start..range_end).copied().collect(); + assert_eq!(stable_range, std_range); + + // Remove elements. + for i in 0..n { + assert_eq!(stable.remove(&i), std.remove(&i)); + } + + // Should be empty after removals. + assert_eq!(stable.len(), std.len() as u64); + assert!(stable.is_empty()); + assert!(std.is_empty()); + } + #[test] fn test_union_with_duplicates() { let mem1 = make_memory(); From 3f39f6feb8377b022047c36231f67e12ab3e4eb0 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Tue, 17 Jun 2025 10:00:56 +0200 Subject: [PATCH 18/22] . --- src/btreeset.rs | 75 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 9 deletions(-) diff --git a/src/btreeset.rs b/src/btreeset.rs index 0b174887..ddb9ef68 100644 --- a/src/btreeset.rs +++ b/src/btreeset.rs @@ -907,10 +907,15 @@ mod test { assert_eq!(stable.contains(&i), std.contains(&i)); } - // Length and is_empty. + // is_empty and len. // Note: stable.len() returns u64, std.len() returns usize. - assert_eq!(stable.len(), std.len() as u64); assert_eq!(stable.is_empty(), std.is_empty()); + assert_eq!(stable.len(), std.len() as u64); + + // First and last. + // Note: stable.first()/last() returns Option, std returns Option<&T>. + assert_eq!(stable.first(), std.first().copied()); + assert_eq!(stable.last(), std.last().copied()); // Iteration. // Note: stable.iter() yields T, std.iter() yields &T. @@ -918,11 +923,6 @@ mod test { let std_items: Vec<_> = std.iter().copied().collect(); assert_eq!(stable_items, std_items); - // First and last elements. - // Note: stable.first()/last() returns Option, std.first()/last() returns Option<&T>. - assert_eq!(stable.first(), std.first().copied()); - assert_eq!(stable.last(), std.last().copied()); - // Range. let range_start = 3; let range_end = 7; @@ -930,15 +930,72 @@ mod test { let std_range: Vec<_> = std.range(range_start..range_end).copied().collect(); assert_eq!(stable_range, std_range); + // pop_first / pop_last. + let mem2 = make_memory(); + let mut stable_temp = BTreeSet::new(mem2); + let mut std_temp = std::collections::BTreeSet::new(); + for i in 0..n { + assert_eq!(stable_temp.insert(i), std_temp.insert(i)); + } + + assert_eq!(stable_temp.pop_first(), std_temp.pop_first()); + assert_eq!(stable_temp.pop_last(), std_temp.pop_last()); + // Remove elements. for i in 0..n { assert_eq!(stable.remove(&i), std.remove(&i)); } + assert!(stable.is_empty()); + assert!(std.is_empty()); - // Should be empty after removals. - assert_eq!(stable.len(), std.len() as u64); + // Clear. + for i in 0..n { + stable.insert(i); + std.insert(i); + } + stable.clear(); + std.clear(); assert!(stable.is_empty()); assert!(std.is_empty()); + + // Reinsert for set operations. + for i in 0..n { + if i % 2 == 0 { + stable.insert(i); + std.insert(i); + } + } + + let mem2 = make_memory(); + let mut stable2 = BTreeSet::new(mem2); + let mut std2 = std::collections::BTreeSet::new(); + + for i in 0..n { + if i % 3 == 0 { + stable2.insert(i); + std2.insert(i); + } + } + + // is_disjoint, is_subset, is_superset. + assert_eq!(stable.is_disjoint(&stable2), std.is_disjoint(&std2)); + assert_eq!(stable.is_subset(&stable2), std.is_subset(&std2)); + assert_eq!(stable.is_superset(&stable2), std.is_superset(&std2)); + + // union + let stable_union: Vec<_> = stable.union(&stable2).collect(); + let std_union: Vec<_> = std.union(&std2).copied().collect(); + assert_eq!(stable_union, std_union); + + // intersection + let stable_inter: Vec<_> = stable.intersection(&stable2).collect(); + let std_inter: Vec<_> = std.intersection(&std2).copied().collect(); + assert_eq!(stable_inter, std_inter); + + // symmetric_difference + let stable_diff: Vec<_> = stable.symmetric_difference(&stable2).collect(); + let std_diff: Vec<_> = std.symmetric_difference(&std2).copied().collect(); + assert_eq!(stable_diff, std_diff); } #[test] From 1214cf5401442c663b4dd5e6ea3883f869a0d06a Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Tue, 17 Jun 2025 10:15:25 +0200 Subject: [PATCH 19/22] relocate files --- src/api_conformance/btreemap.rs | 118 ++++++++++++++++++++++++++++++++ src/api_conformance/btreeset.rs | 110 +++++++++++++++++++++++++++++ src/api_conformance/min_heap.rs | 52 ++++++++++++++ src/api_conformance/mod.rs | 19 +++++ src/api_conformance/vec.rs | 57 +++++++++++++++ src/btreemap.rs | 116 ------------------------------- src/btreeset.rs | 108 ----------------------------- src/lib.rs | 11 +-- src/min_heap.rs | 62 ----------------- src/vec.rs | 67 ------------------ 10 files changed, 363 insertions(+), 357 deletions(-) create mode 100644 src/api_conformance/btreemap.rs create mode 100644 src/api_conformance/btreeset.rs create mode 100644 src/api_conformance/min_heap.rs create mode 100644 src/api_conformance/mod.rs create mode 100644 src/api_conformance/vec.rs diff --git a/src/api_conformance/btreemap.rs b/src/api_conformance/btreemap.rs new file mode 100644 index 00000000..cdea662a --- /dev/null +++ b/src/api_conformance/btreemap.rs @@ -0,0 +1,118 @@ +use crate::api_conformance::make_memory; +use crate::btreemap::BTreeMap; + +#[test] +fn api_conformance() { + let mem = make_memory(); + let mut stable = BTreeMap::new(mem); + let mut std = std::collections::BTreeMap::new(); + let n = 10_u32; + + // Insert keys and values. + for i in 0..n { + stable.insert(i, format!("{i}")); + std.insert(i, format!("{i}")); + } + + // Get and contains. + // Note: stable.get returns owned value (Option), std.get returns reference (Option<&V>). + assert_eq!(stable.get(&1), std.get(&1).cloned()); + assert_eq!(stable.contains_key(&1), std.contains_key(&1)); + + // Remove. + // Same return behavior as get: stable returns owned value, std returns owned too. + assert_eq!(stable.remove(&1), std.remove(&1)); + + // Length and is_empty. + // Note: stable.len() returns u64, std.len() returns usize. + assert_eq!(stable.len(), std.len() as u64); + assert_eq!(stable.is_empty(), std.is_empty()); + + // Clear. + // Note: stable uses clear_new(); std uses clear(). + stable.clear_new(); + std.clear(); + assert_eq!(stable.len(), std.len() as u64); + assert_eq!(stable.is_empty(), std.is_empty()); + + // Re-insert to test iteration-related methods. + for i in 0..n { + let v = i.to_string(); + stable.insert(i, v.clone()); + std.insert(i, v); + } + + // Iterators. + // Note: stable.iter() yields (K, V), std.iter() yields (&K, &V) + let stable_items: Vec<_> = stable.iter().collect(); + let std_items: Vec<_> = std.iter().map(|(k, v)| (*k, v.clone())).collect(); + assert_eq!(stable_items, std_items); + + let stable_keys: Vec<_> = stable.keys().collect(); + let std_keys: Vec<_> = std.keys().copied().collect(); + assert_eq!(stable_keys, std_keys); + + let stable_values: Vec<_> = stable.values().collect(); + let std_values: Vec<_> = std.values().cloned().collect(); + assert_eq!(stable_values, std_values); + + // First and last. + // Note: stable returns (K, V), std returns (&K, &V) + let stable_first = stable.first_key_value(); + let std_first = std.first_key_value().map(|(k, v)| (*k, v.clone())); + assert_eq!(stable_first, std_first); + + let stable_last = stable.last_key_value(); + let std_last = std.last_key_value().map(|(k, v)| (*k, v.clone())); + assert_eq!(stable_last, std_last); + + // Pop first and last. + // Note: both stable and std return Option<(K, V)> + let stable_pop_first = stable.pop_first(); + let std_pop_first = std.pop_first(); + assert_eq!(stable_pop_first, std_pop_first); + + let stable_pop_last = stable.pop_last(); + let std_pop_last = std.pop_last(); + assert_eq!(stable_pop_last, std_pop_last); + + // Range. + let range_start = 3; + let range_end = 7; + + let stable_range: Vec<_> = stable.range(range_start..range_end).collect(); + let std_range: Vec<_> = std + .range(range_start..range_end) + .map(|(k, v)| (*k, v.clone())) + .collect(); + assert_eq!(stable_range, std_range); + + // keys_range + // Note: stable has a dedicated keys_range() method; std uses map.range().map(|(k, _)| ...) + let stable_keys_range: Vec<_> = stable.keys_range(range_start..range_end).collect(); + let std_keys_range: Vec<_> = std.range(range_start..range_end).map(|(k, _)| *k).collect(); + assert_eq!(stable_keys_range, std_keys_range); + + // values_range + // Note: stable has a dedicated values_range() method; std uses map.range().map(|(_, v)| ...) + let stable_values_range: Vec<_> = stable.values_range(range_start..range_end).collect(); + let std_values_range: Vec<_> = std + .range(range_start..range_end) + .map(|(_, v)| v.clone()) + .collect(); + assert_eq!(stable_values_range, std_values_range); + + // iter_from_prev_key + // Note: stable.iter_from_prev_key(bound) is a custom method. + // It starts from the largest key strictly less than `bound`, and continues forward. + // To simulate in std: use .range(..bound).next_back() to get the largest < bound, + // then iterate from that key forward using .range(start..). + let bound = 5; + let stable_result: Vec<_> = stable.iter_from_prev_key(&bound).collect(); + let std_result: Vec<_> = if let Some((start, _)) = std.range(..bound).next_back() { + std.range(start..).map(|(k, v)| (*k, v.clone())).collect() + } else { + Vec::new() + }; + assert_eq!(stable_result, std_result); +} diff --git a/src/api_conformance/btreeset.rs b/src/api_conformance/btreeset.rs new file mode 100644 index 00000000..c3d3e11b --- /dev/null +++ b/src/api_conformance/btreeset.rs @@ -0,0 +1,110 @@ +use crate::api_conformance::make_memory; +use crate::btreeset::BTreeSet; + +#[test] +fn api_conformance() { + let mem = make_memory(); + let mut stable = BTreeSet::new(mem); + let mut std = std::collections::BTreeSet::new(); + let n = 10_u32; + + // Insert elements. + for i in 0..n { + assert_eq!(stable.insert(i), std.insert(i)); + } + + // Contains. + for i in 0..n { + assert_eq!(stable.contains(&i), std.contains(&i)); + } + + // is_empty and len. + // Note: stable.len() returns u64, std.len() returns usize. + assert_eq!(stable.is_empty(), std.is_empty()); + assert_eq!(stable.len(), std.len() as u64); + + // First and last. + // Note: stable.first()/last() returns Option, std returns Option<&T>. + assert_eq!(stable.first(), std.first().copied()); + assert_eq!(stable.last(), std.last().copied()); + + // Iteration. + // Note: stable.iter() yields T, std.iter() yields &T. + let stable_items: Vec<_> = stable.iter().collect(); + let std_items: Vec<_> = std.iter().copied().collect(); + assert_eq!(stable_items, std_items); + + // Range. + let range_start = 3; + let range_end = 7; + let stable_range: Vec<_> = stable.range(range_start..range_end).collect(); + let std_range: Vec<_> = std.range(range_start..range_end).copied().collect(); + assert_eq!(stable_range, std_range); + + // pop_first / pop_last. + let mem2 = make_memory(); + let mut stable_temp = BTreeSet::new(mem2); + let mut std_temp = std::collections::BTreeSet::new(); + for i in 0..n { + assert_eq!(stable_temp.insert(i), std_temp.insert(i)); + } + + assert_eq!(stable_temp.pop_first(), std_temp.pop_first()); + assert_eq!(stable_temp.pop_last(), std_temp.pop_last()); + + // Remove elements. + for i in 0..n { + assert_eq!(stable.remove(&i), std.remove(&i)); + } + assert!(stable.is_empty()); + assert!(std.is_empty()); + + // Clear. + for i in 0..n { + stable.insert(i); + std.insert(i); + } + stable.clear(); + std.clear(); + assert!(stable.is_empty()); + assert!(std.is_empty()); + + // Reinsert for set operations. + for i in 0..n { + if i % 2 == 0 { + stable.insert(i); + std.insert(i); + } + } + + let mem2 = make_memory(); + let mut stable2 = BTreeSet::new(mem2); + let mut std2 = std::collections::BTreeSet::new(); + + for i in 0..n { + if i % 3 == 0 { + stable2.insert(i); + std2.insert(i); + } + } + + // is_disjoint, is_subset, is_superset. + assert_eq!(stable.is_disjoint(&stable2), std.is_disjoint(&std2)); + assert_eq!(stable.is_subset(&stable2), std.is_subset(&std2)); + assert_eq!(stable.is_superset(&stable2), std.is_superset(&std2)); + + // union + let stable_union: Vec<_> = stable.union(&stable2).collect(); + let std_union: Vec<_> = std.union(&std2).copied().collect(); + assert_eq!(stable_union, std_union); + + // intersection + let stable_inter: Vec<_> = stable.intersection(&stable2).collect(); + let std_inter: Vec<_> = std.intersection(&std2).copied().collect(); + assert_eq!(stable_inter, std_inter); + + // symmetric_difference + let stable_diff: Vec<_> = stable.symmetric_difference(&stable2).collect(); + let std_diff: Vec<_> = std.symmetric_difference(&std2).copied().collect(); + assert_eq!(stable_diff, std_diff); +} diff --git a/src/api_conformance/min_heap.rs b/src/api_conformance/min_heap.rs new file mode 100644 index 00000000..0324fb8e --- /dev/null +++ b/src/api_conformance/min_heap.rs @@ -0,0 +1,52 @@ +use crate::api_conformance::make_memory; +use crate::min_heap::MinHeap; +use std::cmp::Reverse; +use std::collections::BinaryHeap; + +#[test] +fn api_conformance() { + let mem = make_memory(); + let mut stable = MinHeap::new(mem).unwrap(); + let mut std = BinaryHeap::new(); + let n = 10_u32; + + // Push elements. + for i in 0..n { + stable.push(&i).expect("push failed"); + std.push(Reverse(i)); + } + + // Length and is_empty. + // Note: stable.len() returns u64, std.len() returns usize. + assert_eq!(stable.len(), std.len() as u64); + assert_eq!(stable.is_empty(), std.is_empty()); + + // Peek (min element). + // Note: stable.peek() returns Option, std.peek() returns Option<&Reverse>. + assert_eq!(stable.peek(), std.peek().map(|r| r.0)); + + // TODO: add Copy trait to iter. + // // Iteration (unordered for heap). + // // Note: both yield &T, need to sort to compare content. + // let mut stable_items: Vec<_> = stable.iter().copied().collect(); + // let mut std_items: Vec<_> = std.iter().map(|r| r.0).collect(); + // stable_items.sort(); + // std_items.sort(); + // assert_eq!(stable_items, std_items); + + // Pop all elements, should match in ascending order. + let mut stable_popped = Vec::new(); + let mut std_popped = Vec::new(); + while let Some(v) = stable.pop() { + stable_popped.push(v); + } + while let Some(Reverse(v)) = std.pop() { + std_popped.push(v); + } + assert_eq!(stable_popped, std_popped); + + // After popping everything, both should be empty. + assert_eq!(stable.len(), 0); + assert!(stable.is_empty()); + assert!(std.is_empty()); +} diff --git a/src/api_conformance/mod.rs b/src/api_conformance/mod.rs new file mode 100644 index 00000000..74b5c2a6 --- /dev/null +++ b/src/api_conformance/mod.rs @@ -0,0 +1,19 @@ +use std::cell::RefCell; +use std::rc::Rc; + +#[cfg(test)] +mod btreemap; + +#[cfg(test)] +mod btreeset; + +#[cfg(test)] +mod min_heap; + +#[cfg(test)] +mod vec; + +#[cfg(test)] +pub(crate) fn make_memory() -> Rc>> { + Rc::new(RefCell::new(std::vec::Vec::new())) +} diff --git a/src/api_conformance/vec.rs b/src/api_conformance/vec.rs new file mode 100644 index 00000000..93a8a670 --- /dev/null +++ b/src/api_conformance/vec.rs @@ -0,0 +1,57 @@ +use crate::api_conformance::make_memory; +use crate::vec::Vec; + +#[test] +fn api_conformance() { + let mem = make_memory(); + let stable = Vec::new(mem).unwrap(); + let mut std = std::vec::Vec::new(); + let n = 10_u32; + + // Push elements. + for i in 0..n { + stable.push(&i).expect("push failed"); + std.push(i); + } + + // Length and is_empty. + // Note: stable.len() returns u64, std.len() returns usize. + assert_eq!(stable.len(), std.len() as u64); + assert_eq!(stable.is_empty(), std.is_empty()); + + // Get by index. + // Note: stable.get returns Option<&T>, std returns Option<&T>. + for i in 0..n { + assert_eq!(stable.get(i as u64), Some(std[i as usize])); + } + + // Set by index. + // Note: stable.set uses &T, std uses indexing. + for i in 0..n { + let value = i * 10; + stable.set(i as u64, &value); + std[i as usize] = value; + } + + // Confirm updated values. + for i in 0..n { + assert_eq!(stable.get(i as u64), Some(std[i as usize])); + } + + // TODO: add Copy trait to iter. + // // Iteration. + // // Note: stable.iter() yields &T, std.iter() yields &T. + // let stable_items: Vec<_> = stable.iter().copied().collect(); + // let std_items: Vec<_> = std.iter().copied().collect(); + // assert_eq!(stable_items, std_items); + + // Pop elements. + // Note: stable.pop() and std.pop() both return Option. + for _ in 0..n { + assert_eq!(stable.pop(), std.pop()); + } + + // After popping everything, both should be empty. + assert_eq!(stable.len(), std.len() as u64); + assert_eq!(stable.is_empty(), std.is_empty()); +} diff --git a/src/btreemap.rs b/src/btreemap.rs index 55ad1adb..b5532522 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -1377,122 +1377,6 @@ mod test { buf } - #[test] - fn api_conformance() { - let mem = make_memory(); - let mut stable = BTreeMap::new(mem); - let mut std = std::collections::BTreeMap::new(); - let n = 10_u32; - - // Insert keys and values. - for i in 0..n { - stable.insert(i, format!("{i}")); - std.insert(i, format!("{i}")); - } - - // Get and contains. - // Note: stable.get returns owned value (Option), std.get returns reference (Option<&V>). - assert_eq!(stable.get(&1), std.get(&1).cloned()); - assert_eq!(stable.contains_key(&1), std.contains_key(&1)); - - // Remove. - // Same return behavior as get: stable returns owned value, std returns owned too. - assert_eq!(stable.remove(&1), std.remove(&1)); - - // Length and is_empty. - // Note: stable.len() returns u64, std.len() returns usize. - assert_eq!(stable.len(), std.len() as u64); - assert_eq!(stable.is_empty(), std.is_empty()); - - // Clear. - // Note: stable uses clear_new(); std uses clear(). - stable.clear_new(); - std.clear(); - assert_eq!(stable.len(), std.len() as u64); - assert_eq!(stable.is_empty(), std.is_empty()); - - // Re-insert to test iteration-related methods. - for i in 0..n { - let v = i.to_string(); - stable.insert(i, v.clone()); - std.insert(i, v); - } - - // Iterators. - // Note: stable.iter() yields (K, V), std.iter() yields (&K, &V) - let stable_items: Vec<_> = stable.iter().collect(); - let std_items: Vec<_> = std.iter().map(|(k, v)| (*k, v.clone())).collect(); - assert_eq!(stable_items, std_items); - - let stable_keys: Vec<_> = stable.keys().collect(); - let std_keys: Vec<_> = std.keys().copied().collect(); - assert_eq!(stable_keys, std_keys); - - let stable_values: Vec<_> = stable.values().collect(); - let std_values: Vec<_> = std.values().cloned().collect(); - assert_eq!(stable_values, std_values); - - // First and last. - // Note: stable returns (K, V), std returns (&K, &V) - let stable_first = stable.first_key_value(); - let std_first = std.first_key_value().map(|(k, v)| (*k, v.clone())); - assert_eq!(stable_first, std_first); - - let stable_last = stable.last_key_value(); - let std_last = std.last_key_value().map(|(k, v)| (*k, v.clone())); - assert_eq!(stable_last, std_last); - - // Pop first and last. - // Note: both stable and std return Option<(K, V)> - let stable_pop_first = stable.pop_first(); - let std_pop_first = std.pop_first(); - assert_eq!(stable_pop_first, std_pop_first); - - let stable_pop_last = stable.pop_last(); - let std_pop_last = std.pop_last(); - assert_eq!(stable_pop_last, std_pop_last); - - // Range. - let range_start = 3; - let range_end = 7; - - let stable_range: Vec<_> = stable.range(range_start..range_end).collect(); - let std_range: Vec<_> = std - .range(range_start..range_end) - .map(|(k, v)| (*k, v.clone())) - .collect(); - assert_eq!(stable_range, std_range); - - // keys_range - // Note: stable has a dedicated keys_range() method; std uses map.range().map(|(k, _)| ...) - let stable_keys_range: Vec<_> = stable.keys_range(range_start..range_end).collect(); - let std_keys_range: Vec<_> = std.range(range_start..range_end).map(|(k, _)| *k).collect(); - assert_eq!(stable_keys_range, std_keys_range); - - // values_range - // Note: stable has a dedicated values_range() method; std uses map.range().map(|(_, v)| ...) - let stable_values_range: Vec<_> = stable.values_range(range_start..range_end).collect(); - let std_values_range: Vec<_> = std - .range(range_start..range_end) - .map(|(_, v)| v.clone()) - .collect(); - assert_eq!(stable_values_range, std_values_range); - - // iter_from_prev_key - // Note: stable.iter_from_prev_key(bound) is a custom method. - // It starts from the largest key strictly less than `bound`, and continues forward. - // To simulate in std: use .range(..bound).next_back() to get the largest < bound, - // then iterate from that key forward using .range(start..). - let bound = 5; - let stable_result: Vec<_> = stable.iter_from_prev_key(&bound).collect(); - let std_result: Vec<_> = if let Some((start, _)) = std.range(..bound).next_back() { - std.range(start..).map(|(k, v)| (*k, v.clone())).collect() - } else { - Vec::new() - }; - assert_eq!(stable_result, std_result); - } - #[test] fn test_monotonic_buffer() { let cases: &[(u32, [u8; 4])] = &[ diff --git a/src/btreeset.rs b/src/btreeset.rs index ddb9ef68..b356d346 100644 --- a/src/btreeset.rs +++ b/src/btreeset.rs @@ -890,114 +890,6 @@ mod test { f(btree); } - #[test] - fn api_conformance() { - let mem = make_memory(); - let mut stable = BTreeSet::new(mem); - let mut std = std::collections::BTreeSet::new(); - let n = 10_u32; - - // Insert elements. - for i in 0..n { - assert_eq!(stable.insert(i), std.insert(i)); - } - - // Contains. - for i in 0..n { - assert_eq!(stable.contains(&i), std.contains(&i)); - } - - // is_empty and len. - // Note: stable.len() returns u64, std.len() returns usize. - assert_eq!(stable.is_empty(), std.is_empty()); - assert_eq!(stable.len(), std.len() as u64); - - // First and last. - // Note: stable.first()/last() returns Option, std returns Option<&T>. - assert_eq!(stable.first(), std.first().copied()); - assert_eq!(stable.last(), std.last().copied()); - - // Iteration. - // Note: stable.iter() yields T, std.iter() yields &T. - let stable_items: Vec<_> = stable.iter().collect(); - let std_items: Vec<_> = std.iter().copied().collect(); - assert_eq!(stable_items, std_items); - - // Range. - let range_start = 3; - let range_end = 7; - let stable_range: Vec<_> = stable.range(range_start..range_end).collect(); - let std_range: Vec<_> = std.range(range_start..range_end).copied().collect(); - assert_eq!(stable_range, std_range); - - // pop_first / pop_last. - let mem2 = make_memory(); - let mut stable_temp = BTreeSet::new(mem2); - let mut std_temp = std::collections::BTreeSet::new(); - for i in 0..n { - assert_eq!(stable_temp.insert(i), std_temp.insert(i)); - } - - assert_eq!(stable_temp.pop_first(), std_temp.pop_first()); - assert_eq!(stable_temp.pop_last(), std_temp.pop_last()); - - // Remove elements. - for i in 0..n { - assert_eq!(stable.remove(&i), std.remove(&i)); - } - assert!(stable.is_empty()); - assert!(std.is_empty()); - - // Clear. - for i in 0..n { - stable.insert(i); - std.insert(i); - } - stable.clear(); - std.clear(); - assert!(stable.is_empty()); - assert!(std.is_empty()); - - // Reinsert for set operations. - for i in 0..n { - if i % 2 == 0 { - stable.insert(i); - std.insert(i); - } - } - - let mem2 = make_memory(); - let mut stable2 = BTreeSet::new(mem2); - let mut std2 = std::collections::BTreeSet::new(); - - for i in 0..n { - if i % 3 == 0 { - stable2.insert(i); - std2.insert(i); - } - } - - // is_disjoint, is_subset, is_superset. - assert_eq!(stable.is_disjoint(&stable2), std.is_disjoint(&std2)); - assert_eq!(stable.is_subset(&stable2), std.is_subset(&std2)); - assert_eq!(stable.is_superset(&stable2), std.is_superset(&std2)); - - // union - let stable_union: Vec<_> = stable.union(&stable2).collect(); - let std_union: Vec<_> = std.union(&std2).copied().collect(); - assert_eq!(stable_union, std_union); - - // intersection - let stable_inter: Vec<_> = stable.intersection(&stable2).collect(); - let std_inter: Vec<_> = std.intersection(&std2).copied().collect(); - assert_eq!(stable_inter, std_inter); - - // symmetric_difference - let stable_diff: Vec<_> = stable.symmetric_difference(&stable2).collect(); - let std_diff: Vec<_> = std.symmetric_difference(&std2).copied().collect(); - assert_eq!(stable_diff, std_diff); - } - #[test] fn test_union_with_duplicates() { let mem1 = make_memory(); diff --git a/src/lib.rs b/src/lib.rs index e528dd4b..6c963f39 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,12 +3,13 @@ mod base_vec; pub mod btreemap; pub mod cell; pub use cell::{Cell as StableCell, Cell}; +#[cfg(test)] +mod api_conformance; +pub mod btreeset; pub mod file_mem; #[cfg(target_arch = "wasm32")] mod ic0_memory; // Memory API for canisters. pub mod log; -pub use log::{Log as StableLog, Log}; -pub mod btreeset; pub mod memory_manager; pub mod min_heap; pub mod reader; @@ -17,20 +18,22 @@ pub mod storable; mod tests; mod types; pub mod vec; -pub use min_heap::{MinHeap, MinHeap as StableMinHeap}; -pub use vec::{Vec as StableVec, Vec}; pub mod vec_mem; pub mod writer; + pub use btreemap::{BTreeMap, BTreeMap as StableBTreeMap}; pub use btreeset::{BTreeSet, BTreeSet as StableBTreeSet}; pub use file_mem::FileMemory; #[cfg(target_arch = "wasm32")] pub use ic0_memory::Ic0StableMemory; +pub use log::{Log as StableLog, Log}; +pub use min_heap::{MinHeap, MinHeap as StableMinHeap}; use std::error; use std::fmt::{Display, Formatter}; use std::mem::MaybeUninit; pub use storable::Storable; use types::Address; +pub use vec::{Vec as StableVec, Vec}; pub use vec_mem::VectorMemory; #[cfg(target_arch = "wasm32")] diff --git a/src/min_heap.rs b/src/min_heap.rs index f9fbf8eb..ecd50d15 100644 --- a/src/min_heap.rs +++ b/src/min_heap.rs @@ -207,65 +207,3 @@ where self.0.fmt(fmt) } } - -#[cfg(test)] -mod test { - use super::*; - use std::cell::RefCell; - use std::cmp::Reverse; - use std::collections::BinaryHeap; - use std::rc::Rc; - - /// Creates a new shared memory instance. - fn make_memory() -> Rc>> { - Rc::new(RefCell::new(std::vec::Vec::new())) - } - - #[test] - fn api_conformance() { - let mem = make_memory(); - let mut stable = MinHeap::new(mem).unwrap(); - let mut std = BinaryHeap::new(); - let n = 10_u32; - - // Push elements. - for i in 0..n { - stable.push(&i).expect("push failed"); - std.push(Reverse(i)); - } - - // Length and is_empty. - // Note: stable.len() returns u64, std.len() returns usize. - assert_eq!(stable.len(), std.len() as u64); - assert_eq!(stable.is_empty(), std.is_empty()); - - // Peek (min element). - // Note: stable.peek() returns Option, std.peek() returns Option<&Reverse>. - assert_eq!(stable.peek(), std.peek().map(|r| r.0)); - - // TODO: add Copy trait to iter. - // // Iteration (unordered for heap). - // // Note: both yield &T, need to sort to compare content. - // let mut stable_items: Vec<_> = stable.iter().copied().collect(); - // let mut std_items: Vec<_> = std.iter().map(|r| r.0).collect(); - // stable_items.sort(); - // std_items.sort(); - // assert_eq!(stable_items, std_items); - - // Pop all elements, should match in ascending order. - let mut stable_popped = Vec::new(); - let mut std_popped = Vec::new(); - while let Some(v) = stable.pop() { - stable_popped.push(v); - } - while let Some(Reverse(v)) = std.pop() { - std_popped.push(v); - } - assert_eq!(stable_popped, std_popped); - - // After popping everything, both should be empty. - assert_eq!(stable.len(), 0); - assert!(stable.is_empty()); - assert!(std.is_empty()); - } -} diff --git a/src/vec.rs b/src/vec.rs index 3a4de9c4..7e353a7c 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -98,70 +98,3 @@ impl fmt::Debug for Vec { self.0.fmt(fmt) } } - -#[cfg(test)] -mod test { - use super::*; - use std::cell::RefCell; - use std::rc::Rc; - - /// Creates a new shared memory instance. - fn make_memory() -> Rc>> { - Rc::new(RefCell::new(std::vec::Vec::new())) - } - - #[test] - fn api_conformance() { - let mem = make_memory(); - let stable = Vec::new(mem).unwrap(); - let mut std = std::vec::Vec::new(); - let n = 10_u32; - - // Push elements. - for i in 0..n { - stable.push(&i).expect("push failed"); - std.push(i); - } - - // Length and is_empty. - // Note: stable.len() returns u64, std.len() returns usize. - assert_eq!(stable.len(), std.len() as u64); - assert_eq!(stable.is_empty(), std.is_empty()); - - // Get by index. - // Note: stable.get returns Option<&T>, std returns Option<&T>. - for i in 0..n { - assert_eq!(stable.get(i as u64), Some(std[i as usize])); - } - - // Set by index. - // Note: stable.set uses &T, std uses indexing. - for i in 0..n { - let value = i * 10; - stable.set(i as u64, &value); - std[i as usize] = value; - } - - // Confirm updated values. - for i in 0..n { - assert_eq!(stable.get(i as u64), Some(std[i as usize])); - } - - // TODO: add Copy trait to iter. - // // Iteration. - // // Note: stable.iter() yields &T, std.iter() yields &T. - // let stable_items: Vec<_> = stable.iter().copied().collect(); - // let std_items: Vec<_> = std.iter().copied().collect(); - // assert_eq!(stable_items, std_items); - - // Pop elements. - // Note: stable.pop() and std.pop() both return Option. - for _ in 0..n { - assert_eq!(stable.pop(), std.pop()); - } - - // After popping everything, both should be empty. - assert_eq!(stable.len(), std.len() as u64); - assert_eq!(stable.is_empty(), std.is_empty()); - } -} From 32febf7a3596bc38624a2e4cdaab1322a8aef3fc Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Tue, 17 Jun 2025 10:20:18 +0200 Subject: [PATCH 20/22] rm comment --- src/api_conformance/min_heap.rs | 9 --------- src/api_conformance/vec.rs | 7 ------- 2 files changed, 16 deletions(-) diff --git a/src/api_conformance/min_heap.rs b/src/api_conformance/min_heap.rs index 0324fb8e..dbc2d67a 100644 --- a/src/api_conformance/min_heap.rs +++ b/src/api_conformance/min_heap.rs @@ -25,15 +25,6 @@ fn api_conformance() { // Note: stable.peek() returns Option, std.peek() returns Option<&Reverse>. assert_eq!(stable.peek(), std.peek().map(|r| r.0)); - // TODO: add Copy trait to iter. - // // Iteration (unordered for heap). - // // Note: both yield &T, need to sort to compare content. - // let mut stable_items: Vec<_> = stable.iter().copied().collect(); - // let mut std_items: Vec<_> = std.iter().map(|r| r.0).collect(); - // stable_items.sort(); - // std_items.sort(); - // assert_eq!(stable_items, std_items); - // Pop all elements, should match in ascending order. let mut stable_popped = Vec::new(); let mut std_popped = Vec::new(); diff --git a/src/api_conformance/vec.rs b/src/api_conformance/vec.rs index 93a8a670..39a3f9bf 100644 --- a/src/api_conformance/vec.rs +++ b/src/api_conformance/vec.rs @@ -38,13 +38,6 @@ fn api_conformance() { assert_eq!(stable.get(i as u64), Some(std[i as usize])); } - // TODO: add Copy trait to iter. - // // Iteration. - // // Note: stable.iter() yields &T, std.iter() yields &T. - // let stable_items: Vec<_> = stable.iter().copied().collect(); - // let std_items: Vec<_> = std.iter().copied().collect(); - // assert_eq!(stable_items, std_items); - // Pop elements. // Note: stable.pop() and std.pop() both return Option. for _ in 0..n { From d6bbff7abdf47368e0ab3af1e4cb0bbde8b2211a Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Tue, 17 Jun 2025 10:27:57 +0200 Subject: [PATCH 21/22] . --- src/api_conformance/mod.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/api_conformance/mod.rs b/src/api_conformance/mod.rs index 74b5c2a6..687e98b2 100644 --- a/src/api_conformance/mod.rs +++ b/src/api_conformance/mod.rs @@ -1,18 +1,15 @@ -use std::cell::RefCell; -use std::rc::Rc; - #[cfg(test)] mod btreemap; - #[cfg(test)] mod btreeset; - #[cfg(test)] mod min_heap; - #[cfg(test)] mod vec; +use std::cell::RefCell; +use std::rc::Rc; + #[cfg(test)] pub(crate) fn make_memory() -> Rc>> { Rc::new(RefCell::new(std::vec::Vec::new())) From f0e5381e31c5da30e06dc5f69167d2c855946151 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Tue, 17 Jun 2025 14:42:11 +0200 Subject: [PATCH 22/22] tests/ --- src/api_conformance/btreemap.rs | 118 ------------ src/api_conformance/btreeset.rs | 110 ----------- src/api_conformance/min_heap.rs | 43 ----- src/api_conformance/mod.rs | 16 -- src/api_conformance/vec.rs | 50 ----- src/lib.rs | 2 - tests/api_confomrance.rs | 325 ++++++++++++++++++++++++++++++++ 7 files changed, 325 insertions(+), 339 deletions(-) delete mode 100644 src/api_conformance/btreemap.rs delete mode 100644 src/api_conformance/btreeset.rs delete mode 100644 src/api_conformance/min_heap.rs delete mode 100644 src/api_conformance/mod.rs delete mode 100644 src/api_conformance/vec.rs create mode 100644 tests/api_confomrance.rs diff --git a/src/api_conformance/btreemap.rs b/src/api_conformance/btreemap.rs deleted file mode 100644 index cdea662a..00000000 --- a/src/api_conformance/btreemap.rs +++ /dev/null @@ -1,118 +0,0 @@ -use crate::api_conformance::make_memory; -use crate::btreemap::BTreeMap; - -#[test] -fn api_conformance() { - let mem = make_memory(); - let mut stable = BTreeMap::new(mem); - let mut std = std::collections::BTreeMap::new(); - let n = 10_u32; - - // Insert keys and values. - for i in 0..n { - stable.insert(i, format!("{i}")); - std.insert(i, format!("{i}")); - } - - // Get and contains. - // Note: stable.get returns owned value (Option), std.get returns reference (Option<&V>). - assert_eq!(stable.get(&1), std.get(&1).cloned()); - assert_eq!(stable.contains_key(&1), std.contains_key(&1)); - - // Remove. - // Same return behavior as get: stable returns owned value, std returns owned too. - assert_eq!(stable.remove(&1), std.remove(&1)); - - // Length and is_empty. - // Note: stable.len() returns u64, std.len() returns usize. - assert_eq!(stable.len(), std.len() as u64); - assert_eq!(stable.is_empty(), std.is_empty()); - - // Clear. - // Note: stable uses clear_new(); std uses clear(). - stable.clear_new(); - std.clear(); - assert_eq!(stable.len(), std.len() as u64); - assert_eq!(stable.is_empty(), std.is_empty()); - - // Re-insert to test iteration-related methods. - for i in 0..n { - let v = i.to_string(); - stable.insert(i, v.clone()); - std.insert(i, v); - } - - // Iterators. - // Note: stable.iter() yields (K, V), std.iter() yields (&K, &V) - let stable_items: Vec<_> = stable.iter().collect(); - let std_items: Vec<_> = std.iter().map(|(k, v)| (*k, v.clone())).collect(); - assert_eq!(stable_items, std_items); - - let stable_keys: Vec<_> = stable.keys().collect(); - let std_keys: Vec<_> = std.keys().copied().collect(); - assert_eq!(stable_keys, std_keys); - - let stable_values: Vec<_> = stable.values().collect(); - let std_values: Vec<_> = std.values().cloned().collect(); - assert_eq!(stable_values, std_values); - - // First and last. - // Note: stable returns (K, V), std returns (&K, &V) - let stable_first = stable.first_key_value(); - let std_first = std.first_key_value().map(|(k, v)| (*k, v.clone())); - assert_eq!(stable_first, std_first); - - let stable_last = stable.last_key_value(); - let std_last = std.last_key_value().map(|(k, v)| (*k, v.clone())); - assert_eq!(stable_last, std_last); - - // Pop first and last. - // Note: both stable and std return Option<(K, V)> - let stable_pop_first = stable.pop_first(); - let std_pop_first = std.pop_first(); - assert_eq!(stable_pop_first, std_pop_first); - - let stable_pop_last = stable.pop_last(); - let std_pop_last = std.pop_last(); - assert_eq!(stable_pop_last, std_pop_last); - - // Range. - let range_start = 3; - let range_end = 7; - - let stable_range: Vec<_> = stable.range(range_start..range_end).collect(); - let std_range: Vec<_> = std - .range(range_start..range_end) - .map(|(k, v)| (*k, v.clone())) - .collect(); - assert_eq!(stable_range, std_range); - - // keys_range - // Note: stable has a dedicated keys_range() method; std uses map.range().map(|(k, _)| ...) - let stable_keys_range: Vec<_> = stable.keys_range(range_start..range_end).collect(); - let std_keys_range: Vec<_> = std.range(range_start..range_end).map(|(k, _)| *k).collect(); - assert_eq!(stable_keys_range, std_keys_range); - - // values_range - // Note: stable has a dedicated values_range() method; std uses map.range().map(|(_, v)| ...) - let stable_values_range: Vec<_> = stable.values_range(range_start..range_end).collect(); - let std_values_range: Vec<_> = std - .range(range_start..range_end) - .map(|(_, v)| v.clone()) - .collect(); - assert_eq!(stable_values_range, std_values_range); - - // iter_from_prev_key - // Note: stable.iter_from_prev_key(bound) is a custom method. - // It starts from the largest key strictly less than `bound`, and continues forward. - // To simulate in std: use .range(..bound).next_back() to get the largest < bound, - // then iterate from that key forward using .range(start..). - let bound = 5; - let stable_result: Vec<_> = stable.iter_from_prev_key(&bound).collect(); - let std_result: Vec<_> = if let Some((start, _)) = std.range(..bound).next_back() { - std.range(start..).map(|(k, v)| (*k, v.clone())).collect() - } else { - Vec::new() - }; - assert_eq!(stable_result, std_result); -} diff --git a/src/api_conformance/btreeset.rs b/src/api_conformance/btreeset.rs deleted file mode 100644 index c3d3e11b..00000000 --- a/src/api_conformance/btreeset.rs +++ /dev/null @@ -1,110 +0,0 @@ -use crate::api_conformance::make_memory; -use crate::btreeset::BTreeSet; - -#[test] -fn api_conformance() { - let mem = make_memory(); - let mut stable = BTreeSet::new(mem); - let mut std = std::collections::BTreeSet::new(); - let n = 10_u32; - - // Insert elements. - for i in 0..n { - assert_eq!(stable.insert(i), std.insert(i)); - } - - // Contains. - for i in 0..n { - assert_eq!(stable.contains(&i), std.contains(&i)); - } - - // is_empty and len. - // Note: stable.len() returns u64, std.len() returns usize. - assert_eq!(stable.is_empty(), std.is_empty()); - assert_eq!(stable.len(), std.len() as u64); - - // First and last. - // Note: stable.first()/last() returns Option, std returns Option<&T>. - assert_eq!(stable.first(), std.first().copied()); - assert_eq!(stable.last(), std.last().copied()); - - // Iteration. - // Note: stable.iter() yields T, std.iter() yields &T. - let stable_items: Vec<_> = stable.iter().collect(); - let std_items: Vec<_> = std.iter().copied().collect(); - assert_eq!(stable_items, std_items); - - // Range. - let range_start = 3; - let range_end = 7; - let stable_range: Vec<_> = stable.range(range_start..range_end).collect(); - let std_range: Vec<_> = std.range(range_start..range_end).copied().collect(); - assert_eq!(stable_range, std_range); - - // pop_first / pop_last. - let mem2 = make_memory(); - let mut stable_temp = BTreeSet::new(mem2); - let mut std_temp = std::collections::BTreeSet::new(); - for i in 0..n { - assert_eq!(stable_temp.insert(i), std_temp.insert(i)); - } - - assert_eq!(stable_temp.pop_first(), std_temp.pop_first()); - assert_eq!(stable_temp.pop_last(), std_temp.pop_last()); - - // Remove elements. - for i in 0..n { - assert_eq!(stable.remove(&i), std.remove(&i)); - } - assert!(stable.is_empty()); - assert!(std.is_empty()); - - // Clear. - for i in 0..n { - stable.insert(i); - std.insert(i); - } - stable.clear(); - std.clear(); - assert!(stable.is_empty()); - assert!(std.is_empty()); - - // Reinsert for set operations. - for i in 0..n { - if i % 2 == 0 { - stable.insert(i); - std.insert(i); - } - } - - let mem2 = make_memory(); - let mut stable2 = BTreeSet::new(mem2); - let mut std2 = std::collections::BTreeSet::new(); - - for i in 0..n { - if i % 3 == 0 { - stable2.insert(i); - std2.insert(i); - } - } - - // is_disjoint, is_subset, is_superset. - assert_eq!(stable.is_disjoint(&stable2), std.is_disjoint(&std2)); - assert_eq!(stable.is_subset(&stable2), std.is_subset(&std2)); - assert_eq!(stable.is_superset(&stable2), std.is_superset(&std2)); - - // union - let stable_union: Vec<_> = stable.union(&stable2).collect(); - let std_union: Vec<_> = std.union(&std2).copied().collect(); - assert_eq!(stable_union, std_union); - - // intersection - let stable_inter: Vec<_> = stable.intersection(&stable2).collect(); - let std_inter: Vec<_> = std.intersection(&std2).copied().collect(); - assert_eq!(stable_inter, std_inter); - - // symmetric_difference - let stable_diff: Vec<_> = stable.symmetric_difference(&stable2).collect(); - let std_diff: Vec<_> = std.symmetric_difference(&std2).copied().collect(); - assert_eq!(stable_diff, std_diff); -} diff --git a/src/api_conformance/min_heap.rs b/src/api_conformance/min_heap.rs deleted file mode 100644 index dbc2d67a..00000000 --- a/src/api_conformance/min_heap.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::api_conformance::make_memory; -use crate::min_heap::MinHeap; -use std::cmp::Reverse; -use std::collections::BinaryHeap; - -#[test] -fn api_conformance() { - let mem = make_memory(); - let mut stable = MinHeap::new(mem).unwrap(); - let mut std = BinaryHeap::new(); - let n = 10_u32; - - // Push elements. - for i in 0..n { - stable.push(&i).expect("push failed"); - std.push(Reverse(i)); - } - - // Length and is_empty. - // Note: stable.len() returns u64, std.len() returns usize. - assert_eq!(stable.len(), std.len() as u64); - assert_eq!(stable.is_empty(), std.is_empty()); - - // Peek (min element). - // Note: stable.peek() returns Option, std.peek() returns Option<&Reverse>. - assert_eq!(stable.peek(), std.peek().map(|r| r.0)); - - // Pop all elements, should match in ascending order. - let mut stable_popped = Vec::new(); - let mut std_popped = Vec::new(); - while let Some(v) = stable.pop() { - stable_popped.push(v); - } - while let Some(Reverse(v)) = std.pop() { - std_popped.push(v); - } - assert_eq!(stable_popped, std_popped); - - // After popping everything, both should be empty. - assert_eq!(stable.len(), 0); - assert!(stable.is_empty()); - assert!(std.is_empty()); -} diff --git a/src/api_conformance/mod.rs b/src/api_conformance/mod.rs deleted file mode 100644 index 687e98b2..00000000 --- a/src/api_conformance/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -#[cfg(test)] -mod btreemap; -#[cfg(test)] -mod btreeset; -#[cfg(test)] -mod min_heap; -#[cfg(test)] -mod vec; - -use std::cell::RefCell; -use std::rc::Rc; - -#[cfg(test)] -pub(crate) fn make_memory() -> Rc>> { - Rc::new(RefCell::new(std::vec::Vec::new())) -} diff --git a/src/api_conformance/vec.rs b/src/api_conformance/vec.rs deleted file mode 100644 index 39a3f9bf..00000000 --- a/src/api_conformance/vec.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::api_conformance::make_memory; -use crate::vec::Vec; - -#[test] -fn api_conformance() { - let mem = make_memory(); - let stable = Vec::new(mem).unwrap(); - let mut std = std::vec::Vec::new(); - let n = 10_u32; - - // Push elements. - for i in 0..n { - stable.push(&i).expect("push failed"); - std.push(i); - } - - // Length and is_empty. - // Note: stable.len() returns u64, std.len() returns usize. - assert_eq!(stable.len(), std.len() as u64); - assert_eq!(stable.is_empty(), std.is_empty()); - - // Get by index. - // Note: stable.get returns Option<&T>, std returns Option<&T>. - for i in 0..n { - assert_eq!(stable.get(i as u64), Some(std[i as usize])); - } - - // Set by index. - // Note: stable.set uses &T, std uses indexing. - for i in 0..n { - let value = i * 10; - stable.set(i as u64, &value); - std[i as usize] = value; - } - - // Confirm updated values. - for i in 0..n { - assert_eq!(stable.get(i as u64), Some(std[i as usize])); - } - - // Pop elements. - // Note: stable.pop() and std.pop() both return Option. - for _ in 0..n { - assert_eq!(stable.pop(), std.pop()); - } - - // After popping everything, both should be empty. - assert_eq!(stable.len(), std.len() as u64); - assert_eq!(stable.is_empty(), std.is_empty()); -} diff --git a/src/lib.rs b/src/lib.rs index 6c963f39..e096b11e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,8 +3,6 @@ mod base_vec; pub mod btreemap; pub mod cell; pub use cell::{Cell as StableCell, Cell}; -#[cfg(test)] -mod api_conformance; pub mod btreeset; pub mod file_mem; #[cfg(target_arch = "wasm32")] diff --git a/tests/api_confomrance.rs b/tests/api_confomrance.rs new file mode 100644 index 00000000..7b5cb568 --- /dev/null +++ b/tests/api_confomrance.rs @@ -0,0 +1,325 @@ +use ic_stable_structures::btreemap::BTreeMap; +use ic_stable_structures::btreeset::BTreeSet; +use ic_stable_structures::min_heap::MinHeap; +use ic_stable_structures::vec::Vec as StableVec; +use std::cell::RefCell; +use std::cmp::Reverse; +use std::collections::BinaryHeap; +use std::rc::Rc; + +pub fn make_memory() -> Rc>> { + Rc::new(RefCell::new(Vec::new())) +} + +#[test] +fn api_conformance_btreemap() { + let mem = make_memory(); + let mut stable = BTreeMap::new(mem); + let mut std = std::collections::BTreeMap::new(); + let n = 10_u32; + + // Insert keys and values. + for i in 0..n { + stable.insert(i, format!("{i}")); + std.insert(i, format!("{i}")); + } + + // Get and contains. + // Note: stable.get returns owned value (Option), std.get returns reference (Option<&V>). + assert_eq!(stable.get(&1), std.get(&1).cloned()); + assert_eq!(stable.contains_key(&1), std.contains_key(&1)); + + // Remove. + // Same return behavior as get: stable returns owned value, std returns owned too. + assert_eq!(stable.remove(&1), std.remove(&1)); + + // Length and is_empty. + // Note: stable.len() returns u64, std.len() returns usize. + assert_eq!(stable.len(), std.len() as u64); + assert_eq!(stable.is_empty(), std.is_empty()); + + // Clear. + // Note: stable uses clear_new(); std uses clear(). + stable.clear_new(); + std.clear(); + assert_eq!(stable.len(), std.len() as u64); + assert_eq!(stable.is_empty(), std.is_empty()); + + // Re-insert to test iteration-related methods. + for i in 0..n { + let v = i.to_string(); + stable.insert(i, v.clone()); + std.insert(i, v); + } + + // Iterators. + // Note: stable.iter() yields (K, V), std.iter() yields (&K, &V) + let stable_items: std::vec::Vec<_> = stable.iter().collect(); + let std_items: std::vec::Vec<_> = std.iter().map(|(k, v)| (*k, v.clone())).collect(); + assert_eq!(stable_items, std_items); + + let stable_keys: std::vec::Vec<_> = stable.keys().collect(); + let std_keys: std::vec::Vec<_> = std.keys().copied().collect(); + assert_eq!(stable_keys, std_keys); + + let stable_values: std::vec::Vec<_> = stable.values().collect(); + let std_values: std::vec::Vec<_> = std.values().cloned().collect(); + assert_eq!(stable_values, std_values); + + // First and last. + // Note: stable returns (K, V), std returns (&K, &V) + let stable_first = stable.first_key_value(); + let std_first = std.first_key_value().map(|(k, v)| (*k, v.clone())); + assert_eq!(stable_first, std_first); + + let stable_last = stable.last_key_value(); + let std_last = std.last_key_value().map(|(k, v)| (*k, v.clone())); + assert_eq!(stable_last, std_last); + + // Pop first and last. + // Note: both stable and std return Option<(K, V)> + let stable_pop_first = stable.pop_first(); + let std_pop_first = std.pop_first(); + assert_eq!(stable_pop_first, std_pop_first); + + let stable_pop_last = stable.pop_last(); + let std_pop_last = std.pop_last(); + assert_eq!(stable_pop_last, std_pop_last); + + // Range. + let range_start = 3; + let range_end = 7; + + let stable_range: std::vec::Vec<_> = stable.range(range_start..range_end).collect(); + let std_range: std::vec::Vec<_> = std + .range(range_start..range_end) + .map(|(k, v)| (*k, v.clone())) + .collect(); + assert_eq!(stable_range, std_range); + + // keys_range + // Note: stable has a dedicated keys_range() method; std uses map.range().map(|(k, _)| ...) + let stable_keys_range: std::vec::Vec<_> = stable.keys_range(range_start..range_end).collect(); + let std_keys_range: std::vec::Vec<_> = + std.range(range_start..range_end).map(|(k, _)| *k).collect(); + assert_eq!(stable_keys_range, std_keys_range); + + // values_range + // Note: stable has a dedicated values_range() method; std uses map.range().map(|(_, v)| ...) + let stable_values_range: std::vec::Vec<_> = + stable.values_range(range_start..range_end).collect(); + let std_values_range: std::vec::Vec<_> = std + .range(range_start..range_end) + .map(|(_, v)| v.clone()) + .collect(); + assert_eq!(stable_values_range, std_values_range); + + // iter_from_prev_key + // Note: stable.iter_from_prev_key(bound) is a custom method. + // It starts from the largest key strictly less than `bound`, and continues forward. + // To simulate in std: use .range(..bound).next_back() to get the largest < bound, + // then iterate from that key forward using .range(start..). + let bound = 5; + let stable_result: std::vec::Vec<_> = stable.iter_from_prev_key(&bound).collect(); + let std_result: std::vec::Vec<_> = if let Some((start, _)) = std.range(..bound).next_back() { + std.range(start..).map(|(k, v)| (*k, v.clone())).collect() + } else { + Vec::new() + }; + assert_eq!(stable_result, std_result); +} + +#[test] +fn api_conformance_btreeset() { + let mem = make_memory(); + let mut stable = BTreeSet::new(mem); + let mut std = std::collections::BTreeSet::new(); + let n = 10_u32; + + // Insert elements. + for i in 0..n { + assert_eq!(stable.insert(i), std.insert(i)); + } + + // Contains. + for i in 0..n { + assert_eq!(stable.contains(&i), std.contains(&i)); + } + + // is_empty and len. + // Note: stable.len() returns u64, std.len() returns usize. + assert_eq!(stable.is_empty(), std.is_empty()); + assert_eq!(stable.len(), std.len() as u64); + + // First and last. + // Note: stable.first()/last() returns Option, std returns Option<&T>. + assert_eq!(stable.first(), std.first().copied()); + assert_eq!(stable.last(), std.last().copied()); + + // Iteration. + // Note: stable.iter() yields T, std.iter() yields &T. + let stable_items: std::vec::Vec<_> = stable.iter().collect(); + let std_items: std::vec::Vec<_> = std.iter().copied().collect(); + assert_eq!(stable_items, std_items); + + // Range. + let range_start = 3; + let range_end = 7; + let stable_range: std::vec::Vec<_> = stable.range(range_start..range_end).collect(); + let std_range: std::vec::Vec<_> = std.range(range_start..range_end).copied().collect(); + assert_eq!(stable_range, std_range); + + // pop_first / pop_last. + let mem2 = make_memory(); + let mut stable_temp = BTreeSet::new(mem2); + let mut std_temp = std::collections::BTreeSet::new(); + for i in 0..n { + assert_eq!(stable_temp.insert(i), std_temp.insert(i)); + } + + assert_eq!(stable_temp.pop_first(), std_temp.pop_first()); + assert_eq!(stable_temp.pop_last(), std_temp.pop_last()); + + // Remove elements. + for i in 0..n { + assert_eq!(stable.remove(&i), std.remove(&i)); + } + assert!(stable.is_empty()); + assert!(std.is_empty()); + + // Clear. + for i in 0..n { + stable.insert(i); + std.insert(i); + } + stable.clear(); + std.clear(); + assert!(stable.is_empty()); + assert!(std.is_empty()); + + // Reinsert for set operations. + for i in 0..n { + if i % 2 == 0 { + stable.insert(i); + std.insert(i); + } + } + + let mem2 = make_memory(); + let mut stable2 = BTreeSet::new(mem2); + let mut std2 = std::collections::BTreeSet::new(); + + for i in 0..n { + if i % 3 == 0 { + stable2.insert(i); + std2.insert(i); + } + } + + // is_disjoint, is_subset, is_superset. + assert_eq!(stable.is_disjoint(&stable2), std.is_disjoint(&std2)); + assert_eq!(stable.is_subset(&stable2), std.is_subset(&std2)); + assert_eq!(stable.is_superset(&stable2), std.is_superset(&std2)); + + // union + let stable_union: std::vec::Vec<_> = stable.union(&stable2).collect(); + let std_union: std::vec::Vec<_> = std.union(&std2).copied().collect(); + assert_eq!(stable_union, std_union); + + // intersection + let stable_inter: std::vec::Vec<_> = stable.intersection(&stable2).collect(); + let std_inter: std::vec::Vec<_> = std.intersection(&std2).copied().collect(); + assert_eq!(stable_inter, std_inter); + + // symmetric_difference + let stable_diff: std::vec::Vec<_> = stable.symmetric_difference(&stable2).collect(); + let std_diff: std::vec::Vec<_> = std.symmetric_difference(&std2).copied().collect(); + assert_eq!(stable_diff, std_diff); +} + +#[test] +fn api_conformance_min_heap() { + let mem = make_memory(); + let mut stable = MinHeap::new(mem).unwrap(); + let mut std = BinaryHeap::new(); + let n = 10_u32; + + // Push elements. + for i in 0..n { + stable.push(&i).expect("push failed"); + std.push(Reverse(i)); + } + + // Length and is_empty. + // Note: stable.len() returns u64, std.len() returns usize. + assert_eq!(stable.len(), std.len() as u64); + assert_eq!(stable.is_empty(), std.is_empty()); + + // Peek (min element). + // Note: stable.peek() returns Option, std.peek() returns Option<&Reverse>. + assert_eq!(stable.peek(), std.peek().map(|r| r.0)); + + // Pop all elements, should match in ascending order. + let mut stable_popped = Vec::new(); + let mut std_popped = Vec::new(); + while let Some(v) = stable.pop() { + stable_popped.push(v); + } + while let Some(Reverse(v)) = std.pop() { + std_popped.push(v); + } + assert_eq!(stable_popped, std_popped); + + // After popping everything, both should be empty. + assert_eq!(stable.len(), 0); + assert!(stable.is_empty()); + assert!(std.is_empty()); +} + +#[test] +fn api_conformance_vec() { + let mem = make_memory(); + let stable = StableVec::new(mem).unwrap(); + let mut std = Vec::new(); + let n = 10_u32; + + // Push elements. + for i in 0..n { + stable.push(&i).expect("push failed"); + std.push(i); + } + + // Length and is_empty. + // Note: stable.len() returns u64, std.len() returns usize. + assert_eq!(stable.len(), std.len() as u64); + assert_eq!(stable.is_empty(), std.is_empty()); + + // Get by index. + // Note: stable.get returns Option<&T>, std returns Option<&T>. + for i in 0..n { + assert_eq!(stable.get(i as u64), Some(std[i as usize])); + } + + // Set by index. + // Note: stable.set uses &T, std uses indexing. + for i in 0..n { + let value = i * 10; + stable.set(i as u64, &value); + std[i as usize] = value; + } + + // Confirm updated values. + for i in 0..n { + assert_eq!(stable.get(i as u64), Some(std[i as usize])); + } + + // Pop elements. + // Note: stable.pop() and std.pop() both return Option. + for _ in 0..n { + assert_eq!(stable.pop(), std.pop()); + } + + // After popping everything, both should be empty. + assert_eq!(stable.len(), std.len() as u64); + assert_eq!(stable.is_empty(), std.is_empty()); +}