1use super::{FixedUInt, MachineWord};
21use crate::const_numtraits::{
22 ConstBorrowingSub, ConstCarryingAdd, ConstCarryingMul, ConstWideningMul,
23};
24use crate::machineword::ConstMachineWord;
25use crate::patch_num_traits::{CarryingMul, WideningMul};
26
27c0nst::c0nst! {
28 c0nst fn add_with_carry<T: [c0nst] ConstMachineWord + [c0nst] ConstCarryingAdd, const N: usize>(
31 a: &[T; N],
32 b: &[T; N],
33 carry_in: bool,
34 ) -> ([T; N], bool) {
35 let mut result = [T::zero(); N];
36 let mut carry = carry_in;
37 let mut i = 0usize;
38 while i < N {
39 let (sum, c) = ConstCarryingAdd::carrying_add(a[i], b[i], carry);
40 result[i] = sum;
41 carry = c;
42 i += 1;
43 }
44 (result, carry)
45 }
46
47 c0nst fn sub_with_borrow<T: [c0nst] ConstMachineWord + [c0nst] ConstBorrowingSub, const N: usize>(
50 a: &[T; N],
51 b: &[T; N],
52 borrow_in: bool,
53 ) -> ([T; N], bool) {
54 let mut result = [T::zero(); N];
55 let mut borrow = borrow_in;
56 let mut i = 0usize;
57 while i < N {
58 let (diff, b) = ConstBorrowingSub::borrowing_sub(a[i], b[i], borrow);
59 result[i] = diff;
60 borrow = b;
61 i += 1;
62 }
63 (result, borrow)
64 }
65
66 impl<T: [c0nst] ConstMachineWord + [c0nst] ConstCarryingAdd + MachineWord, const N: usize> c0nst ConstCarryingAdd for FixedUInt<T, N> {
67 fn carrying_add(self, rhs: Self, carry: bool) -> (Self, bool) {
68 let (array, carry_out) = add_with_carry(&self.array, &rhs.array, carry);
69 (Self { array }, carry_out)
70 }
71 }
72
73 impl<T: [c0nst] ConstMachineWord + [c0nst] ConstBorrowingSub + MachineWord, const N: usize> c0nst ConstBorrowingSub for FixedUInt<T, N> {
74 fn borrowing_sub(self, rhs: Self, borrow: bool) -> (Self, bool) {
75 let (array, borrow_out) = sub_with_borrow(&self.array, &rhs.array, borrow);
76 (Self { array }, borrow_out)
77 }
78 }
79
80 c0nst fn get_at<T: [c0nst] ConstMachineWord, const N: usize>(
82 lo: &[T; N], hi: &[T; N], pos: usize
83 ) -> T {
84 if pos < N { lo[pos] } else if pos < 2 * N { hi[pos - N] } else { T::zero() }
85 }
86
87 c0nst fn set_at<T: [c0nst] ConstMachineWord, const N: usize>(
89 lo: &mut [T; N], hi: &mut [T; N], pos: usize, val: T
90 ) {
91 if pos < N { lo[pos] = val; } else if pos < 2 * N { hi[pos - N] = val; }
92 }
93
94 impl<T: [c0nst] ConstMachineWord + [c0nst] ConstCarryingAdd + [c0nst] ConstBorrowingSub + MachineWord, const N: usize> c0nst ConstWideningMul for FixedUInt<T, N> {
95 fn widening_mul(self, rhs: Self) -> (Self, Self) {
96 let mut result_low = [T::zero(); N];
99 let mut result_high = [T::zero(); N];
100
101 let mut i = 0usize;
102 while i < N {
103 let mut j = 0usize;
104 while j < N {
105 let pos = i + j;
106 let (mul_lo, mul_hi) = ConstWideningMul::widening_mul(self.array[i], rhs.array[j]);
107
108 let cur0 = get_at(&result_low, &result_high, pos);
111 let (sum0, c0) = ConstCarryingAdd::carrying_add(cur0, mul_lo, false);
112 set_at(&mut result_low, &mut result_high, pos, sum0);
113
114 let cur1 = get_at(&result_low, &result_high, pos + 1);
116 let (sum1, c1) = ConstCarryingAdd::carrying_add(cur1, mul_hi, c0);
117 set_at(&mut result_low, &mut result_high, pos + 1, sum1);
118
119 let mut carry = c1;
121 let mut p = pos + 2;
122 while carry && p < 2 * N {
123 let cur = get_at(&result_low, &result_high, p);
124 let (sum, c) = ConstCarryingAdd::carrying_add(cur, T::zero(), true);
125 set_at(&mut result_low, &mut result_high, p, sum);
126 carry = c;
127 p += 1;
128 }
129
130 j += 1;
131 }
132 i += 1;
133 }
134
135 (Self { array: result_low }, Self { array: result_high })
136 }
137 }
138
139 impl<T: [c0nst] ConstMachineWord + [c0nst] ConstCarryingAdd + [c0nst] ConstBorrowingSub + MachineWord, const N: usize> c0nst ConstCarryingMul for FixedUInt<T, N> {
140 fn carrying_mul(self, rhs: Self, carry: Self) -> (Self, Self) {
141 let (lo, hi) = ConstWideningMul::widening_mul(self, rhs);
143
144 let (lo2, c) = add_with_carry(&lo.array, &carry.array, false);
146
147 let zeros = [T::zero(); N];
149 let (hi2, _) = add_with_carry(&hi.array, &zeros, c);
150
151 (Self { array: lo2 }, Self { array: hi2 })
152 }
153
154 fn carrying_mul_add(self, rhs: Self, addend: Self, carry: Self) -> (Self, Self) {
155 let (lo, hi) = ConstWideningMul::widening_mul(self, rhs);
157
158 let (lo2, c1) = add_with_carry(&lo.array, &carry.array, false);
160
161 let (lo3, c2) = add_with_carry(&lo2, &addend.array, false);
163
164 let zeros = [T::zero(); N];
166 let (hi2, _) = add_with_carry(&hi.array, &zeros, c1);
167 let (hi3, _) = add_with_carry(&hi2, &zeros, c2);
168
169 (Self { array: lo3 }, Self { array: hi3 })
170 }
171 }
172}
173
174impl<T: ConstMachineWord + ConstCarryingAdd + ConstBorrowingSub + MachineWord, const N: usize>
176 WideningMul for FixedUInt<T, N>
177{
178 #[inline]
179 fn widening_mul(self, rhs: Self) -> (Self, Self) {
180 <Self as ConstWideningMul>::widening_mul(self, rhs)
181 }
182}
183
184impl<T: ConstMachineWord + ConstCarryingAdd + ConstBorrowingSub + MachineWord, const N: usize>
186 CarryingMul for FixedUInt<T, N>
187{
188 #[inline]
189 fn carrying_mul(self, rhs: Self, carry: Self) -> (Self, Self) {
190 <Self as ConstCarryingMul>::carrying_mul(self, rhs, carry)
191 }
192
193 #[inline]
194 fn carrying_mul_add(self, rhs: Self, addend: Self, carry: Self) -> (Self, Self) {
195 <Self as ConstCarryingMul>::carrying_mul_add(self, rhs, addend, carry)
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202
203 type U16 = FixedUInt<u8, 2>;
204 type U32 = FixedUInt<u8, 4>;
205
206 c0nst::c0nst! {
207 pub c0nst fn const_carrying_add<T: [c0nst] ConstMachineWord + [c0nst] ConstCarryingAdd + [c0nst] ConstBorrowingSub + MachineWord, const N: usize>(
208 a: FixedUInt<T, N>,
209 b: FixedUInt<T, N>,
210 carry: bool,
211 ) -> (FixedUInt<T, N>, bool) {
212 ConstCarryingAdd::carrying_add(a, b, carry)
213 }
214
215 pub c0nst fn const_borrowing_sub<T: [c0nst] ConstMachineWord + [c0nst] ConstCarryingAdd + [c0nst] ConstBorrowingSub + MachineWord, const N: usize>(
216 a: FixedUInt<T, N>,
217 b: FixedUInt<T, N>,
218 borrow: bool,
219 ) -> (FixedUInt<T, N>, bool) {
220 ConstBorrowingSub::borrowing_sub(a, b, borrow)
221 }
222
223 pub c0nst fn const_widening_mul<T: [c0nst] ConstMachineWord + [c0nst] ConstCarryingAdd + [c0nst] ConstBorrowingSub + MachineWord, const N: usize>(
224 a: FixedUInt<T, N>,
225 b: FixedUInt<T, N>,
226 ) -> (FixedUInt<T, N>, FixedUInt<T, N>) {
227 ConstWideningMul::widening_mul(a, b)
228 }
229
230 pub c0nst fn const_carrying_mul<T: [c0nst] ConstMachineWord + [c0nst] ConstCarryingAdd + [c0nst] ConstBorrowingSub + MachineWord, const N: usize>(
231 a: FixedUInt<T, N>,
232 b: FixedUInt<T, N>,
233 carry: FixedUInt<T, N>,
234 ) -> (FixedUInt<T, N>, FixedUInt<T, N>) {
235 ConstCarryingMul::carrying_mul(a, b, carry)
236 }
237
238 pub c0nst fn const_carrying_mul_add<T: [c0nst] ConstMachineWord + [c0nst] ConstCarryingAdd + [c0nst] ConstBorrowingSub + MachineWord, const N: usize>(
239 a: FixedUInt<T, N>,
240 b: FixedUInt<T, N>,
241 addend: FixedUInt<T, N>,
242 carry: FixedUInt<T, N>,
243 ) -> (FixedUInt<T, N>, FixedUInt<T, N>) {
244 ConstCarryingMul::carrying_mul_add(a, b, addend, carry)
245 }
246 }
247
248 #[test]
249 fn test_carrying_add_no_carry() {
250 let a = U16::from(100u8);
251 let b = U16::from(50u8);
252
253 let (sum, carry_out) = const_carrying_add(a, b, false);
255 assert_eq!(sum, U16::from(150u8));
256 assert!(!carry_out);
257
258 let (sum, carry_out) = const_carrying_add(a, b, true);
260 assert_eq!(sum, U16::from(151u8));
261 assert!(!carry_out);
262 }
263
264 #[test]
265 fn test_carrying_add_with_overflow() {
266 let max = U16::from(0xFFFFu16);
267 let one = U16::from(1u8);
268
269 let (sum, carry_out) = const_carrying_add(max, U16::from(0u8), true);
271 assert_eq!(sum, U16::from(0u8));
272 assert!(carry_out);
273
274 let (sum, carry_out) = const_carrying_add(max, one, false);
276 assert_eq!(sum, U16::from(0u8));
277 assert!(carry_out);
278
279 let (sum, carry_out) = const_carrying_add(max, max, false);
281 assert_eq!(sum, U16::from(0xFFFEu16));
282 assert!(carry_out);
283 }
284
285 #[test]
286 fn test_borrowing_sub_no_borrow() {
287 let a = U16::from(150u8);
288 let b = U16::from(50u8);
289
290 let (diff, borrow_out) = const_borrowing_sub(a, b, false);
292 assert_eq!(diff, U16::from(100u8));
293 assert!(!borrow_out);
294
295 let (diff, borrow_out) = const_borrowing_sub(a, b, true);
297 assert_eq!(diff, U16::from(99u8));
298 assert!(!borrow_out);
299 }
300
301 #[test]
302 fn test_borrowing_sub_with_underflow() {
303 let zero = U16::from(0u8);
304 let one = U16::from(1u8);
305
306 let (diff, borrow_out) = const_borrowing_sub(zero, one, false);
308 assert_eq!(diff, U16::from(0xFFFFu16));
309 assert!(borrow_out);
310
311 let (diff, borrow_out) = const_borrowing_sub(zero, zero, true);
313 assert_eq!(diff, U16::from(0xFFFFu16));
314 assert!(borrow_out);
315
316 let (diff, borrow_out) = const_borrowing_sub(one, one, true);
318 assert_eq!(diff, U16::from(0xFFFFu16));
319 assert!(borrow_out);
320 }
321
322 #[test]
323 fn test_widening_mul() {
324 let a = U16::from(100u8);
326 let (lo, hi) = const_widening_mul(a, a);
327 assert_eq!(lo, U16::from(10000u16));
328 assert_eq!(hi, U16::from(0u8));
329
330 let b = U16::from(256u16);
332 let (lo, hi) = const_widening_mul(b, b);
333 assert_eq!(lo, U16::from(0u8));
334 assert_eq!(hi, U16::from(1u8));
335
336 let max = U16::from(0xFFFFu16);
338 let (lo, hi) = const_widening_mul(max, max);
339 assert_eq!(lo, U16::from(0x0001u16)); assert_eq!(hi, U16::from(0xFFFEu16)); }
342
343 #[test]
344 fn test_widening_mul_larger() {
345 let a = U32::from(0x10000u32); let b = U32::from(0x10000u32); let (lo, hi) = const_widening_mul(a, b);
349 assert_eq!(lo, U32::from(0u8));
352 assert_eq!(hi, U32::from(1u8));
353 }
354
355 #[test]
356 fn test_carrying_mul() {
357 let a = U16::from(100u8);
358 let b = U16::from(100u8);
359 let carry = U16::from(5u8);
360
361 let (lo, hi) = const_carrying_mul(a, b, carry);
363 assert_eq!(lo, U16::from(10005u16));
364 assert_eq!(hi, U16::from(0u8));
365
366 let max = U16::from(0xFFFFu16);
368 let one = U16::from(1u8);
369 let (lo, hi) = const_carrying_mul(one, one, max);
371 assert_eq!(lo, U16::from(0u8));
372 assert_eq!(hi, U16::from(1u8));
373 }
374
375 #[test]
376 fn test_carrying_mul_add() {
377 let a = U16::from(100u8);
378 let b = U16::from(100u8);
379 let addend = U16::from(10u8);
380 let carry = U16::from(5u8);
381
382 let (lo, hi) = const_carrying_mul_add(a, b, addend, carry);
384 assert_eq!(lo, U16::from(10015u16));
385 assert_eq!(hi, U16::from(0u8));
386 }
387
388 #[test]
389 fn test_carrying_mul_add_double_overflow() {
390 let max = U16::from(0xFFFFu16);
392 let one = U16::from(1u8);
393
394 let (lo, hi) = const_carrying_mul_add(one, one, max, max);
396 assert_eq!(lo, U16::from(0xFFFFu16));
397 assert_eq!(hi, U16::from(1u8));
398 }
399
400 #[test]
401 fn test_const_context() {
402 #[cfg(feature = "nightly")]
403 {
404 const A: U16 = FixedUInt { array: [100, 0] };
405 const B: U16 = FixedUInt { array: [50, 0] };
406
407 const ADD_RESULT: (U16, bool) = const_carrying_add(A, B, false);
409 assert_eq!(ADD_RESULT.0, U16::from(150u8));
410 assert!(!ADD_RESULT.1);
411
412 const ADD_WITH_CARRY: (U16, bool) = const_carrying_add(A, B, true);
413 assert_eq!(ADD_WITH_CARRY.0, U16::from(151u8));
414
415 const SUB_RESULT: (U16, bool) = const_borrowing_sub(A, B, false);
417 assert_eq!(SUB_RESULT.0, U16::from(50u8));
418 assert!(!SUB_RESULT.1);
419
420 const C: U16 = FixedUInt { array: [0, 1] }; const MUL_RESULT: (U16, U16) = const_widening_mul(C, C);
423 assert_eq!(MUL_RESULT.0, U16::from(0u8)); assert_eq!(MUL_RESULT.1, U16::from(1u8)); }
426 }
427
428 #[test]
431 fn test_widening_mul_polymorphic() {
432 fn test_widening<T>(a: T, b: T, expected_lo: T, expected_hi: T)
434 where
435 T: ConstWideningMul
436 + ConstCarryingAdd
437 + ConstBorrowingSub
438 + Eq
439 + core::fmt::Debug
440 + Copy,
441 {
442 let (lo, hi) = ConstWideningMul::widening_mul(a, b);
443 assert_eq!(lo, expected_lo, "lo mismatch");
444 assert_eq!(hi, expected_hi, "hi mismatch");
445 }
446
447 test_widening(
450 U16::from(256u16),
451 U16::from(256u16),
452 U16::from(0u16),
453 U16::from(1u16),
454 );
455
456 test_widening(
458 U32::from(256u32),
459 U32::from(256u32),
460 U32::from(65536u32),
461 U32::from(0u32),
462 );
463
464 test_widening(
466 U16::from(0xFFFFu16),
467 U16::from(0xFFFFu16),
468 U16::from(0x0001u16),
469 U16::from(0xFFFEu16),
470 );
471
472 test_widening(
474 U32::from(0xFFFFFFFFu32),
475 U32::from(2u32),
476 U32::from(0xFFFFFFFEu32),
477 U32::from(1u32),
478 );
479 }
480
481 #[test]
483 fn test_carrying_mul_add_polymorphic() {
484 fn test_cma<T>(a: T, b: T, addend: T, carry: T, expected_lo: T, expected_hi: T)
485 where
486 T: ConstCarryingMul + Eq + core::fmt::Debug + Copy,
487 {
488 let (lo, hi) = ConstCarryingMul::carrying_mul_add(a, b, addend, carry);
489 assert_eq!(lo, expected_lo, "lo mismatch");
490 assert_eq!(hi, expected_hi, "hi mismatch");
491 }
492
493 let max16 = U16::from(0xFFFFu16);
497 test_cma(
498 max16,
499 max16,
500 max16,
501 max16,
502 U16::from(0xFFFFu16),
503 U16::from(0xFFFFu16),
504 );
505
506 let max32 = U32::from(0xFFFFFFFFu32);
508 let zero32 = U32::from(0u32);
509 test_cma(
511 max32,
512 U32::from(1u32),
513 zero32,
514 max32,
515 U32::from(0xFFFFFFFEu32),
516 U32::from(1u32),
517 );
518 }
519
520 #[test]
522 fn test_widening_mul_trait() {
523 assert_eq!(WideningMul::widening_mul(255u8, 255u8), (1, 254)); assert_eq!(
526 WideningMul::widening_mul(0xFFFFu16, 0xFFFFu16),
527 (0x0001, 0xFFFE)
528 );
529 assert_eq!(
530 WideningMul::widening_mul(0xFFFF_FFFFu32, 2u32),
531 (0xFFFF_FFFE, 1)
532 );
533 assert_eq!(
534 WideningMul::widening_mul(0xFFFF_FFFF_FFFF_FFFFu64, 2u64),
535 (0xFFFF_FFFF_FFFF_FFFE, 1)
536 );
537
538 let a = U16::from(0xFFFFu16);
540 let (lo, hi) = WideningMul::widening_mul(a, a);
541 assert_eq!(lo, U16::from(0x0001u16));
542 assert_eq!(hi, U16::from(0xFFFEu16));
543
544 let b = U32::from(0x1234_5678u32);
546 let c = U32::from(0x9ABC_DEF0u32);
547 let (lo_trait, hi_trait) = WideningMul::widening_mul(b, c);
548 let (lo_const, hi_const) = ConstWideningMul::widening_mul(b, c);
549 assert_eq!(lo_trait, lo_const);
550 assert_eq!(hi_trait, hi_const);
551 }
552
553 #[test]
555 fn test_carrying_mul_trait() {
556 assert_eq!(CarryingMul::carrying_mul(10u8, 10u8, 5u8), (105, 0));
559 assert_eq!(CarryingMul::carrying_mul(255u8, 255u8, 255u8), (0, 255));
561
562 assert_eq!(
564 CarryingMul::carrying_mul_add(10u8, 10u8, 3u8, 2u8),
565 (105, 0)
566 );
567 assert_eq!(
569 CarryingMul::carrying_mul_add(255u8, 255u8, 255u8, 255u8),
570 (255, 255)
571 );
572
573 let a = U16::from(100u8);
575 let b = U16::from(100u8);
576 let carry = U16::from(5u8);
577 let (lo, hi) = CarryingMul::carrying_mul(a, b, carry);
578 assert_eq!(lo, U16::from(10005u16)); assert_eq!(hi, U16::from(0u8));
580
581 let x = U32::from(0x1234u32);
583 let y = U32::from(0x5678u32);
584 let c = U32::from(0xABCDu32);
585 let (lo_trait, hi_trait) = CarryingMul::carrying_mul(x, y, c);
586 let (lo_const, hi_const) = ConstCarryingMul::carrying_mul(x, y, c);
587 assert_eq!(lo_trait, lo_const);
588 assert_eq!(hi_trait, hi_const);
589
590 let addend = U32::from(0x9999u32);
592 let (lo_trait, hi_trait) = CarryingMul::carrying_mul_add(x, y, addend, c);
593 let (lo_const, hi_const) = ConstCarryingMul::carrying_mul_add(x, y, addend, c);
594 assert_eq!(lo_trait, lo_const);
595 assert_eq!(hi_trait, hi_const);
596 }
597}