const_num_traits/ops/
clmul.rs1#[cfg(target_pointer_width = "16")]
13type UDoubleSize = u32;
14#[cfg(target_pointer_width = "32")]
15type UDoubleSize = u64;
16#[cfg(target_pointer_width = "64")]
17type UDoubleSize = u128;
18
19c0nst::c0nst! {
20pub c0nst trait CarrylessMul: Sized {
22 type Output;
33 fn carryless_mul(self, rhs: Self) -> Self::Output;
34}
35}
36
37macro_rules! carryless_mul_impl {
38 ($($t:ty)*) => {$(
39 c0nst::c0nst! {
40 c0nst impl CarrylessMul for $t {
41 type Output = $t;
42 #[inline]
43 fn carryless_mul(self, rhs: Self) -> $t {
44 let mut result: $t = 0;
45 let mut i = 0u32;
46 while i < <$t>::BITS {
47 let mask = ((rhs >> i) & 1).wrapping_neg();
49 result ^= (self << i) & mask;
50 i += 1;
51 }
52 result
53 }
54 }
55 }
56 )*};
57}
58
59carryless_mul_impl!(usize u8 u16 u32 u64 u128);
60
61c0nst::c0nst! {
62pub c0nst trait WideningCarrylessMul: Sized {
65 type Wide;
67
68 fn widening_carryless_mul(self, rhs: Self) -> Self::Wide;
78}
79}
80
81macro_rules! widening_carryless_mul_impl {
82 ($($t:ty => $w:ty;)*) => {$(
83 c0nst::c0nst! {
84 c0nst impl WideningCarrylessMul for $t {
85 type Wide = $w;
86
87 #[inline]
88 fn widening_carryless_mul(self, rhs: Self) -> $w {
89 let wide = self as $w;
90 let mut result: $w = 0;
91 let mut i = 0u32;
92 while i < <$t>::BITS {
93 let mask = (((rhs >> i) & 1) as $w).wrapping_neg();
95 result ^= (wide << i) & mask;
96 i += 1;
97 }
98 result
99 }
100 }
101 }
102 )*};
103}
104
105widening_carryless_mul_impl! {
106 u8 => u16;
107 u16 => u32;
108 u32 => u64;
109 u64 => u128;
110}
111
112c0nst::c0nst! {
113pub c0nst trait CarryingCarrylessMul: Sized {
116 type Output;
132 fn carrying_carryless_mul(self, rhs: Self, carry: Self) -> (Self::Output, Self::Output);
133}
134}
135
136macro_rules! carrying_carryless_mul_impl {
137 ($($t:ty => $w:ty;)*) => {$(
139 c0nst::c0nst! {
140 c0nst impl CarryingCarrylessMul for $t {
141 type Output = $t;
142 #[inline]
143 fn carrying_carryless_mul(self, rhs: Self, carry: Self) -> ($t, $t) {
144 let a = self as $w;
145 let mut p: $w = 0;
146 let mut i = 0u32;
147 while i < <$t>::BITS {
148 let mask = (((rhs >> i) & 1) as $w).wrapping_neg();
150 p ^= (a << i) & mask;
151 i += 1;
152 }
153 ((p as $t) ^ carry, (p >> <$t>::BITS) as $t)
154 }
155 }
156 }
157 )*};
158}
159
160carrying_carryless_mul_impl! {
161 u8 => u16;
162 u16 => u32;
163 u32 => u64;
164 u64 => u128;
165 usize => UDoubleSize;
166}
167
168const fn wide_clmul_u64(a: u64, b: u64) -> u128 {
171 let a = a as u128;
172 let mut p: u128 = 0;
173 let mut i = 0u32;
174 while i < 64 {
175 let mask = (((b >> i) & 1) as u128).wrapping_neg();
177 p ^= (a << i) & mask;
178 i += 1;
179 }
180 p
181}
182
183c0nst::c0nst! {
184c0nst impl CarryingCarrylessMul for u128 {
185 type Output = u128;
186 #[inline]
187 fn carrying_carryless_mul(self, rhs: Self, carry: Self) -> (u128, u128) {
188 let x0 = self as u64;
189 let x1 = (self >> 64) as u64;
190 let y0 = rhs as u64;
191 let y1 = (rhs >> 64) as u64;
192
193 let z0 = wide_clmul_u64(x0, y0);
194 let z2 = wide_clmul_u64(x1, y1);
195 let z3 = wide_clmul_u64(x0 ^ x1, y0 ^ y1);
197 let z1 = z3 ^ z0 ^ z2;
198
199 let lo = z0 ^ (z1 << 64);
200 let hi = z2 ^ (z1 >> 64);
201 (lo ^ carry, hi)
202 }
203}
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn carrying_carryless() {
212 for a in [0u8, 1, 3, 0x80, 0xAB, 0xFF] {
214 for b in [0u8, 1, 3, 0x80, 0xCD, 0xFF] {
215 let wide = WideningCarrylessMul::widening_carryless_mul(a, b);
216 let (lo, hi) = CarryingCarrylessMul::carrying_carryless_mul(a, b, 0x5A);
217 assert_eq!(lo, (wide as u8) ^ 0x5A);
218 assert_eq!(hi, (wide >> 8) as u8);
219 }
220 }
221 fn reference(a: u128, b: u128) -> (u128, u128) {
223 let (mut lo, mut hi) = (0u128, 0u128);
224 for i in 0..128u32 {
225 if (b >> i) & 1 == 1 {
226 lo ^= a.wrapping_shl(i);
227 if i > 0 {
228 hi ^= a >> (128 - i);
229 }
230 }
231 }
232 (lo, hi)
233 }
234 let vals = [
235 0u128,
236 1,
237 u128::MAX,
238 0x0123_4567_89ab_cdef_fedc_ba98_7654_3210,
239 1 << 127,
240 0xdead_beef,
241 ];
242 for &a in &vals {
243 for &b in &vals {
244 let (lo, hi) = CarryingCarrylessMul::carrying_carryless_mul(a, b, 0);
245 assert_eq!((lo, hi), reference(a, b), "{a:#x} {b:#x}");
246 }
247 }
248 let (lo0, hi0) = CarryingCarrylessMul::carrying_carryless_mul(u128::MAX, u128::MAX, 0);
250 let (lo1, hi1) = CarryingCarrylessMul::carrying_carryless_mul(u128::MAX, u128::MAX, 0xFF);
251 assert_eq!((lo0 ^ 0xFF, hi0), (lo1, hi1));
252 }
253
254 #[test]
255 fn carryless() {
256 assert_eq!(CarrylessMul::carryless_mul(0b11u8, 0b11), 0b101);
257 assert_eq!(CarrylessMul::carryless_mul(0x80u8, 0x80), 0); assert_eq!(
259 WideningCarrylessMul::widening_carryless_mul(0x80u8, 0x80),
260 0x4000u16
261 );
262 assert_eq!(CarrylessMul::carryless_mul(0b1011u32, 0b100), 0b101100);
264 }
265}