bellframe/
parity.rs

1use std::ops::Mul;
2
3// Imports used solely for doc tests
4#[allow(unused_imports)]
5use crate::{Row, RowBuf, Stage};
6
7/// Data type representing the parity of a [`Row`].  To generate these, you probably want to use
8/// [`Row::parity`].  Note that [`Row::parity`] always performs a heap allocation and is
9/// linear-time in the [`Stage`] of the [`Row`].  If you are using `Parity`s as optimisations
10/// within hot code I would recommend computing them upfront on your input [`Row`]s and then
11/// tracking them by hand, probably using the [`*` operator](Parity::mul).
12///
13/// Note that it would be quite cheap for [`Row`]s to track their [`Parity`] all the time, but that
14/// would result in less-than ideal data layouts and doesn't fit with the 'only pay for what you
15/// use' design goal of this library.
16#[repr(u8)]
17#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
18pub enum Parity {
19    /// A given [`Row`] requires an **even** number of [`swap`](Row::swap)s to return to
20    /// [`rounds`](RowBuf::rounds).  This is also often called _in course_ or _positive_.  The
21    /// parity of [`rounds`](RowBuf::rounds) on every [`Stage`].
22    Even = 0,
23    /// A given [`Row`] requires an **odd** number of [`swap`](Row::swap)s to return to
24    /// [`rounds`](RowBuf::rounds).  This is also often called _out of course_ or _negative_.
25    Odd = 1,
26}
27
28impl Parity {
29    /// Maps `true` to [`Parity::Even`] and `false` to [`Parity::Odd`].  On most machines, this
30    /// will not actually do anything and be removed by the compiler.
31    #[inline(always)]
32    pub fn from_is_odd(v: bool) -> Self {
33        match v {
34            false => Parity::Even,
35            true => Parity::Odd,
36        }
37    }
38
39    /// Returns the `Parity` of a given number.
40    #[inline(always)]
41    pub fn from_number(v: usize) -> Parity {
42        Self::from_is_odd(v % 2 != 0)
43    }
44
45    /// Returns the `Parity` of a given number.
46    #[inline(always)]
47    pub fn zero_or_one(self) -> u8 {
48        match self {
49            Parity::Even => 0,
50            Parity::Odd => 1,
51        }
52    }
53
54    /// Returns the `Parity` of a given number.
55    #[inline(always)]
56    pub fn plus_or_minus(self) -> char {
57        match self {
58            Parity::Even => '+',
59            Parity::Odd => '-',
60        }
61    }
62}
63
64impl Mul for Parity {
65    type Output = Self;
66
67    /// 'Multiply' two [`Parity`]s together (this corresponds to xor/uncarried addition where
68    /// `Even` is `0` and `Odd` is `1`).  Also, if you have a [`Row`] `r1` with [`Parity`] `p1` and
69    /// another [`Row`] `r2` with [`Parity`] `p2` then `(r1 * r2).parity()` will always equal `p1 *
70    /// p2`.  Thus, this can be used to convert calls to [`Row::parity`] into an extremely
71    /// cheap (likely single-cycle) update function.
72    #[inline(always)]
73    fn mul(self, rhs: Self) -> Self::Output {
74        Self::from_is_odd(self != rhs)
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use crate::{RowBuf, Stage};
81
82    // In general, it's a bad idea to import enum variants directly, but in this case it makes our
83    // tests much terser
84    use super::Parity::{Even, Odd};
85    use super::*;
86
87    #[test]
88    fn mul() {
89        assert_eq!(Even * Even, Even);
90        assert_eq!(Even * Odd, Odd);
91        assert_eq!(Odd * Even, Odd);
92        assert_eq!(Odd * Odd, Even);
93    }
94
95    #[test]
96    fn from_number() {
97        assert_eq!(Parity::from_number(0), Even);
98        assert_eq!(Parity::from_number(1000), Even);
99        assert_eq!(Parity::from_number(1), Odd);
100        assert_eq!(Parity::from_number(47), Odd);
101    }
102
103    #[test]
104    fn row_parity() {
105        assert_eq!(RowBuf::rounds(Stage::TWO).parity(), Even);
106        assert_eq!(RowBuf::rounds(Stage::MAJOR).parity(), Even);
107        assert_eq!(RowBuf::parse("13245").unwrap().parity(), Odd);
108        assert_eq!(RowBuf::parse("231546").unwrap().parity(), Odd);
109        assert_eq!(RowBuf::parse("231564").unwrap().parity(), Even);
110    }
111}