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

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

  • 演算: std::ops::Mul
  • 単位元: Monoid::one()
  • 逆元: Group::inv()

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

  • 演算: std::ops::Add
  • 単位元: AGroup::zero()
  • 逆元: std::ops::neg

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

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

/// 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 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());
    }
}