1use crate::{Choice, WideWord, Word};
4use core::hint::black_box;
5
6#[inline(always)]
8#[allow(clippy::cast_possible_truncation)]
9pub(crate) const fn carrying_add(lhs: Word, rhs: Word, carry: Word) -> (Word, Word) {
10 let a = lhs as WideWord;
13 let b = rhs as WideWord;
14 let carry = carry as WideWord;
15 let ret = a + b + carry;
16 (ret as Word, (ret >> Word::BITS) as Word)
17}
18
19#[inline(always)]
21pub(crate) const fn overflowing_add(lhs: Word, rhs: Word) -> (Word, Word) {
22 let (res, carry) = lhs.overflowing_add(rhs);
23 (res, carry as Word)
24}
25
26#[inline]
31pub(crate) const fn borrowing_sub(lhs: Word, rhs: Word, borrow: Word) -> (Word, Word) {
32 let difference = lhs
35 .wrapping_sub(rhs)
36 .wrapping_sub(borrow >> (Word::BITS - 1));
37
38 let borrow = ((!lhs & rhs) | ((!lhs | rhs) & difference)) >> (Word::BITS - 1);
40
41 (difference, black_box(borrow).wrapping_neg())
44}
45
46#[inline(always)]
48#[allow(clippy::cast_possible_truncation)]
49pub(crate) const fn widening_mul(lhs: Word, rhs: Word) -> (Word, Word) {
50 let a = lhs as WideWord;
51 let b = rhs as WideWord;
52 let ret = a * b;
53 (ret as Word, (ret >> Word::BITS) as Word)
54}
55
56#[inline(always)]
58#[allow(clippy::cast_possible_truncation)]
59pub(crate) const fn carrying_mul_add(
60 lhs: Word,
61 rhs: Word,
62 addend: Word,
63 carry: Word,
64) -> (Word, Word) {
65 let lhs = lhs as WideWord;
66 let rhs = rhs as WideWord;
67 let addend = addend as WideWord;
68 let carry = carry as WideWord;
69
70 let ret = ((lhs * rhs) + addend) + carry;
77 (ret as Word, (ret >> Word::BITS) as Word)
78}
79
80#[inline]
82#[allow(clippy::cast_possible_truncation)]
83pub(crate) const fn leading_zeros(value: Word) -> u32 {
84 if cfg!(any(target_arch = "aarch64", target_arch = "x86_64")) {
85 value.leading_zeros()
86 } else {
87 cpubits::cpubits! {
88 32 => { leading_zeros_u32(value) }
89 64 => {
90 let hi = (value >> 32) as u32;
91 let lo = value as u32;
92 let mask = Choice::from_u32_nz(hi).not().to_u32_mask();
93 leading_zeros_u32(hi).wrapping_add(mask & leading_zeros_u32(lo))
94 }
95 }
96 }
97}
98
99#[inline]
101const fn leading_zeros_u32(value: u32) -> u32 {
102 if cfg!(any(
103 target_arch = "aarch64",
104 target_arch = "x86",
105 target_arch = "x86_64",
106 target_feature = "thumb2",
107 target_feature = "zbb"
108 )) {
109 value.leading_zeros()
110 } else {
111 leading_zeros_u32_soft(value)
112 }
113}
114
115#[inline]
117const fn leading_zeros_u32_soft(mut value: u32) -> u32 {
118 let mut count = 0u32;
119 let mut shift = 16;
120
121 while shift != 0 {
122 let mask = Choice::from_u32_nz(value >> (u32::BITS - shift))
123 .not()
124 .to_u32_mask();
125 value ^= mask & (value ^ (value << shift));
126 count = count.wrapping_add(mask & shift);
127 shift >>= 1;
128 }
129
130 count.wrapping_add(Choice::from_u32_nz(value).not().to_u32_mask() & 1)
131}
132
133#[inline]
135#[allow(clippy::cast_possible_truncation)]
136pub(crate) const fn trailing_zeros(value: Word) -> u32 {
137 if cfg!(any(target_arch = "aarch64", target_arch = "x86_64")) {
138 value.trailing_zeros()
139 } else {
140 cpubits::cpubits! {
141 32 => { trailing_zeros_u32(value) }
142 64 => {
143 let hi = (value >> 32) as u32;
144 let lo = value as u32;
145 let mask = Choice::from_u32_nz(lo).not().to_u32_mask();
146 trailing_zeros_u32(lo).wrapping_add(mask & trailing_zeros_u32(hi))
147 }
148 }
149 }
150}
151
152#[inline]
154const fn trailing_zeros_u32(value: u32) -> u32 {
155 if cfg!(any(
156 target_arch = "aarch64",
157 target_arch = "x86",
158 target_arch = "x86_64",
159 target_feature = "thumb2",
160 target_feature = "zbb"
161 )) {
162 value.trailing_zeros()
163 } else {
164 leading_zeros_u32_soft(value.reverse_bits())
166 }
167}
168
169#[inline]
171pub(crate) const fn u32_max(a: u32, b: u32) -> u32 {
172 Choice::from_u32_lt(a, b).select_u32(a, b)
173}
174
175#[inline]
177pub(crate) const fn u32_min(a: u32, b: u32) -> u32 {
178 Choice::from_u32_lt(a, b).select_u32(b, a)
179}
180
181#[inline(never)]
185#[allow(clippy::cast_possible_truncation, reason = "needs triage")]
186#[allow(clippy::integer_division_remainder_used, reason = "needs triage")]
187pub(crate) const fn u32_rem(n: u32, d: u32) -> u32 {
188 assert!(d > 0, "divisor must be nonzero");
189 let c = u64::MAX / (d as u64) + 1;
190 (((c.wrapping_mul(n as u64) as u128) * d as u128) >> 64) as u32
191}
192
193#[inline(always)]
195pub(crate) const fn u32_bits(n: u32) -> u32 {
196 u32::BITS - n.leading_zeros()
197}
198
199#[allow(clippy::cast_possible_truncation)]
201#[cfg(target_pointer_width = "32")]
202#[inline]
203pub(crate) const fn usize_lt(a: usize, b: usize) -> Choice {
204 Choice::from_u32_lt(a as u32, b as u32)
205}
206
207#[allow(clippy::cast_possible_truncation)]
209#[cfg(target_pointer_width = "64")]
210#[inline]
211pub(crate) const fn usize_lt(a: usize, b: usize) -> Choice {
212 Choice::from_u64_lt(a as u64, b as u64)
213}
214
215cpubits::cpubits! {
216 32 => {
217 #[must_use]
221 pub(crate) const fn u32_invert_odd(value: u32) -> u32 {
222 debug_assert!(value & 1 == 1, "value must be odd");
223 let x = value.wrapping_mul(3) ^ 2;
224 let y = 1u32.wrapping_sub(x.wrapping_mul(value));
225 let (x, y) = (x.wrapping_mul(y.wrapping_add(1)), y.wrapping_mul(y));
226 let (x, y) = (x.wrapping_mul(y.wrapping_add(1)), y.wrapping_mul(y));
227 x.wrapping_mul(y.wrapping_add(1))
228 }
229 }
230}
231
232#[must_use]
242pub(crate) const fn u64_invert_odd(value: u64) -> u64 {
243 debug_assert!(value & 1 == 1, "value must be odd");
244 let x = value.wrapping_mul(3) ^ 2;
245 let y = 1u64.wrapping_sub(x.wrapping_mul(value));
246 let (x, y) = (x.wrapping_mul(y.wrapping_add(1)), y.wrapping_mul(y));
247 let (x, y) = (x.wrapping_mul(y.wrapping_add(1)), y.wrapping_mul(y));
248 let (x, y) = (x.wrapping_mul(y.wrapping_add(1)), y.wrapping_mul(y));
249 x.wrapping_mul(y.wrapping_add(1))
250}
251
252#[cfg(test)]
253mod tests {
254 use super::{u32_max, u32_min, u32_rem, usize_lt};
255 use crate::Word;
256
257 const _: () = {
259 assert!(super::borrowing_sub(0, 0, Word::MAX).0 == Word::MAX);
260 assert!(super::borrowing_sub(0, 0, Word::MAX).1 == Word::MAX);
261 assert!(super::borrowing_sub(1, 0, Word::MAX).1 == 0);
262 assert!(super::leading_zeros(0) == Word::BITS);
263 assert!(super::trailing_zeros(0) == Word::BITS);
264 assert!(super::leading_zeros_u32_soft(0) == u32::BITS);
265 };
266
267 #[test]
268 fn zero_counts() {
269 for bit in 0..Word::BITS {
270 for value in [0, 1 << bit, !(1 << bit), Word::MAX >> bit] {
271 assert_eq!(super::leading_zeros(value), value.leading_zeros());
272 assert_eq!(super::trailing_zeros(value), value.trailing_zeros());
273 }
274 }
275 }
276
277 #[test]
278 fn zero_counts_u32() {
279 for bit in 0..u32::BITS {
280 for value in [0, 1 << bit, !(1 << bit), u32::MAX >> bit] {
281 assert_eq!(super::leading_zeros_u32(value), value.leading_zeros());
282 assert_eq!(super::trailing_zeros_u32(value), value.trailing_zeros());
283 assert_eq!(super::leading_zeros_u32_soft(value), value.leading_zeros());
285 assert_eq!(
286 super::leading_zeros_u32_soft(value.reverse_bits()),
287 value.trailing_zeros()
288 );
289 }
290 }
291 }
292
293 #[test]
294 fn carrying_mul_add_cannot_overflow() {
295 let lhs = Word::MAX;
296 let rhs = Word::MAX;
297 let addend = Word::MAX;
298 let carry_in = Word::MAX;
299 let (result, carry_out) = super::carrying_mul_add(lhs, rhs, addend, carry_in);
300 assert_eq!(result, Word::MAX);
301 assert_eq!(carry_out, Word::MAX);
302 }
303
304 #[test]
305 fn test_u32_const_min() {
306 assert_eq!(u32_min(0, 5), 0);
307 assert_eq!(u32_min(7, 0), 0);
308 assert_eq!(u32_min(7, 5), 5);
309 assert_eq!(u32_min(7, 7), 7);
310 }
311
312 #[test]
313 fn test_u32_const_max() {
314 assert_eq!(u32_max(0, 5), 5);
315 assert_eq!(u32_max(7, 0), 7);
316 assert_eq!(u32_max(7, 5), 7);
317 assert_eq!(u32_max(7, 7), 7);
318 }
319
320 #[test]
321 fn test_u32_const_rem() {
322 assert_eq!(u32_rem(0, 5), 0);
323 assert_eq!(u32_rem(4, 5), 4);
324 assert_eq!(u32_rem(7, 5), 2);
325 assert_eq!(u32_rem(101, 5), 1);
326 }
327
328 #[test]
329 fn test_usize_const_lt() {
330 assert!(usize_lt(0, 5).to_bool());
331 assert!(!usize_lt(7, 0).to_bool());
332 assert!(!usize_lt(7, 5).to_bool());
333 assert!(!usize_lt(7, 7).to_bool());
334 }
335
336 cpubits::cpubits! {
337 32 => {
338 #[test]
339 fn test_u32_invert_odd() {
340 use super::u32_invert_odd;
341
342 assert_eq!(u32_invert_odd(1), 1);
343 assert_eq!(u32_invert_odd(5).wrapping_mul(5), 1);
344 assert_eq!(u32_invert_odd(u32::MAX).wrapping_mul(u32::MAX), 1);
345 }
346 }
347 }
348}