use smallvec::SmallVec;
pub trait Monoid: Clone + Send + Sync {
fn identity() -> Self;
fn combine(&self, other: &Self) -> Self;
}
pub trait MonoidReduce: Monoid {
fn reduce<I>(iter: I) -> Self
where
I: IntoIterator<Item = Self>,
I::IntoIter: ExactSizeIterator,
{
let mut items: SmallVec<[Self; 16]> = iter.into_iter().collect();
if items.is_empty() {
return Self::identity();
}
while items.len() > 1 {
let mut next_level = SmallVec::<[Self; 16]>::new();
let mut i = 0;
while i < items.len() {
if i + 1 < items.len() {
next_level.push(items[i].combine(&items[i + 1]));
i += 2;
} else {
next_level.push(items[i].clone());
i += 1;
}
}
items = next_level;
}
items.into_iter().next().unwrap()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Clone, PartialEq)]
struct TestMonoid(i32);
impl Monoid for TestMonoid {
fn identity() -> Self {
TestMonoid(0)
}
fn combine(&self, other: &Self) -> Self {
TestMonoid(self.0 + other.0)
}
}
impl MonoidReduce for TestMonoid {}
#[test]
fn test_identity_property() {
let a = TestMonoid(42);
let id = TestMonoid::identity();
assert_eq!(a.combine(&id), a);
assert_eq!(id.combine(&a), a);
}
#[test]
fn test_associativity() {
let a = TestMonoid(1);
let b = TestMonoid(2);
let c = TestMonoid(3);
let left = a.combine(&b).combine(&c);
let right = a.combine(&b.combine(&c));
assert_eq!(left, right);
}
#[test]
fn test_reduce_empty() {
let empty: Vec<TestMonoid> = vec![];
let result = TestMonoid::reduce(empty);
assert_eq!(result, TestMonoid::identity());
}
#[test]
fn test_reduce_single() {
let single = vec![TestMonoid(42)];
let result = TestMonoid::reduce(single);
assert_eq!(result, TestMonoid(42));
}
#[test]
fn test_reduce_multiple() {
let values = vec![TestMonoid(1), TestMonoid(2), TestMonoid(3), TestMonoid(4)];
let result = TestMonoid::reduce(values);
assert_eq!(result, TestMonoid(10));
}
}