From 572b0387e6c3948e3fce0963d9f9b6f3437921b9 Mon Sep 17 00:00:00 2001 From: Aria Shrimpton Date: Mon, 5 Feb 2024 12:49:27 +0000 Subject: add adaptive container type --- src/Cargo.lock | 7 ++ src/crates/library/Cargo.toml | 5 +- src/crates/library/src/adaptive.rs | 161 ++++++++++++++++++++++++++ src/crates/library/src/eager_sorted_vector.rs | 8 ++ src/crates/library/src/eager_unique_vector.rs | 9 ++ src/crates/library/src/lib.rs | 3 + src/crates/library/src/profiler.rs | 20 ++++ src/crates/library/src/proptest/strategies.rs | 22 ---- src/crates/library/src/traits.rs | 3 +- 9 files changed, 214 insertions(+), 24 deletions(-) create mode 100644 src/crates/library/src/adaptive.rs (limited to 'src') diff --git a/src/Cargo.lock b/src/Cargo.lock index 038dae7..45b658f 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -652,6 +652,7 @@ version = "0.1.0" dependencies = [ "im", "proptest", + "take_mut", ] [[package]] @@ -1007,6 +1008,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "take_mut" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" + [[package]] name = "tempfile" version = "3.9.0" diff --git a/src/crates/library/Cargo.toml b/src/crates/library/Cargo.toml index afcd90d..b8216a6 100644 --- a/src/crates/library/Cargo.toml +++ b/src/crates/library/Cargo.toml @@ -7,4 +7,7 @@ edition = "2021" [dev-dependencies] im = "10.2.0" -proptest = "1.0.0" \ No newline at end of file +proptest = "1.0.0" + +[dependencies] +take_mut = "0.2.2" diff --git a/src/crates/library/src/adaptive.rs b/src/crates/library/src/adaptive.rs new file mode 100644 index 0000000..e464602 --- /dev/null +++ b/src/crates/library/src/adaptive.rs @@ -0,0 +1,161 @@ +use std::marker::PhantomData; + +use crate::traits::Container; +use take_mut::take_or_recover; + +pub enum AdaptiveContainer { + Low(LO, PhantomData), + High(HI), +} + +impl Default + for AdaptiveContainer +{ + fn default() -> Self { + Self::Low(LO::default(), PhantomData) + } +} + +macro_rules! get_inner { + ($self:ident, $to:ident, $expr:expr) => { + match $self { + AdaptiveContainer::Low($to, _) => $expr, + AdaptiveContainer::High($to) => $expr, + } + }; +} + +impl, HI: Default + Container, E> Container + for AdaptiveContainer +{ + fn len(&self) -> usize { + get_inner!(self, c, c.len()) + } + + fn is_empty(&self) -> bool { + get_inner!(self, c, c.is_empty()) + } + + fn contains(&mut self, x: &E) -> bool { + get_inner!(self, c, c.contains(x)) + } + + fn insert(&mut self, elt: E) { + get_inner!(self, c, c.insert(elt)); + if let Self::Low(l, _) = self { + if l.len() < THRESHOLD { + return; + } + // adapt! + take_or_recover(self, Default::default, |old| { + let Self::Low(l, _) = old else { + unreachable!(); + }; + Self::High(HI::from_iter(l)) + }); + } + } + + fn remove(&mut self, elt: E) -> Option { + get_inner!(self, c, c.remove(elt)) + } + + fn clear(&mut self) { + get_inner!(self, c, c.clear()) + } +} + +impl FromIterator for AdaptiveContainer +where + LO: FromIterator, +{ + fn from_iter>(iter: T) -> Self { + Self::Low(LO::from_iter(iter), PhantomData) + } +} + +impl IntoIterator for AdaptiveContainer +where + LO: IntoIterator, + HI: IntoIterator, +{ + type Item = E; + + type IntoIter = AdaptiveContainerIter; + + fn into_iter(self) -> Self::IntoIter { + match self { + AdaptiveContainer::Low(l, _) => AdaptiveContainerIter::Low(l.into_iter(), PhantomData), + AdaptiveContainer::High(h) => AdaptiveContainerIter::High(h.into_iter()), + } + } +} + +pub enum AdaptiveContainerIter { + Low(LO, PhantomData), + High(HI), +} + +impl Iterator for AdaptiveContainerIter +where + LO: Iterator, + HI: Iterator, +{ + type Item = E; + + fn next(&mut self) -> Option { + match self { + AdaptiveContainerIter::Low(l, _) => l.next(), + AdaptiveContainerIter::High(h) => h.next(), + } + } +} + +#[cfg(test)] +mod tests { + use std::collections::HashSet; + + use crate::{traits::Container, AdaptiveContainer, EagerUniqueVec}; + + #[test] + fn adaptive_container_lo_functionality() { + let mut c: AdaptiveContainer<10, EagerUniqueVec<_>, HashSet<_>, usize> = Default::default(); + + for i in 0..5 { + c.insert(i); + } + + assert_eq!(c.len(), 5, "length is correct"); + } + + #[test] + fn adaptive_container_adapts() { + let mut c: AdaptiveContainer<10, EagerUniqueVec<_>, HashSet<_>, usize> = Default::default(); + + for i in 1..=9 { + c.insert(i); + } + + if let AdaptiveContainer::High(_) = c { + panic!("should not yet change to high variant"); + } + + c.insert(10); + if let AdaptiveContainer::Low(_, _) = c { + panic!("should change to high variant at threshold"); + } + + for i in 11..=20 { + c.insert(i); + } + + if let AdaptiveContainer::Low(_, _) = c { + panic!("should remain at high variant"); + } + assert_eq!(c.len(), 20, "length is correct"); + + for i in 1..=20 { + assert!(c.contains(&i), "contains correct data"); + } + } +} diff --git a/src/crates/library/src/eager_sorted_vector.rs b/src/crates/library/src/eager_sorted_vector.rs index 20d270a..64b5711 100644 --- a/src/crates/library/src/eager_sorted_vector.rs +++ b/src/crates/library/src/eager_sorted_vector.rs @@ -220,6 +220,14 @@ impl IntoIterator for EagerSortedVec { } } +impl FromIterator for EagerSortedVec { + fn from_iter>(iter: T) -> Self { + let mut v = Vec::from_iter(iter); + v.sort(); + Self { v } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/crates/library/src/eager_unique_vector.rs b/src/crates/library/src/eager_unique_vector.rs index a8f5abf..8dfebbc 100644 --- a/src/crates/library/src/eager_unique_vector.rs +++ b/src/crates/library/src/eager_unique_vector.rs @@ -227,6 +227,15 @@ impl IntoIterator for EagerUniqueVec { } } +impl FromIterator for EagerUniqueVec { + fn from_iter>(iter: T) -> Self { + let mut v = Vec::from_iter(iter); + v.sort(); + v.dedup(); + Self { v } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/crates/library/src/lib.rs b/src/crates/library/src/lib.rs index f1ff34d..0a62c75 100644 --- a/src/crates/library/src/lib.rs +++ b/src/crates/library/src/lib.rs @@ -7,6 +7,9 @@ pub mod traits; mod profiler; pub use profiler::ProfilerWrapper; +mod adaptive; +pub use adaptive::AdaptiveContainer; + mod eager_sorted_vector; mod eager_unique_vector; diff --git a/src/crates/library/src/profiler.rs b/src/crates/library/src/profiler.rs index 2385553..6a72916 100644 --- a/src/crates/library/src/profiler.rs +++ b/src/crates/library/src/profiler.rs @@ -198,3 +198,23 @@ impl IntoIterator for ProfilerWrapper, E> FromIterator for ProfilerWrapper { + fn from_iter>(iter: T) -> Self { + Self { + inner: Some(C::from_iter(iter)), + max_n: 0, + n_contains: 0, + n_insert: 0, + n_clear: 0, + n_remove: 0, + n_first: 0, + n_last: 0, + n_nth: 0, + n_push: 0, + n_pop: 0, + n_get: 0, + _d: PhantomData, + } + } +} diff --git a/src/crates/library/src/proptest/strategies.rs b/src/crates/library/src/proptest/strategies.rs index d087fcc..b866c67 100644 --- a/src/crates/library/src/proptest/strategies.rs +++ b/src/crates/library/src/proptest/strategies.rs @@ -4,8 +4,6 @@ use std::ops::Range; use crate::eager_sorted_vector::EagerSortedVec; use crate::eager_unique_vector::EagerUniqueVec; -use crate::lazy_sorted_vector::LazySortedVec; -use crate::lazy_unique_vector::LazyUniqueVec; use proptest::collection::vec; @@ -19,16 +17,6 @@ where vec(element, size.clone()).prop_map(EagerUniqueVec::from_vec) } -pub fn lazy_unique_vec( - element: T, - size: Range, -) -> impl Strategy> -where - ::Value: Ord, -{ - vec(element, size.clone()).prop_map(LazyUniqueVec::from_vec) -} - pub fn eager_sorted_vec( element: T, size: Range, @@ -38,13 +26,3 @@ where { vec(element, size.clone()).prop_map(EagerSortedVec::from_vec) } - -pub fn lazy_sorted_vec( - element: T, - size: Range, -) -> impl Strategy> -where - ::Value: Ord, -{ - vec(element, size.clone()).prop_map(LazySortedVec::from_vec) -} diff --git a/src/crates/library/src/traits.rs b/src/crates/library/src/traits.rs index 528e643..e08ef91 100644 --- a/src/crates/library/src/traits.rs +++ b/src/crates/library/src/traits.rs @@ -1,5 +1,6 @@ //! Common traits for primrose container types -pub trait Container: Default + IntoIterator { + +pub trait Container: Default + IntoIterator + FromIterator { /// Get the current number of elements fn len(&self) -> usize; -- cgit v1.2.3