Skip to main content

relp_num/
zero.rs

1//! # Zero
2//!
3//! A type that is always zero.
4use std::fmt;
5use std::ops::{Add, AddAssign, Mul, Neg};
6
7use crate::{Sign, Signed, Negateable};
8
9/// # Zero
10///
11/// A ZST who's value is always zero.
12///
13/// Can be used in specific situations where one knows that, for example, the right-hand side `b` is
14/// always zero. Operations related to `b` should then be compiled away because the operations on
15/// its elements are no-ops.
16///
17/// The same applies to a matrix provider whose coefficients are structurally absent: the incidence
18/// matrix of a network is mostly zero, and storing a rational number for each of those entries
19/// wastes both space and time. This type stores the coefficient in no space at all, and
20/// [`Absorb`](crate::Absorb) applies it to a wide accumulator without ever materialising a `0`:
21/// adding it is nothing at all, multiplying by it clears the accumulator.
22///
23/// # Absent traits
24///
25/// There is deliberately no [`NonZero`](crate::NonZero) impl: this type *is* zero, and the whole
26/// point of that trait is to assert the opposite.
27#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
28pub struct Zero;
29
30impl num_traits::Zero for Zero {
31    fn zero() -> Self {
32        Self
33    }
34
35    fn is_zero(&self) -> bool {
36        true
37    }
38}
39
40impl Add for Zero {
41    type Output = Self;
42
43    fn add(self, _: Self) -> Self::Output {
44        Self
45    }
46}
47
48impl Mul for Zero {
49    type Output = Self;
50
51    fn mul(self, _: Self) -> Self::Output {
52        Self
53    }
54}
55
56/// The only value of this type is `0`, which is neither positive nor negative.
57impl Signed for Zero {
58    #[inline]
59    fn signum(&self) -> Sign {
60        Sign::Zero
61    }
62}
63
64/// Negating zero is a no-op, and the result is representable, so this type is negateable.
65impl Negateable for Zero {
66    #[inline]
67    fn negate(&mut self) {
68    }
69}
70
71impl Neg for Zero {
72    type Output = Self;
73
74    #[inline]
75    fn neg(self) -> Self::Output {
76        self
77    }
78}
79
80/// Debug forwards to `Display`, because `0` reads better in test output than `Zero`.
81impl fmt::Debug for Zero {
82    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83        <Self as fmt::Display>::fmt(self, f)
84    }
85}
86
87impl fmt::Display for Zero {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        f.write_str("0")
90    }
91}
92
93macro_rules! define_ops {
94    ($primitive:ident) => {
95        impl From<Zero> for $primitive {
96            fn from(_: Zero) -> Self {
97                0
98            }
99        }
100
101        impl From<&Zero> for $primitive {
102            fn from(_: &Zero) -> Self {
103                0
104            }
105        }
106
107        impl Add<Zero> for $primitive {
108            type Output = Self;
109
110            fn add(self, _: Zero) -> Self::Output {
111                self
112            }
113        }
114
115        impl Add<&Zero> for $primitive {
116            type Output = Self;
117
118            fn add(self, _: &Zero) -> Self::Output {
119                self
120            }
121        }
122
123        impl AddAssign<&Zero> for $primitive {
124            fn add_assign(&mut self, _: &Zero) {
125            }
126        }
127
128        impl Mul<Zero> for $primitive {
129            type Output = Self;
130
131            fn mul(self, _: Zero) -> Self::Output {
132                0
133            }
134        }
135
136        impl Mul<&Zero> for $primitive {
137            type Output = Self;
138
139            fn mul(self, _: &Zero) -> Self::Output {
140                0
141            }
142        }
143
144        impl Mul<&Zero> for &$primitive {
145            type Output = $primitive;
146
147            fn mul(self, _: &Zero) -> Self::Output {
148                0
149            }
150        }
151    }
152}
153
154define_ops!(i8);
155define_ops!(i16);
156define_ops!(i32);
157define_ops!(i64);
158define_ops!(i128);
159define_ops!(u8);
160define_ops!(u16);
161define_ops!(u32);
162define_ops!(u64);
163define_ops!(u128);
164
165#[cfg(test)]
166mod test {
167    use std::cmp::Ordering;
168
169    use crate::{Abs, Negateable, Sign, Signed};
170    use crate::fixed::Zero;
171
172    #[test]
173    fn test() {
174        assert_eq!(Zero, num_traits::Zero::zero());
175        assert!(num_traits::Zero::is_zero(&Zero));
176        assert_eq!(Zero + Zero, Zero);
177        assert_eq!(Zero * Zero, Zero);
178        assert_eq!(Zero.abs(), Zero);
179    }
180
181    /// Constructing a unit struct through `Default` is exactly what is under test here.
182    #[test]
183    #[allow(clippy::default_constructed_unit_structs)]
184    fn test_default() {
185        assert_eq!(Zero::default(), Zero);
186    }
187
188    #[test]
189    fn test_signum() {
190        assert_eq!(Zero.signum(), Sign::Zero);
191        assert!(!Zero.is_positive());
192        assert!(!Zero.is_negative());
193    }
194
195    /// This type is the one of the four fixed types that must *not* implement
196    /// [`NonZero`](crate::NonZero); all it can do is report that it is zero.
197    #[test]
198    fn test_non_zero() {
199        assert!(num_traits::Zero::is_zero(&Zero));
200    }
201
202    #[test]
203    fn test_negate() {
204        let mut value = Zero;
205        value.negate();
206        assert_eq!(value, Zero);
207        assert_eq!(-Zero, Zero);
208    }
209
210    #[test]
211    fn test_ord() {
212        assert_eq!(Zero.cmp(&Zero), Ordering::Equal);
213        assert_eq!(Zero.partial_cmp(&Zero), Some(Ordering::Equal));
214        assert_eq!(Zero, Zero);
215    }
216
217    #[test]
218    fn test_display() {
219        assert_eq!(Zero.to_string(), "0");
220        assert_eq!(format!("{Zero:?}"), "0");
221    }
222
223
224    #[test]
225    fn test_integer() {
226        assert_eq!(1 + Zero, 1);
227        assert_eq!(-1 + Zero, -1);
228        assert_eq!(894 * Zero, 0);
229        assert_eq!(-894 * &Zero, 0);
230        assert_eq!(0_u8 * &Zero, 0);
231    }
232}