From 4c68d019e3d16660ddc95c1fafca2ae15acbea29 Mon Sep 17 00:00:00 2001 From: Egor Suvorov Date: Wed, 23 Nov 2022 20:52:34 +0200 Subject: [PATCH] persistent_range_query: first draft --- Cargo.lock | 7 ++ libs/persistent_range_query/Cargo.toml | 9 ++ libs/persistent_range_query/src/lib.rs | 68 ++++++++++++++ libs/persistent_range_query/src/naive.rs | 93 +++++++++++++++++++ libs/persistent_range_query/src/ops/mod.rs | 14 +++ libs/persistent_range_query/src/ops/rsq.rs | 85 +++++++++++++++++ libs/persistent_range_query/tests/rsq_test.rs | 56 +++++++++++ 7 files changed, 332 insertions(+) create mode 100644 libs/persistent_range_query/Cargo.toml create mode 100644 libs/persistent_range_query/src/lib.rs create mode 100644 libs/persistent_range_query/src/naive.rs create mode 100644 libs/persistent_range_query/src/ops/mod.rs create mode 100644 libs/persistent_range_query/src/ops/rsq.rs create mode 100644 libs/persistent_range_query/tests/rsq_test.rs diff --git a/Cargo.lock b/Cargo.lock index c112c05188..1d993aaf65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2255,6 +2255,13 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +[[package]] +name = "persistent_range_query" +version = "0.1.0" +dependencies = [ + "workspace_hack", +] + [[package]] name = "petgraph" version = "0.6.2" diff --git a/libs/persistent_range_query/Cargo.toml b/libs/persistent_range_query/Cargo.toml new file mode 100644 index 0000000000..c18896fac3 --- /dev/null +++ b/libs/persistent_range_query/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "persistent_range_query" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +workspace_hack = { version = "0.1", path = "../../workspace_hack" } diff --git a/libs/persistent_range_query/src/lib.rs b/libs/persistent_range_query/src/lib.rs new file mode 100644 index 0000000000..68b41dc819 --- /dev/null +++ b/libs/persistent_range_query/src/lib.rs @@ -0,0 +1,68 @@ +use std::ops::Range; + +pub mod naive; +pub mod ops; + +/// Should be a monoid: +/// * Identity element: for all a: combine(new_for_empty_range(), a) = combine(a, new_for_empty_range()) = a +/// * Associativity: for all a, b, c: combine(combine(a, b), c) == combine(a, combine(b, c)) +pub trait RangeQueryResult: Sized { + fn new_for_empty_range() -> Self; + + // Contract: left_range.end == right_range.start + // left_range.start == left_range.end == right_range.start == right_range.end is still possible + fn combine(left: &Self, left_range: &Range, right: &Self, right_range: &Range) -> Self + where + Self: Clone, + { + let mut left = left.clone(); + Self::add(&mut left, left_range, right, right_range); + left + } + + // TODO: does it work with non-Clone? + fn add(left: &mut Self, left_range: &Range, right: &Self, right_range: &Range) + where + Self: Clone, + { + *left = Self::combine(left, left_range, right, right_range); + } +} + +pub trait LazyRangeInitializer { + fn get(&self, range: &Range) -> R; +} + +/// Should be a monoid: +/// * Identity element: for all op: compose(no_op(), op) == compose(op, no_op()) == op +/// * Associativity: for all op_1, op_2, op_3: compose(compose(op_1, op_2), op_3) == compose(op_1, compose(op_2, op_3)) +/// +/// Should left act on Result: +/// * Identity operation: for all r: no_op().apply(r) == r +/// * Compatibility: for all op_1, op_2, r: op_1.apply(op_2.apply(r)) == compose(op_1, op_2).apply(r) +pub trait RangeModification { + fn no_op() -> Self; + fn apply<'a>(&self, result: &mut Result, range: &Range); + fn compose(later: &Self, earlier: &mut Self); +} + +pub trait VecVersion< + Key, + Result: RangeQueryResult, + Modification: RangeModification, +>: Clone +{ + fn get(&self, keys: Range) -> Result; + fn modify(&mut self, keys: Range, modification: Modification); +} + +pub trait PersistentVecStorage< + Key, + Result: RangeQueryResult, + Initializer: LazyRangeInitializer, + Modification: RangeModification, +> +{ + type Version: VecVersion; + fn new(all_keys: Range, initializer: Initializer) -> Self::Version; +} diff --git a/libs/persistent_range_query/src/naive.rs b/libs/persistent_range_query/src/naive.rs new file mode 100644 index 0000000000..193349975d --- /dev/null +++ b/libs/persistent_range_query/src/naive.rs @@ -0,0 +1,93 @@ +use crate::{ + LazyRangeInitializer, PersistentVecStorage, RangeModification, RangeQueryResult, VecVersion, +}; +use std::marker::PhantomData; +use std::ops::Range; + +pub struct NaiveVecVersion< + Key: Clone, + Result: RangeQueryResult + Clone, + Modification: RangeModification, +> { + all_keys: Range, + values: Vec, + _modification: PhantomData, +} + +impl< + Key: Clone, + Result: RangeQueryResult + Clone, + Modification: RangeModification, + > Clone for NaiveVecVersion +{ + fn clone(&self) -> Self { + Self { + all_keys: self.all_keys.clone(), + values: self.values.clone(), + _modification: PhantomData, + } + } +} + +pub trait IndexableKey: Clone { + fn index(all_keys: &Range, key: &Self) -> usize; + fn element_range(all_keys: &Range, index: usize) -> Range; +} + +impl< + Key: IndexableKey, + Result: Clone + RangeQueryResult, + Modification: RangeModification, + > VecVersion for NaiveVecVersion +{ + fn get(&self, keys: Range) -> Result { + let mut result = Result::new_for_empty_range(); + let mut result_range = keys.start.clone()..keys.start.clone(); + for index in IndexableKey::index(&self.all_keys, &keys.start) + ..IndexableKey::index(&self.all_keys, &keys.end) + { + let element_range = IndexableKey::element_range(&self.all_keys, index); + Result::add( + &mut result, + &result_range, + &self.values[index], + &element_range, + ); + result_range.end = element_range.end; + } + result + } + + fn modify(&mut self, keys: Range, modification: Modification) { + for index in IndexableKey::index(&self.all_keys, &keys.start) + ..IndexableKey::index(&self.all_keys, &keys.end) + { + let element_range = IndexableKey::element_range(&self.all_keys, index); + modification.apply(&mut self.values[index], &element_range); + } + } +} + +pub struct NaiveVecStorage; + +impl< + Key: IndexableKey, + Result: Clone + RangeQueryResult, + Initializer: LazyRangeInitializer, + Modification: RangeModification, + > PersistentVecStorage for NaiveVecStorage +{ + type Version = NaiveVecVersion; + + fn new(all_keys: Range, initializer: Initializer) -> Self::Version { + let mut values = Vec::with_capacity(IndexableKey::index(&all_keys, &all_keys.end)); + for index in 0..values.capacity() { + values.push(initializer.get(&IndexableKey::element_range(&all_keys, index))); + } + NaiveVecVersion { + all_keys, + values, + _modification: PhantomData, + } + } +} diff --git a/libs/persistent_range_query/src/ops/mod.rs b/libs/persistent_range_query/src/ops/mod.rs new file mode 100644 index 0000000000..5fb1712db3 --- /dev/null +++ b/libs/persistent_range_query/src/ops/mod.rs @@ -0,0 +1,14 @@ +pub mod rsq; + +#[derive(Copy, Clone, Debug)] +pub struct SameElementsInitializer { + initial_element_value: T, +} + +impl SameElementsInitializer { + pub fn new(initial_element_value: T) -> Self { + SameElementsInitializer { + initial_element_value, + } + } +} diff --git a/libs/persistent_range_query/src/ops/rsq.rs b/libs/persistent_range_query/src/ops/rsq.rs new file mode 100644 index 0000000000..e0b9d5f533 --- /dev/null +++ b/libs/persistent_range_query/src/ops/rsq.rs @@ -0,0 +1,85 @@ +//! # Range Sum Query + +use crate::ops::SameElementsInitializer; +use crate::{LazyRangeInitializer, RangeModification, RangeQueryResult}; +use std::borrow::Borrow; +use std::ops::{AddAssign, Mul, Range, Sub}; + +// TODO: commutative Add + +#[derive(Clone, Copy, Debug)] +pub struct SumResult { + sum: T, +} + +impl SumResult { + pub fn sum(&self) -> &T { + &self.sum + } +} + +impl AddAssign<&'a T> + From, Key> RangeQueryResult for SumResult { + fn new_for_empty_range() -> Self { + SumResult { sum: 0.into() } + } + + fn add(left: &mut Self, _left_range: &Range, right: &Self, _right_range: &Range) { + left.sum += &right.sum + } +} + +impl, KeyDiff> LazyRangeInitializer> + for SameElementsInitializer +where + for<'a> &'a T: Mul, + for<'b> &'b Key: Sub, +{ + fn get(&self, range: &Range) -> SumResult { + SumResult { + sum: self.initial_element_value.borrow() * (&range.end - &range.start), + } + } +} + +#[derive(Copy, Clone, Debug)] +pub enum AddAssignModification { + None, + Add(T), + Assign(T), +} + +impl AddAssign<&'a T>, Key> RangeModification, Key> + for AddAssignModification +where + for<'a> SameElementsInitializer<&'a T>: LazyRangeInitializer>, +{ + fn no_op() -> Self { + AddAssignModification::None + } + + fn apply<'a>(&self, result: &'a mut SumResult, range: &Range) { + use AddAssignModification::*; + match self { + None => {} + Add(x) | Assign(x) => { + let to_add = SameElementsInitializer::new(x).get(range).sum; + if let Assign(_) = self { + result.sum = to_add; + } else { + result.sum += &to_add; + } + } + } + } + + fn compose(later: &Self, earlier: &mut Self) { + use AddAssignModification::*; + match (later, earlier) { + (_, e @ None) => *e = later.clone(), + (None, _) => {} + (Assign(_), e) => *e = later.clone(), + (Add(x), Add(y)) => *y += x, + (Add(x), Assign(value)) => *value += x, + } + } +} diff --git a/libs/persistent_range_query/tests/rsq_test.rs b/libs/persistent_range_query/tests/rsq_test.rs new file mode 100644 index 0000000000..5c7db94a21 --- /dev/null +++ b/libs/persistent_range_query/tests/rsq_test.rs @@ -0,0 +1,56 @@ +use persistent_range_query::naive::*; +use persistent_range_query::ops::rsq::*; +use persistent_range_query::ops::SameElementsInitializer; +use persistent_range_query::{PersistentVecStorage, VecVersion}; +use std::ops::{Mul, Range, Sub}; + +#[derive(Copy, Clone, Debug)] +struct K(u8); + +impl IndexableKey for K { + fn index(all_keys: &Range, key: &Self) -> usize { + (key.0 as usize) - (all_keys.start.0 as usize) + } + + fn element_range(all_keys: &Range, index: usize) -> Range { + K(all_keys.start.0 + index as u8)..K(all_keys.start.0 + index as u8 + 1) + } +} + +struct KDiff(i16); + +impl Sub<&K> for &K { + type Output = KDiff; + + fn sub(self, rhs: &K) -> Self::Output { + KDiff((self.0 as i16) - (rhs.0 as i16)) + } +} + +impl<'a> Mul for &'a i32 { + type Output = i32; + + fn mul(self, rhs: KDiff) -> Self::Output { + self * (rhs.0 as i32) + } +} + +#[test] +fn test_naive() { + let mut s: NaiveVecVersion<_, _, AddAssignModification<_>> = + NaiveVecStorage::new(K(0)..K(12), SameElementsInitializer::new(0i32)); + assert_eq!(*s.get(K(0)..K(12)).sum(), 0); + + s.modify(K(2)..K(5), AddAssignModification::Add(3)); + assert_eq!(*s.get(K(0)..K(12)).sum(), 3 + 3 + 3); + let s_old = s.clone(); + + s.modify(K(3)..K(6), AddAssignModification::Assign(10)); + assert_eq!(*s.get(K(0)..K(12)).sum(), 3 + 10 + 10 + 10); + + s.modify(K(4)..K(7), AddAssignModification::Add(2)); + assert_eq!(*s.get(K(0)..K(12)).sum(), 3 + 10 + 12 + 12 + 2); + + assert_eq!(*s.get(K(4)..K(6)).sum(), 12 + 12); + assert_eq!(*s_old.get(K(4)..K(6)).sum(), 3); +}