aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAria Shrimpton <me@aria.rip>2024-02-05 12:49:27 +0000
committerAria Shrimpton <me@aria.rip>2024-02-05 13:20:08 +0000
commit572b0387e6c3948e3fce0963d9f9b6f3437921b9 (patch)
treed2c3f838fb8d97d775d9efbcd10d781973c9a87e /src
parent3d909b86b2f0950116b34442e4438439dc9f6bd5 (diff)
add adaptive container type
Diffstat (limited to 'src')
-rw-r--r--src/Cargo.lock7
-rw-r--r--src/crates/library/Cargo.toml5
-rw-r--r--src/crates/library/src/adaptive.rs161
-rw-r--r--src/crates/library/src/eager_sorted_vector.rs8
-rw-r--r--src/crates/library/src/eager_unique_vector.rs9
-rw-r--r--src/crates/library/src/lib.rs3
-rw-r--r--src/crates/library/src/profiler.rs20
-rw-r--r--src/crates/library/src/proptest/strategies.rs22
-rw-r--r--src/crates/library/src/traits.rs3
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;