Last active
November 15, 2023 23:37
-
-
Save MikuroXina/65e789e65a84442c8a81e1f4da9717b4 to your computer and use it in GitHub Desktop.
Querying group sum of range by event sourcing method with Rust.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Group must satisfy: | |
/// ```rs | |
/// fn test<G: Group>(m: G, n: G, l: G) { | |
/// assert_eq!(m.op(&G::id()), m); | |
/// assert_eq!(G::id().op(&m), m); | |
/// assert_eq!(m.op(&n.op(&l)), m.op(&n).op(&l)); | |
/// assert_eq!(m.op(&m.inv()), G::id()); | |
/// assert_eq!(m.inv().op(&m), G::id()); | |
/// } | |
/// ``` | |
pub trait Group { | |
fn id() -> Self; | |
fn op(&self, other: &Self) -> Self; | |
fn inv(&self) -> Self; | |
} | |
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | |
pub struct RangeQ<T> { | |
cum: Vec<T>, | |
} | |
impl<T> RangeQ<T> { | |
pub const fn new() -> Self { | |
Self { cum: vec![] } | |
} | |
pub fn from<I>(values: I) -> Self | |
where | |
I: IntoIterator<Item = T>, | |
T: Group + Clone, | |
{ | |
Self { | |
cum: values | |
.into_iter() | |
.scan(T::id(), |state, item| { | |
*state = state.op(&item); | |
Some(state.clone()) | |
}) | |
.collect(), | |
} | |
} | |
pub fn len(&self) -> usize { | |
self.cum.len() | |
} | |
pub fn is_empty(&self) -> bool { | |
self.cum.is_empty() | |
} | |
/// Sums up at index from `0` (inclusive) to `end` (exclusive). | |
pub fn sum_from_start_to(&self, end: usize) -> T | |
where | |
T: Clone + Group, | |
{ | |
if end == 0 { | |
T::id() | |
} else { | |
self.cum | |
.get(end - 1) | |
.or_else(|| self.cum.last()) | |
.cloned() | |
.unwrap_or_else(T::id) | |
} | |
} | |
pub fn sum(&self, range: impl std::ops::RangeBounds<usize>) -> T | |
where | |
T: Clone + Group, | |
{ | |
use std::ops::Bound; | |
let start = self.sum_from_start_to(match range.start_bound() { | |
Bound::Included(&i) => i, | |
Bound::Excluded(i) => i + 1, | |
Bound::Unbounded => 0, | |
}); | |
let end = self.sum_from_start_to(match range.end_bound() { | |
Bound::Included(i) => i + 1, | |
Bound::Excluded(&i) => i, | |
Bound::Unbounded => self.cum.len(), | |
}); | |
start.inv().op(&end) | |
} | |
} | |
impl<T: Group + Clone, I: IntoIterator<Item = T>> From<I> for RangeQ<T> { | |
fn from(value: I) -> Self { | |
Self::from(value) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | |
pub struct Sum(pub i64); | |
impl Group for Sum { | |
fn id() -> Self { | |
Sum(0) | |
} | |
fn op(&self, other: &Self) -> Self { | |
Sum(self.0 + other.0) | |
} | |
fn inv(&self) -> Self { | |
Sum(-self.0) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment