use crate::Semigroup;
use std::ops::{Add, Mul};
pub trait Monoid: Semigroup {
fn empty() -> Self;
}
impl<T> Monoid for Vec<T> {
fn empty() -> Self {
Vec::new()
}
}
impl Monoid for String {
fn empty() -> Self {
String::new()
}
}
impl<T: Semigroup> Monoid for Option<T> {
fn empty() -> Self {
None
}
}
macro_rules! impl_monoid_tuple {
($($idx:tt $T:ident),+) => {
impl<$($T: Monoid),+> Monoid for ($($T,)+) {
fn empty() -> Self {
($($T::empty(),)+)
}
}
};
}
impl_monoid_tuple!(0 T1, 1 T2);
impl_monoid_tuple!(0 T1, 1 T2, 2 T3);
impl_monoid_tuple!(0 T1, 1 T2, 2 T3, 3 T4);
impl_monoid_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5);
impl_monoid_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6);
impl_monoid_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7);
impl_monoid_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8);
impl_monoid_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8, 8 T9);
impl_monoid_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8, 8 T9, 9 T10);
impl_monoid_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8, 8 T9, 9 T10, 10 T11);
impl_monoid_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8, 8 T9, 9 T10, 10 T11, 11 T12);
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::hash::Hash;
impl<K, V> Monoid for HashMap<K, V>
where
K: Eq + Hash + Clone,
V: Semigroup + Clone,
{
fn empty() -> Self {
HashMap::new()
}
}
impl<T> Monoid for HashSet<T>
where
T: Eq + Hash,
{
fn empty() -> Self {
HashSet::new()
}
}
impl<K, V> Monoid for BTreeMap<K, V>
where
K: Ord + Clone,
V: Semigroup + Clone,
{
fn empty() -> Self {
BTreeMap::new()
}
}
impl<T> Monoid for BTreeSet<T>
where
T: Ord,
{
fn empty() -> Self {
BTreeSet::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Sum<T>(pub T);
impl<T: Add<Output = T>> Semigroup for Sum<T> {
fn combine(self, other: Self) -> Self {
Sum(self.0 + other.0)
}
}
impl<T: Add<Output = T> + Default> Monoid for Sum<T> {
fn empty() -> Self {
Sum(T::default())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Product<T>(pub T);
pub trait One {
fn one() -> Self;
}
impl One for i8 {
fn one() -> Self {
1
}
}
impl One for i16 {
fn one() -> Self {
1
}
}
impl One for i32 {
fn one() -> Self {
1
}
}
impl One for i64 {
fn one() -> Self {
1
}
}
impl One for i128 {
fn one() -> Self {
1
}
}
impl One for isize {
fn one() -> Self {
1
}
}
impl One for u8 {
fn one() -> Self {
1
}
}
impl One for u16 {
fn one() -> Self {
1
}
}
impl One for u32 {
fn one() -> Self {
1
}
}
impl One for u64 {
fn one() -> Self {
1
}
}
impl One for u128 {
fn one() -> Self {
1
}
}
impl One for usize {
fn one() -> Self {
1
}
}
impl One for f32 {
fn one() -> Self {
1.0
}
}
impl One for f64 {
fn one() -> Self {
1.0
}
}
impl<T: Mul<Output = T>> Semigroup for Product<T> {
fn combine(self, other: Self) -> Self {
Product(self.0 * other.0)
}
}
impl<T: Mul<Output = T> + One> Monoid for Product<T> {
fn empty() -> Self {
Product(T::one())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Max<T>(pub T);
impl<T: Ord> Semigroup for Max<T> {
fn combine(self, other: Self) -> Self {
Max(self.0.max(other.0))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Min<T>(pub T);
impl<T: Ord> Semigroup for Min<T> {
fn combine(self, other: Self) -> Self {
Min(self.0.min(other.0))
}
}
pub fn fold_all<M, I>(iter: I) -> M
where
M: Monoid,
I: IntoIterator<Item = M>,
{
iter.into_iter().fold(M::empty(), |acc, x| acc.combine(x))
}
pub fn reduce<M, I>(iter: I) -> M
where
M: Monoid,
I: IntoIterator<Item = M>,
{
fold_all(iter)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vec_right_identity() {
let v = vec![1, 2, 3];
let empty: Vec<i32> = Monoid::empty();
assert_eq!(v.clone().combine(empty), v);
}
#[test]
fn test_vec_left_identity() {
let v = vec![1, 2, 3];
let empty: Vec<i32> = Monoid::empty();
assert_eq!(empty.combine(v.clone()), v);
}
#[test]
fn test_string_right_identity() {
let s = "hello".to_string();
let empty: String = Monoid::empty();
assert_eq!(s.clone().combine(empty), s);
}
#[test]
fn test_string_left_identity() {
let s = "hello".to_string();
let empty: String = Monoid::empty();
assert_eq!(empty.combine(s.clone()), s);
}
#[test]
fn test_option_right_identity() {
let v = Some(vec![1, 2, 3]);
let empty: Option<Vec<i32>> = Monoid::empty();
assert_eq!(v.clone().combine(empty), v);
}
#[test]
fn test_option_left_identity() {
let v = Some(vec![1, 2, 3]);
let empty: Option<Vec<i32>> = Monoid::empty();
assert_eq!(empty.combine(v.clone()), v);
}
#[test]
fn test_option_combine() {
let v1 = Some(vec![1, 2]);
let v2 = Some(vec![3, 4]);
assert_eq!(v1.combine(v2), Some(vec![1, 2, 3, 4]));
}
#[test]
fn test_option_none_some() {
let v1: Option<Vec<i32>> = None;
let v2 = Some(vec![1, 2]);
assert_eq!(v1.combine(v2), Some(vec![1, 2]));
}
#[test]
fn test_option_some_none() {
let v1 = Some(vec![1, 2]);
let v2: Option<Vec<i32>> = None;
assert_eq!(v1.combine(v2), Some(vec![1, 2]));
}
#[test]
fn test_tuple_identity() {
let t = (vec![1], "hello".to_string());
let empty: (Vec<i32>, String) = Monoid::empty();
assert_eq!(t.clone().combine(empty.clone()), t);
assert_eq!(empty.combine(t.clone()), t);
}
#[test]
fn test_fold_all_vec() {
let vecs = vec![vec![1], vec![2, 3], vec![4]];
let result = fold_all(vecs);
assert_eq!(result, vec![1, 2, 3, 4]);
}
#[test]
fn test_fold_all_empty() {
let vecs: Vec<Vec<i32>> = vec![];
let result = fold_all(vecs);
assert_eq!(result, Vec::<i32>::new());
}
#[test]
fn test_fold_all_string() {
let strings = vec!["Hello".to_string(), " ".to_string(), "World".to_string()];
let result = fold_all(strings);
assert_eq!(result, "Hello World");
}
#[test]
fn test_sum_combine() {
let s1 = Sum(5);
let s2 = Sum(10);
assert_eq!(s1.combine(s2), Sum(15));
}
#[test]
fn test_sum_identity() {
let s = Sum(42);
let empty: Sum<i32> = Monoid::empty();
assert_eq!(s.combine(empty), Sum(42));
assert_eq!(empty.combine(s), Sum(42));
}
#[test]
fn test_sum_fold_all() {
let nums = vec![Sum(1), Sum(2), Sum(3), Sum(4)];
let result = fold_all(nums);
assert_eq!(result, Sum(10));
}
#[test]
fn test_product_combine() {
let p1 = Product(5);
let p2 = Product(10);
assert_eq!(p1.combine(p2), Product(50));
}
#[test]
fn test_product_identity() {
let p = Product(42);
let empty: Product<i32> = Monoid::empty();
assert_eq!(p.combine(empty), Product(42));
assert_eq!(empty.combine(p), Product(42));
}
#[test]
fn test_product_fold_all() {
let nums = vec![Product(2), Product(3), Product(4)];
let result = fold_all(nums);
assert_eq!(result, Product(24));
}
#[test]
fn test_max_combine() {
let m1 = Max(5);
let m2 = Max(10);
assert_eq!(m1.combine(m2), Max(10));
}
#[test]
fn test_min_combine() {
let m1 = Min(5);
let m2 = Min(10);
assert_eq!(m1.combine(m2), Min(5));
}
#[test]
fn test_reduce() {
let vecs = vec![vec![1], vec![2], vec![3]];
let result: Vec<i32> = reduce(vecs);
assert_eq!(result, vec![1, 2, 3]);
}
#[test]
fn test_sum_associativity() {
let a = Sum(1);
let b = Sum(2);
let c = Sum(3);
let left = a.combine(b).combine(c);
let right = a.combine(b.combine(c));
assert_eq!(left, right);
}
#[test]
fn test_product_associativity() {
let a = Product(2);
let b = Product(3);
let c = Product(4);
let left = a.combine(b).combine(c);
let right = a.combine(b.combine(c));
assert_eq!(left, right);
}
#[cfg(test)]
mod proptests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn prop_vec_right_identity(v: Vec<i32>) {
let empty: Vec<i32> = Monoid::empty();
prop_assert_eq!(v.clone().combine(empty), v);
}
#[test]
fn prop_vec_left_identity(v: Vec<i32>) {
let empty: Vec<i32> = Monoid::empty();
prop_assert_eq!(empty.combine(v.clone()), v);
}
#[test]
fn prop_vec_associativity(a: Vec<i32>, b: Vec<i32>, c: Vec<i32>) {
let left = a.clone().combine(b.clone()).combine(c.clone());
let right = a.combine(b.combine(c));
prop_assert_eq!(left, right);
}
#[test]
fn prop_string_right_identity(s: String) {
let empty: String = Monoid::empty();
prop_assert_eq!(s.clone().combine(empty), s);
}
#[test]
fn prop_string_left_identity(s: String) {
let empty: String = Monoid::empty();
prop_assert_eq!(empty.combine(s.clone()), s);
}
#[test]
fn prop_string_associativity(a: String, b: String, c: String) {
let left = a.clone().combine(b.clone()).combine(c.clone());
let right = a.combine(b.combine(c));
prop_assert_eq!(left, right);
}
#[test]
fn prop_sum_right_identity(n in -1000i32..1000i32) {
let s = Sum(n);
let empty: Sum<i32> = Monoid::empty();
prop_assert_eq!(s.combine(empty), Sum(n));
}
#[test]
fn prop_sum_left_identity(n in -1000i32..1000i32) {
let s = Sum(n);
let empty: Sum<i32> = Monoid::empty();
prop_assert_eq!(empty.combine(s), Sum(n));
}
#[test]
fn prop_sum_associativity(a in -1000i32..1000i32, b in -1000i32..1000i32, c in -1000i32..1000i32) {
let sa = Sum(a);
let sb = Sum(b);
let sc = Sum(c);
let left = sa.combine(sb).combine(sc);
let right = sa.combine(sb.combine(sc));
prop_assert_eq!(left, right);
}
#[test]
fn prop_product_right_identity(n in -100i32..100i32) {
let p = Product(n);
let empty: Product<i32> = Monoid::empty();
prop_assert_eq!(p.combine(empty), Product(n));
}
#[test]
fn prop_product_left_identity(n in -100i32..100i32) {
let p = Product(n);
let empty: Product<i32> = Monoid::empty();
prop_assert_eq!(empty.combine(p), Product(n));
}
#[test]
fn prop_product_associativity(a in -10i32..10i32, b in -10i32..10i32, c in -10i32..10i32) {
let pa = Product(a);
let pb = Product(b);
let pc = Product(c);
let left = pa.combine(pb).combine(pc);
let right = pa.combine(pb.combine(pc));
prop_assert_eq!(left, right);
}
#[test]
fn prop_option_right_identity(v: Option<Vec<i32>>) {
let empty: Option<Vec<i32>> = Monoid::empty();
prop_assert_eq!(v.clone().combine(empty), v);
}
#[test]
fn prop_option_left_identity(v: Option<Vec<i32>>) {
let empty: Option<Vec<i32>> = Monoid::empty();
prop_assert_eq!(empty.combine(v.clone()), v);
}
#[test]
fn prop_option_associativity(
a: Option<Vec<i32>>,
b: Option<Vec<i32>>,
c: Option<Vec<i32>>
) {
let left = a.clone().combine(b.clone()).combine(c.clone());
let right = a.combine(b.combine(c));
prop_assert_eq!(left, right);
}
#[test]
fn prop_fold_all_vec(vecs: Vec<Vec<i32>>) {
let result = fold_all(vecs.clone());
let expected: Vec<i32> = vecs.into_iter().flatten().collect();
prop_assert_eq!(result, expected);
}
#[test]
fn prop_fold_all_string(strings: Vec<String>) {
let result = fold_all(strings.clone());
let expected: String = strings.join("");
prop_assert_eq!(result, expected);
}
}
}
}