代数 - (乗法)群と加法群

積に関する群 \((X,\times,1)\) を Group の名前で モノイド の拡張として定義する.

和に関する群を AGroup (Additive Group) の名前で定義する.

マクロ agroup! はユーザーが定義した型を手早く加法群にする.

agroup! {
    MyStruct<X> where [X: Copy + ...] ;
    zero = MyStruct(0) ;
    add(self, other) = { ... } ;
    neg(self) = { ... } ;
}

agroup!add_assign , sub , sub_assign , sum を自動で定義する.

/// Algebra - Group (*, 1, inv)
use crate::algebra::monoid::*;

pub trait Group: Monoid {
    fn inv(self) -> Self;
}
/// Algebra - AGroup (Additive Group) (+, -, 0)

pub trait AGroup:
    std::ops::Add<Output = Self>
    + std::ops::Sub<Output = Self>
    + std::ops::Neg<Output = Self>
    + std::iter::Sum
where
    Self: std::marker::Sized,
{
    fn zero() -> Self;
}

#[macro_export]
macro_rules! agroup {
    (
        $type:ty where [ $( $params:tt )* ] ;
        zero = $zero:expr ;
        add($self:ident, $y:ident) = $code:block ;
        neg($self_neg:ident) = $code_neg:block
        $(;)*
    ) => {
        impl<$($params)*> std::ops::Add for $type {
            type Output = Self;
            fn add($self, $y: Self) -> Self { $code }
        }
        impl<$($params)*> std::ops::Neg for $type {
            type Output = Self;
            fn neg($self_neg) -> Self { $code_neg }
        }
        impl<$($params)*> std::ops::Sub for $type {
            type Output = Self;
            fn sub($self, other: Self) -> Self { ($self) + (-other) }
        }
        impl<$($params)*> std::ops::AddAssign for $type where Self: Clone {
            fn add_assign(&mut $self, $y: Self) {
                *$self = (*$self).clone() + $y;
            }
        }
        impl<$($params)*> std::ops::SubAssign for $type where Self: Clone {
            fn sub_assign(&mut $self, $y: Self) {
                *$self = (*$self).clone() - $y;
            }
        }
        impl<$($params)*> std::iter::Sum for $type {
            fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
                iter.fold(Self::zero(), std::ops::Add::add)
            }
        }
        impl<$($params)*> AGroup for $type {
            fn zero() -> Self { $zero }
        }
    };
    (
        $type:ty ;
        zero = $zero:expr ;
        add($self:ident, $y:ident) = $code:block ;
        neg($self_neg:ident) = $code_neg:block
        $(;)*
    ) => {
        agroup! { $type where []; zero = $zero; add($self, $y) = $code; neg($self_neg) = $code_neg; }
    };
}

impl AGroup for i64 {
    fn zero() -> Self {
        0
    }
}
impl AGroup for i128 {
    fn zero() -> Self {
        0
    }
}
impl AGroup for f64 {
    fn zero() -> Self {
        0.0
    }
}

#[cfg(test)]
mod test_group {
    use crate::algebra::group_additive::*;
    #[test]
    fn test_agroup_intish() {
        assert_eq!(i64::zero(), 0);
        assert_eq!(i64::zero() + 3, 3);
        assert_eq!(3 - 3, i64::zero());
    }
    #[test]
    fn test_agroup_realish() {
        assert_eq!(f64::zero(), 0.0);
        assert_eq!(f64::zero() + 3.0, 3.0);
        assert_eq!(3.0 - 3.0, f64::zero());
    }
}