use core::marker::PhantomData;
pub trait Semigroup {
fn combine(self, other: Self) -> Self;
#[inline]
fn combine_n(self, n: u32) -> Self
where
Self: Sized + Clone,
{
if n == 0 {
return self;
}
let mut result = self.clone();
for _ in 1..n {
result = result.combine(self.clone());
}
result.combine(self)
}
#[inline]
fn combine_all_option<I>(iter: I) -> Option<Self>
where
I: IntoIterator<Item = Self>,
Self: Sized,
{
let mut iter = iter.into_iter();
iter.next()
.map(|init| iter.fold(init, |acc, x| acc.combine(x)))
}
}
macro_rules! semigroup_numeric {
($($t:ty)*) => ($(
impl Semigroup for $t {
#[inline]
fn combine(self, other: Self) -> Self { self + other }
}
)*)
}
semigroup_numeric! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 }
impl Semigroup for () {
#[inline]
fn combine(self, _other: Self) -> Self {}
}
macro_rules! semigroup_tuple {
($($idx:tt $t:tt),+) => {
impl<$($t: Semigroup,)*> Semigroup for ($($t,)+)
{
#[inline]
fn combine(self, other: Self) -> Self {
($(
$t :: combine(self.$idx, other.$idx),
)+)
}
}
};
}
semigroup_tuple!(0 A);
semigroup_tuple!(0 A, 1 B);
semigroup_tuple!(0 A, 1 B, 2 C);
semigroup_tuple!(0 A, 1 B, 2 C, 3 D);
semigroup_tuple!(0 A, 1 B, 2 C, 3 D, 4 E);
semigroup_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F);
semigroup_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G);
semigroup_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H);
semigroup_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I);
semigroup_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J);
semigroup_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K);
semigroup_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L);
#[macro_export]
macro_rules! semigroup_append {
($name:ident) => {
impl<T> Semigroup for $name<T> {
#[inline]
fn combine(mut self, mut other: Self) -> Self {
self.append(&mut other);
self
}
}
};
($name:ident, $ct:tt $(+ $dt:tt )*) => {
impl<T: $ct $(+ $dt )*> Semigroup for $name<T> {
#[inline]
fn combine(mut self, mut other: Self) -> Self {
self.append(&mut other);
self
}
}
};
}
#[macro_export]
macro_rules! semigroup_extend {
($name:ident) => {
impl<T> Semigroup for $name<T> {
#[inline]
fn combine(mut self, other: Self) -> Self {
self.extend(other);
self
}
}
};
($name:ident, $ct:tt $(+ $dt:tt )*) => {
impl<T: $ct $(+ $dt )*> Semigroup for $name<T> {
#[inline]
fn combine(mut self, other: Self) -> Self {
self.extend(other);
self
}
}
};
}
impl<T> Semigroup for PhantomData<T> {
#[inline]
fn combine(self, _other: Self) -> Self {
PhantomData
}
}
impl<T: Semigroup> Semigroup for Option<T> {
#[inline]
fn combine(self, other: Self) -> Self {
match (self, other) {
(Some(lhs), Some(rhs)) => Some(lhs.combine(rhs)),
(x, y) => x.or(y),
}
}
}
if_std! {
use std::boxed::Box;
use std::collections::*;
use std::hash::Hash;
use std::string::String;
use std::vec::Vec;
impl Semigroup for String {
#[inline]
fn combine(self, other: Self) -> Self {
self + &other
}
}
impl<T: Semigroup> Semigroup for Box<T> {
#[inline]
fn combine(self, other: Self) -> Self {
Box::new((*self).combine(*other))
}
}
semigroup_extend!(Vec);
semigroup_append!(LinkedList);
semigroup_append!(VecDeque);
semigroup_append!(BinaryHeap, Ord);
semigroup_append!(BTreeSet, Ord);
semigroup_extend!(HashSet, Eq + Hash);
impl<K: Eq + Hash, V: Semigroup> Semigroup for HashMap<K, V> {
#[inline]
fn combine(self, other: Self) -> Self {
let (mut acc, other) = if self.len() > other.len() {
(self, other)
} else {
(other, self)
};
for (k, v) in other {
if let Some(v_acc ) = acc.remove(&k){
acc.insert(k, v.combine(v_acc));
} else {
acc.insert(k, v);
}
}
acc
}
}
}