Skip to main content

relp_num/
binary.rs

1//! # Binary data
2//!
3//! A number type that is either zero or one.
4use std::fmt;
5use std::ops::{Add, AddAssign, Mul};
6
7use crate::{NonZero, Sign, Signed};
8
9/// # Binary
10///
11/// A number that is either `0` or `1`, stored in a single byte.
12///
13/// Used primarily for the cost in the artificial tableau, where a variable is either minimised out
14/// of the basis or ignored. The same shape appears in a matrix provider whose coefficients can only
15/// be `0` or `1`: an incidence matrix, or a set cover. Storing a rational number for such a
16/// coefficient wastes both space and time, so this type stores it in a byte and lets
17/// [`Absorb`](crate::Absorb) apply it to a wide accumulator directly — adding a `Zero` is nothing at
18/// all, multiplying by a `One` is a clone.
19///
20/// The variants are declared in increasing numeric order, so the derived [`Ord`] is the order of
21/// the integers they stand for. Do not reorder them.
22///
23/// See also the documentation in relp::algorithm::two_phase::tableau::kind::artificial::Cost.
24///
25/// # Absent traits
26///
27/// There is deliberately no [`Neg`](std::ops::Neg) and no [`Negateable`](crate::Negateable) impl:
28/// `-1` is not a value this type can represent, so negating `One` would have to lie about its
29/// result. Use [`SignedOne`](crate::fixed::SignedOne) when the coefficient can have either sign.
30///
31/// There is also no `Add<Binary> for Binary`, and hence no [`num_traits::Zero`] or
32/// [`num_traits::One`]: `One + One` is `2`, which this type cannot represent. The only addition
33/// that makes sense is into a type that can hold the result, which is what [`Absorb`](crate::Absorb)
34/// and the `Add<Binary> for i32`-style impls below do.
35#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
36pub enum Binary {
37    /// `0`.
38    #[default]
39    Zero,
40    /// `1`.
41    One,
42}
43
44/// Unlike the other fixed types, this one can be zero, so the answer depends on the value.
45impl NonZero for Binary {
46    #[inline]
47    fn is_not_zero(&self) -> bool {
48        match self {
49            Binary::Zero => false,
50            Binary::One => true,
51        }
52    }
53}
54
55/// This type is zero or positive; it is never negative.
56impl Signed for Binary {
57    #[inline]
58    fn signum(&self) -> Sign {
59        match self {
60            Binary::Zero => Sign::Zero,
61            Binary::One => Sign::Positive,
62        }
63    }
64}
65
66/// Debug forwards to `Display`, because `0` and `1` read better in test output than the variant
67/// names.
68impl fmt::Debug for Binary {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        fmt::Display::fmt(&self, f)
71    }
72}
73
74impl fmt::Display for Binary {
75    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
76        f.write_str(match self {
77            Binary::Zero => "0",
78            Binary::One => "1",
79        })
80    }
81}
82
83/// Multiplication is closed on `{0, 1}` and agrees with multiplication of the integers `0` and `1`,
84/// unlike addition, which is why this impl exists and `Add<Binary> for Binary` does not.
85impl Mul<Binary> for Binary {
86    type Output = Self;
87
88    #[inline]
89    fn mul(self, rhs: Binary) -> Self::Output {
90        match (self, rhs) {
91            (Binary::One, Binary::One) => Binary::One,
92            _ => Binary::Zero,
93        }
94    }
95}
96
97macro_rules! define_ops {
98    ($primitive:ident) => {
99        impl From<Binary> for $primitive {
100            fn from(other: Binary) -> Self {
101                From::from(&other)
102            }
103        }
104
105        impl From<&Binary> for $primitive {
106            fn from(other: &Binary) -> Self {
107                match other {
108                    Binary::Zero => 0,
109                    Binary::One => 1,
110                }
111            }
112        }
113
114        impl Add<Binary> for $primitive {
115            type Output = Self;
116
117            fn add(self, rhs: Binary) -> Self::Output {
118                Add::add(self, &rhs)
119            }
120        }
121
122        impl Add<&Binary> for $primitive {
123            type Output = Self;
124
125            fn add(self, rhs: &Binary) -> Self::Output {
126                match rhs {
127                    Binary::Zero => self,
128                    Binary::One => self + 1,
129                }
130            }
131        }
132
133        impl AddAssign<&Binary> for $primitive {
134            fn add_assign(&mut self, rhs: &Binary) {
135                match rhs {
136                    Binary::Zero => {},
137                    Binary::One => *self += 1,
138                }
139            }
140        }
141
142        impl Mul<Binary> for $primitive {
143            type Output = Self;
144
145            fn mul(self, rhs: Binary) -> Self::Output {
146                Mul::mul(self, &rhs)
147            }
148        }
149
150        impl Mul<&Binary> for $primitive {
151            type Output = Self;
152
153            fn mul(self, rhs: &Binary) -> Self::Output {
154                match rhs {
155                    Binary::Zero => 0,
156                    Binary::One => self,
157                }
158            }
159        }
160
161        impl Mul<&Binary> for &$primitive {
162            type Output = $primitive;
163
164            fn mul(self, rhs: &Binary) -> Self::Output {
165                match rhs {
166                    Binary::Zero => 0,
167                    Binary::One => *self,
168                }
169            }
170        }
171    }
172}
173
174define_ops!(i8);
175define_ops!(i16);
176define_ops!(i32);
177define_ops!(i64);
178define_ops!(i128);
179define_ops!(u8);
180define_ops!(u16);
181define_ops!(u32);
182define_ops!(u64);
183define_ops!(u128);
184
185#[cfg(test)]
186mod test {
187    use std::cmp::Ordering;
188
189    use crate::{NonZero, Sign, Signed};
190    use crate::fixed::Binary;
191
192    #[test]
193    fn test_binary() {
194        assert_eq!(Binary::One * Binary::One, Binary::One);
195        assert_eq!(Binary::One * Binary::Zero, Binary::Zero);
196        assert_eq!(Binary::Zero * Binary::Zero, Binary::Zero);
197    }
198
199    #[test]
200    fn test_default() {
201        assert_eq!(Binary::default(), Binary::Zero);
202    }
203
204    #[test]
205    fn test_signum() {
206        assert_eq!(Binary::Zero.signum(), Sign::Zero);
207        assert_eq!(Binary::One.signum(), Sign::Positive);
208        assert!(!Binary::Zero.is_positive());
209        assert!(Binary::One.is_positive());
210        assert!(!Binary::Zero.is_negative());
211        assert!(!Binary::One.is_negative());
212    }
213
214    /// This is the only one of the four fixed types whose answer depends on the value.
215    #[test]
216    fn test_non_zero() {
217        assert!(!Binary::Zero.is_not_zero());
218        assert!(Binary::One.is_not_zero());
219    }
220
221    /// `0 < 1`, and the ordering must be a total order.
222    #[test]
223    fn test_ord() {
224        assert_eq!(Binary::Zero.cmp(&Binary::One), Ordering::Less);
225        assert_eq!(Binary::One.cmp(&Binary::Zero), Ordering::Greater);
226        assert!(Binary::Zero < Binary::One);
227
228        for a in [Binary::Zero, Binary::One] {
229            assert_eq!(a.cmp(&a), Ordering::Equal);
230            for b in [Binary::Zero, Binary::One] {
231                assert_eq!(a.partial_cmp(&b), Some(a.cmp(&b)));
232                assert_eq!(a.cmp(&b), b.cmp(&a).reverse());
233                assert_eq!(a == b, a.cmp(&b) == Ordering::Equal);
234            }
235        }
236    }
237
238    #[test]
239    fn test_display() {
240        assert_eq!(Binary::Zero.to_string(), "0");
241        assert_eq!(Binary::One.to_string(), "1");
242        assert_eq!(format!("{:?}", Binary::Zero), "0");
243        assert_eq!(format!("{:?}", Binary::One), "1");
244    }
245
246    #[test]
247    fn test_integer() {
248        assert_eq!(1 + Binary::One, 2);
249        assert_eq!(-1 + Binary::One, 0);
250        assert_eq!(894 * Binary::One, 894);
251        assert_eq!(-894 * &Binary::One, -894);
252        assert_eq!(0_u8 * &Binary::One, 0);
253
254        assert_eq!(1 + Binary::Zero, 1);
255        assert_eq!(-1 + Binary::Zero, -1);
256        assert_eq!(894 * Binary::Zero, 0);
257        assert_eq!(-894 * &Binary::Zero, 0);
258        assert_eq!(0_u8 * &Binary::Zero, 0);
259    }
260}