persistent_range_query: first draft

This commit is contained in:
Egor Suvorov
2022-11-23 20:52:34 +02:00
parent 85f0975c5a
commit 4c68d019e3
7 changed files with 332 additions and 0 deletions

7
Cargo.lock generated
View File

@@ -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"

View File

@@ -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" }

View File

@@ -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<Key>: 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<Key>, right: &Self, right_range: &Range<Key>) -> 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<Key>, right: &Self, right_range: &Range<Key>)
where
Self: Clone,
{
*left = Self::combine(left, left_range, right, right_range);
}
}
pub trait LazyRangeInitializer<Key, R> {
fn get(&self, range: &Range<Key>) -> 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<Result, Key> {
fn no_op() -> Self;
fn apply<'a>(&self, result: &mut Result, range: &Range<Key>);
fn compose(later: &Self, earlier: &mut Self);
}
pub trait VecVersion<
Key,
Result: RangeQueryResult<Key>,
Modification: RangeModification<Result, Key>,
>: Clone
{
fn get(&self, keys: Range<Key>) -> Result;
fn modify(&mut self, keys: Range<Key>, modification: Modification);
}
pub trait PersistentVecStorage<
Key,
Result: RangeQueryResult<Key>,
Initializer: LazyRangeInitializer<Key, Result>,
Modification: RangeModification<Result, Key>,
>
{
type Version: VecVersion<Key, Result, Modification>;
fn new(all_keys: Range<Key>, initializer: Initializer) -> Self::Version;
}

View File

@@ -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<Key> + Clone,
Modification: RangeModification<Result, Key>,
> {
all_keys: Range<Key>,
values: Vec<Result>,
_modification: PhantomData<Modification>,
}
impl<
Key: Clone,
Result: RangeQueryResult<Key> + Clone,
Modification: RangeModification<Result, Key>,
> Clone for NaiveVecVersion<Key, Result, Modification>
{
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<Self>, key: &Self) -> usize;
fn element_range(all_keys: &Range<Self>, index: usize) -> Range<Self>;
}
impl<
Key: IndexableKey,
Result: Clone + RangeQueryResult<Key>,
Modification: RangeModification<Result, Key>,
> VecVersion<Key, Result, Modification> for NaiveVecVersion<Key, Result, Modification>
{
fn get(&self, keys: Range<Key>) -> 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<Key>, 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<Key>,
Initializer: LazyRangeInitializer<Key, Result>,
Modification: RangeModification<Result, Key>,
> PersistentVecStorage<Key, Result, Initializer, Modification> for NaiveVecStorage
{
type Version = NaiveVecVersion<Key, Result, Modification>;
fn new(all_keys: Range<Key>, 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,
}
}
}

View File

@@ -0,0 +1,14 @@
pub mod rsq;
#[derive(Copy, Clone, Debug)]
pub struct SameElementsInitializer<T> {
initial_element_value: T,
}
impl<T> SameElementsInitializer<T> {
pub fn new(initial_element_value: T) -> Self {
SameElementsInitializer {
initial_element_value,
}
}
}

View File

@@ -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<T> {
sum: T,
}
impl<T> SumResult<T> {
pub fn sum(&self) -> &T {
&self.sum
}
}
impl<T: for<'a> AddAssign<&'a T> + From<u8>, Key> RangeQueryResult<Key> for SumResult<T> {
fn new_for_empty_range() -> Self {
SumResult { sum: 0.into() }
}
fn add(left: &mut Self, _left_range: &Range<Key>, right: &Self, _right_range: &Range<Key>) {
left.sum += &right.sum
}
}
impl<Key, T, TR: Borrow<T>, KeyDiff> LazyRangeInitializer<Key, SumResult<T>>
for SameElementsInitializer<TR>
where
for<'a> &'a T: Mul<KeyDiff, Output = T>,
for<'b> &'b Key: Sub<Output = KeyDiff>,
{
fn get(&self, range: &Range<Key>) -> SumResult<T> {
SumResult {
sum: self.initial_element_value.borrow() * (&range.end - &range.start),
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum AddAssignModification<T> {
None,
Add(T),
Assign(T),
}
impl<T: Clone + for<'a> AddAssign<&'a T>, Key> RangeModification<SumResult<T>, Key>
for AddAssignModification<T>
where
for<'a> SameElementsInitializer<&'a T>: LazyRangeInitializer<Key, SumResult<T>>,
{
fn no_op() -> Self {
AddAssignModification::None
}
fn apply<'a>(&self, result: &'a mut SumResult<T>, range: &Range<Key>) {
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,
}
}
}

View File

@@ -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<Self>, key: &Self) -> usize {
(key.0 as usize) - (all_keys.start.0 as usize)
}
fn element_range(all_keys: &Range<Self>, index: usize) -> Range<Self> {
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<KDiff> 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);
}