fixed_bigint/fixeduint/
parity_impl.rs1use super::{FixedUInt, MachineWord};
28use crate::machineword::ConstMachineWord;
29use const_num_traits::ops::ct::CtParity;
30use const_num_traits::{ConstOne, ConstZero, Parity, Personality};
31use subtle::{Choice, ConstantTimeEq};
32
33c0nst::c0nst! {
34 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> Parity for FixedUInt<T, N, P> {
35 fn is_odd(self) -> bool {
36 if N == 0 {
39 false
40 } else {
41 (self.array[0] & <T as ConstOne>::ONE)
42 != <T as ConstZero>::ZERO
43 }
44 }
45 fn is_even(self) -> bool {
46 !<Self as Parity>::is_odd(self)
47 }
48 }
49
50 }
54
55impl<T, const N: usize, P: Personality> CtParity for FixedUInt<T, N, P>
60where
61 T: MachineWord + ConstantTimeEq,
62{
63 fn ct_is_odd(&self) -> Choice {
69 if N == 0 {
70 return Choice::from(0);
72 }
73 let lsb = self.array[0] & <T as ConstOne>::ONE;
74 !lsb.ct_eq(&<T as ConstZero>::ZERO)
75 }
76
77 fn ct_is_even(&self) -> Choice {
78 !<Self as CtParity>::ct_is_odd(self)
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use const_num_traits::{Ct, Nct};
86
87 type U16Nct = FixedUInt<u8, 2, Nct>;
88 type U16Ct = FixedUInt<u8, 2, Ct>;
89
90 #[test]
91 fn parity_nct() {
92 assert!(Parity::is_odd(U16Nct::from(1u8)));
93 assert!(Parity::is_odd(U16Nct::from(3u8)));
94 assert!(Parity::is_odd(U16Nct::from(0xFFFFu16)));
95 assert!(!Parity::is_odd(U16Nct::from(0u8)));
96 assert!(!Parity::is_odd(U16Nct::from(2u8)));
97 assert!(!Parity::is_odd(U16Nct::from(0xFFFEu16)));
98
99 assert!(Parity::is_even(U16Nct::from(0u8)));
100 assert!(!Parity::is_even(U16Nct::from(1u8)));
101 }
102
103 #[test]
104 fn parity_ct() {
105 let one_ct: U16Ct = FixedUInt::<u8, 2, Nct>::from(1u8).into();
106 let two_ct: U16Ct = FixedUInt::<u8, 2, Nct>::from(2u8).into();
107 assert!(Parity::is_odd(one_ct));
108 assert!(Parity::is_even(two_ct));
109 }
110
111 #[test]
112 #[allow(clippy::needless_borrows_for_generic_args)]
113 fn parity_ref() {
114 let v = U16Nct::from(7u8);
115 assert!(Parity::is_odd(&v));
116 assert!(!Parity::is_even(&v));
117 }
118
119 c0nst::c0nst! {
121 pub c0nst fn const_is_odd<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(v: FixedUInt<T, N, P>) -> bool {
122 Parity::is_odd(v)
123 }
124 pub c0nst fn const_is_even<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(v: FixedUInt<T, N, P>) -> bool {
125 Parity::is_even(v)
126 }
127 }
128
129 #[test]
130 fn nightly_const_eval_parity() {
131 assert!(const_is_odd(U16Nct::from(7u8)));
133 assert!(const_is_even(U16Nct::from(8u8)));
134
135 #[cfg(feature = "nightly")]
136 {
137 const ODD: U16Nct = FixedUInt::from_array([7, 0]);
138 const EVEN: U16Nct = FixedUInt::from_array([8, 0]);
139 const IS_ODD: bool = const_is_odd(ODD);
140 const IS_EVEN: bool = const_is_even(EVEN);
141 const IS_ODD_OF_EVEN: bool = const_is_odd(EVEN);
142 assert!(IS_ODD);
143 assert!(IS_EVEN);
144 assert!(!IS_ODD_OF_EVEN);
145 }
146 }
147
148 #[test]
151 fn ct_parity_nct() {
152 assert!(bool::from(CtParity::ct_is_odd(&U16Nct::from(1u8))));
155 assert!(bool::from(CtParity::ct_is_odd(&U16Nct::from(3u8))));
156 assert!(bool::from(CtParity::ct_is_odd(&U16Nct::from(0xFFFFu16))));
157 assert!(!bool::from(CtParity::ct_is_odd(&U16Nct::from(0u8))));
158 assert!(!bool::from(CtParity::ct_is_odd(&U16Nct::from(2u8))));
159
160 assert!(bool::from(CtParity::ct_is_even(&U16Nct::from(0u8))));
161 assert!(!bool::from(CtParity::ct_is_even(&U16Nct::from(1u8))));
162 }
163
164 #[test]
165 fn ct_parity_ct() {
166 let one_ct: U16Ct = FixedUInt::<u8, 2, Nct>::from(1u8).into();
167 let two_ct: U16Ct = FixedUInt::<u8, 2, Nct>::from(2u8).into();
168 assert!(bool::from(CtParity::ct_is_odd(&one_ct)));
169 assert!(bool::from(CtParity::ct_is_even(&two_ct)));
170 }
171
172 #[test]
173 fn ct_parity_agrees_with_parity() {
174 for v in [
178 0u16, 1, 2, 3, 7, 8, 0xFE, 0xFF, 0x100, 0x101, 0xFFFE, 0xFFFF,
179 ] {
180 let nct = U16Nct::from(v);
181 let ct: U16Ct = U16Nct::from(v).into();
182 assert_eq!(
183 bool::from(CtParity::ct_is_odd(&nct)),
184 Parity::is_odd(nct),
185 "Nct ct_is_odd disagrees with is_odd at v={v}"
186 );
187 assert_eq!(
188 bool::from(CtParity::ct_is_odd(&ct)),
189 Parity::is_odd(ct),
190 "Ct ct_is_odd disagrees with is_odd at v={v}"
191 );
192 }
193 }
194
195 #[test]
198 fn odd_new_ct_round_trips_for_ct_carrier() {
199 use const_num_traits::Odd;
200
201 let odd_ct: U16Ct = FixedUInt::<u8, 2, Nct>::from(7u8).into();
202 let even_ct: U16Ct = FixedUInt::<u8, 2, Nct>::from(8u8).into();
203
204 let p_odd = Odd::<U16Ct>::new_ct(odd_ct);
205 let p_even = Odd::<U16Ct>::new_ct(even_ct);
206
207 assert!(
208 bool::from(p_odd.is_some()),
209 "Odd::new_ct(7) should mask Some"
210 );
211 assert!(
212 !bool::from(p_even.is_some()),
213 "Odd::new_ct(8) should mask None"
214 );
215
216 let recovered = p_odd.unwrap();
217 assert_eq!(recovered.get(), odd_ct);
218 }
219}