1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//!
//! Algebraic _additive_ _group_ traits.
//!
//! An algebraic _additive_ _group_ is an _additive_ _monoid_ `M`,
//! where each group element `g` has a unique additive _inverse_
//! element denoted `-g`. The inverse operation is called
//! _negation_.
//!
//! # Axioms
//!
//! ```notrust
//! ∀g ∈ M
//! 
//! Inverse: ∃-g ∈ M: g + -g = -g + g = 0.
//! ```
//!
//! # References
//!
//! See [references] for a formal definition of an additive group.
//!
#![doc(include = "../doc/references.md")]

use monoid::add_monoid::*;


///
/// An algebraic _additive group_.
///
pub trait AddGroup: AddMonoid {

  /// The unique additive inverse of a group element.
  fn negate(&self) -> Self;


  /// The additive "subtraction" of two group elements.
  fn sub(&self, other: &Self) -> Self {
    self.add(&other.negate())
  }


  /// Test the left axiom of negation.
  fn axiom_left_negate(&self) -> bool {
    self.negate().add(self) == Self::zero()
  }


  /// Test the right axiom of negation.
  fn axiom_right_negate(&self) -> bool {
    self.add(&self.negate()) == Self::zero()
  }
}


///
/// A "numeric" algebraic _additive group_.
///
/// `NumAddGroup` trait is for types that only form additive groups
/// when "numeric" comparisons are used, e.g. floating point types.
///
pub trait NumAddGroup: NumAddMonoid {

  /// The unique additive inverse of a group element.
  fn negate(&self) -> Self;


  /// The additive "subtraction" of two group elements.
  fn sub(&self, other: &Self) -> Self {
    self.add(&other.negate())
  }


  /// Numerically test the left axiom of negation.
  fn axiom_left_negate(&self, eps: &Self::Error) -> bool {
    self.negate().add(self).num_eq(&Self::zero(), eps)
  }


  /// Numerically test the right axiom of negation.
  fn axiom_right_negate(&self, eps: &Self::Error) -> bool {
    self.add(&self.negate()).num_eq(&Self::zero(), eps)
  }
}


///
/// Trait implementation macro for integer types.
///
/// A macro used to avoid writing repetitive, boilerplate `AddGroup`
/// implementations for built-in signed integer types.  Probably not
/// needed if Rust had a signed `Integer` super-trait.
///
macro_rules! integer_add_group {
  ($type:ty) => {
    impl AddGroup for $type {

      /// Additive group negation uses "wrapping" negate to
      /// avoid overflow and guarantee the closure axiom.
      fn negate(&self) -> Self {
        self.wrapping_neg()
      }
    }
  };

  ($type:ty, $($others:ty),+) => {
    integer_add_group! {$type}
    integer_add_group! {$($others),+}
  };
}


// Only signed integers form an additive group.
integer_add_group! {
  i8, i16, i32, i64, i128, isize
}


///
/// Trait implementation macro for floating point types.
///
/// A macro used to avoid writing repetitive, boilerplate
/// `NumAddGroup` implementations for built-in signed floating point
/// types.  Probably not needed if Rust had a `Float` super-trait.
///
macro_rules! float_add_group {
  ($type:ty) => {
    impl NumAddGroup for $type {
      
      // Negation is just floating point negation.
      fn negate(&self) -> Self {
        -*self
      }
    }
  };

  ($type:ty, $($others:ty),+) => {
    float_add_group! {$type}
    float_add_group! {$($others),+}
  };
}


// Numeric additive group floating point types.
float_add_group! {
  f32, f64
}


// Module unit tests are in a separate file.
#[cfg(test)]
#[path = "add_group_test.rs"]
mod add_group_test;