diff options
author | Aria Shrimpton <me@aria.rip> | 2024-02-05 12:49:27 +0000 |
---|---|---|
committer | Aria Shrimpton <me@aria.rip> | 2024-02-05 13:20:08 +0000 |
commit | 572b0387e6c3948e3fce0963d9f9b6f3437921b9 (patch) | |
tree | d2c3f838fb8d97d775d9efbcd10d781973c9a87e /src | |
parent | 3d909b86b2f0950116b34442e4438439dc9f6bd5 (diff) |
add adaptive container type
Diffstat (limited to 'src')
-rw-r--r-- | src/Cargo.lock | 7 | ||||
-rw-r--r-- | src/crates/library/Cargo.toml | 5 | ||||
-rw-r--r-- | src/crates/library/src/adaptive.rs | 161 | ||||
-rw-r--r-- | src/crates/library/src/eager_sorted_vector.rs | 8 | ||||
-rw-r--r-- | src/crates/library/src/eager_unique_vector.rs | 9 | ||||
-rw-r--r-- | src/crates/library/src/lib.rs | 3 | ||||
-rw-r--r-- | src/crates/library/src/profiler.rs | 20 | ||||
-rw-r--r-- | src/crates/library/src/proptest/strategies.rs | 22 | ||||
-rw-r--r-- | src/crates/library/src/traits.rs | 3 |
9 files changed, 214 insertions, 24 deletions
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]] @@ -1008,6 +1009,12 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" 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<const THRESHOLD: usize, LO, HI, E> { + Low(LO, PhantomData<E>), + High(HI), +} + +impl<const THRESHOLD: usize, LO: Default, HI, E> Default + for AdaptiveContainer<THRESHOLD, LO, HI, E> +{ + 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<const THRESHOLD: usize, LO: Default + Container<E>, HI: Default + Container<E>, E> Container<E> + for AdaptiveContainer<THRESHOLD, LO, HI, E> +{ + 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<E> { + get_inner!(self, c, c.remove(elt)) + } + + fn clear(&mut self) { + get_inner!(self, c, c.clear()) + } +} + +impl<const THRESHOLD: usize, LO, HI, E> FromIterator<E> for AdaptiveContainer<THRESHOLD, LO, HI, E> +where + LO: FromIterator<E>, +{ + fn from_iter<T: IntoIterator<Item = E>>(iter: T) -> Self { + Self::Low(LO::from_iter(iter), PhantomData) + } +} + +impl<const THRESHOLD: usize, LO, HI, E> IntoIterator for AdaptiveContainer<THRESHOLD, LO, HI, E> +where + LO: IntoIterator<Item = E>, + HI: IntoIterator<Item = E>, +{ + type Item = E; + + type IntoIter = AdaptiveContainerIter<LO::IntoIter, HI::IntoIter, E>; + + 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<LO, HI, E> { + Low(LO, PhantomData<E>), + High(HI), +} + +impl<LO, HI, E> Iterator for AdaptiveContainerIter<LO, HI, E> +where + LO: Iterator<Item = E>, + HI: Iterator<Item = E>, +{ + type Item = E; + + fn next(&mut self) -> Option<Self::Item> { + 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<T> IntoIterator for EagerSortedVec<T> { } } +impl<E: Ord> FromIterator<E> for EagerSortedVec<E> { + fn from_iter<T: IntoIterator<Item = E>>(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<T> IntoIterator for EagerUniqueVec<T> { } } +impl<E: Ord> FromIterator<E> for EagerUniqueVec<E> { + fn from_iter<T: IntoIterator<Item = E>>(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<const ID: usize, T: IntoIterator, E> IntoIterator for ProfilerWrapper<ID, T self.inner.take().unwrap().into_iter() } } + +impl<const ID: usize, C: FromIterator<E>, E> FromIterator<E> for ProfilerWrapper<ID, C, E> { + fn from_iter<T: IntoIterator<Item = E>>(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<T: Strategy + 'static>( - element: T, - size: Range<usize>, -) -> impl Strategy<Value = LazyUniqueVec<T::Value>> -where - <T as Strategy>::Value: Ord, -{ - vec(element, size.clone()).prop_map(LazyUniqueVec::from_vec) -} - pub fn eager_sorted_vec<T: Strategy + 'static>( element: T, size: Range<usize>, @@ -38,13 +26,3 @@ where { vec(element, size.clone()).prop_map(EagerSortedVec::from_vec) } - -pub fn lazy_sorted_vec<T: Strategy + 'static>( - element: T, - size: Range<usize>, -) -> impl Strategy<Value = LazySortedVec<T::Value>> -where - <T as Strategy>::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<T>: Default + IntoIterator<Item = T> { + +pub trait Container<T>: Default + IntoIterator<Item = T> + FromIterator<T> { /// Get the current number of elements fn len(&self) -> usize; |