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}