1use super::{FixedUInt, MachineWord, add_with_carry, sub_with_borrow};
21use crate::machineword::ConstMachineWord;
22use const_num_traits::{BorrowingSub, Bounded, CarryingAdd, CarryingMul, Zero};
23use const_num_traits::{Personality, PersonalityTag};
24
25c0nst::c0nst! {
26 c0nst impl<T: [c0nst] ConstMachineWord + [c0nst] CarryingAdd + MachineWord, const N: usize, P: Personality> CarryingAdd for FixedUInt<T, N, P> {
27 fn carrying_add(self, rhs: Self, carry: bool) -> (Self, bool) {
28 let (array, carry_out) = add_with_carry(&self.array, &rhs.array, carry);
29 (Self::from_array(array), carry_out)
30 }
31 }
32
33 c0nst impl<T: [c0nst] ConstMachineWord + [c0nst] BorrowingSub + MachineWord, const N: usize, P: Personality> BorrowingSub for FixedUInt<T, N, P> {
34 fn borrowing_sub(self, rhs: Self, borrow: bool) -> (Self, bool) {
35 let (array, borrow_out) = sub_with_borrow(&self.array, &rhs.array, borrow);
36 (Self::from_array(array), borrow_out)
37 }
38 }
39
40 c0nst fn get_at<T: [c0nst] ConstMachineWord, const N: usize>(
42 lo: &[T; N], hi: &[T; N], pos: usize
43 ) -> T {
44 if pos < N { lo[pos] } else if pos < 2 * N { hi[pos - N] } else { T::zero() }
45 }
46
47 c0nst fn set_at<T: [c0nst] ConstMachineWord, const N: usize>(
49 lo: &mut [T; N], hi: &mut [T; N], pos: usize, val: T
50 ) {
51 if pos < N { lo[pos] = val; } else if pos < 2 * N { hi[pos - N] = val; }
52 }
53
54 c0nst fn schoolbook_mul<
65 T: [c0nst] ConstMachineWord + [c0nst] CarryingAdd + MachineWord,
66 const N: usize, P: Personality,
67 >(
68 a: FixedUInt<T, N, P>, b: FixedUInt<T, N, P>,
69 ) -> (FixedUInt<T, N, P>, FixedUInt<T, N, P>)
70 where
71 <T as ConstMachineWord>::ConstDoubleWord:
72 [c0nst] core::ops::Mul<Output = <T as ConstMachineWord>::ConstDoubleWord>
73 + [c0nst] core::ops::BitAnd<Output = <T as ConstMachineWord>::ConstDoubleWord>
74 + [c0nst] core::ops::Shr<usize, Output = <T as ConstMachineWord>::ConstDoubleWord>,
75 {
76 let word_bits = core::mem::size_of::<T>() * 8;
82 let t_max_dw = <T as ConstMachineWord>::to_double(<T as Bounded>::max_value());
83 let mut result_low = [<T as Zero>::zero(); N];
84 let mut result_high = [<T as Zero>::zero(); N];
85
86 let mut i = 0usize;
87 while i < N {
88 let mut j = 0usize;
89 while j < N {
90 let pos = i + j;
91 let op1_dw = <T as ConstMachineWord>::to_double(a.array[i]);
92 let op2_dw = <T as ConstMachineWord>::to_double(b.array[j]);
93 let prod_dw = op1_dw * op2_dw;
94 let mul_lo = <T as ConstMachineWord>::from_double(prod_dw & t_max_dw);
95 let mul_hi = <T as ConstMachineWord>::from_double(prod_dw >> word_bits);
96
97 let cur0 = get_at(&result_low, &result_high, pos);
99 let (sum0, c0) = CarryingAdd::carrying_add(cur0, mul_lo, false);
100 set_at(&mut result_low, &mut result_high, pos, sum0);
101
102 let cur1 = get_at(&result_low, &result_high, pos + 1);
103 let (sum1, c1) = CarryingAdd::carrying_add(cur1, mul_hi, c0);
104 set_at(&mut result_low, &mut result_high, pos + 1, sum1);
105
106 let mut carry = c1;
112 let mut p = pos + 2;
113 match P::TAG {
114 PersonalityTag::Nct => {
115 while carry && p < 2 * N {
116 let cur = get_at(&result_low, &result_high, p);
117 let (sum, c) = CarryingAdd::carrying_add(cur, T::zero(), true);
118 set_at(&mut result_low, &mut result_high, p, sum);
119 carry = c;
120 p += 1;
121 }
122 }
123 PersonalityTag::Ct => {
124 while p < 2 * N {
125 let cur = get_at(&result_low, &result_high, p);
126 let (sum, c) = CarryingAdd::carrying_add(cur, T::zero(), carry);
127 set_at(&mut result_low, &mut result_high, p, sum);
128 carry = c;
129 p += 1;
130 }
131 }
132 }
133
134 j += 1;
135 }
136 i += 1;
137 }
138
139 (FixedUInt::from_array(result_low), FixedUInt::from_array(result_high))
140 }
141
142 c0nst impl<T: [c0nst] ConstMachineWord + [c0nst] CarryingAdd + [c0nst] BorrowingSub + MachineWord, const N: usize, P: Personality> CarryingMul for FixedUInt<T, N, P>
143 where
144 <T as ConstMachineWord>::ConstDoubleWord:
145 [c0nst] core::ops::Mul<Output = <T as ConstMachineWord>::ConstDoubleWord>
146 + [c0nst] core::ops::BitAnd<Output = <T as ConstMachineWord>::ConstDoubleWord>
147 + [c0nst] core::ops::Shr<usize, Output = <T as ConstMachineWord>::ConstDoubleWord>,
148 {
149 type Unsigned = Self;
150 fn carrying_mul(self, rhs: Self, carry: Self) -> (Self, Self) {
151 let (lo, hi) = schoolbook_mul(self, rhs);
153
154 let (lo2, c) = add_with_carry(&lo.array, &carry.array, false);
155 let zeros = [T::zero(); N];
156 let (hi2, _) = add_with_carry(&hi.array, &zeros, c);
157
158 (Self::from_array(lo2), Self::from_array(hi2))
159 }
160
161 fn carrying_mul_add(self, rhs: Self, addend: Self, carry: Self) -> (Self, Self) {
162 let (lo, hi) = schoolbook_mul(self, rhs);
164
165 let (lo2, c1) = add_with_carry(&lo.array, &carry.array, false);
166 let (lo3, c2) = add_with_carry(&lo2, &addend.array, false);
167
168 let zeros = [T::zero(); N];
170 let (hi2, _) = add_with_carry(&hi.array, &zeros, c1);
171 let (hi3, _) = add_with_carry(&hi2, &zeros, c2);
172
173 (Self::from_array(lo3), Self::from_array(hi3))
174 }
175 }
176
177 c0nst impl<T: [c0nst] ConstMachineWord + [c0nst] CarryingAdd + MachineWord, const N: usize, P: Personality> CarryingAdd for &FixedUInt<T, N, P> {
180 fn carrying_add(self, rhs: Self, carry: bool) -> (FixedUInt<T, N, P>, bool) {
181 <FixedUInt<T, N, P> as CarryingAdd>::carrying_add(*self, *rhs, carry)
182 }
183 }
184
185 c0nst impl<T: [c0nst] ConstMachineWord + [c0nst] BorrowingSub + MachineWord, const N: usize, P: Personality> BorrowingSub for &FixedUInt<T, N, P> {
186 fn borrowing_sub(self, rhs: Self, borrow: bool) -> (FixedUInt<T, N, P>, bool) {
187 <FixedUInt<T, N, P> as BorrowingSub>::borrowing_sub(*self, *rhs, borrow)
188 }
189 }
190
191 c0nst impl<T: [c0nst] ConstMachineWord + [c0nst] CarryingAdd + [c0nst] BorrowingSub + MachineWord, const N: usize, P: Personality> CarryingMul for &FixedUInt<T, N, P>
192 where
193 <T as ConstMachineWord>::ConstDoubleWord:
194 [c0nst] core::ops::Mul<Output = <T as ConstMachineWord>::ConstDoubleWord>
195 + [c0nst] core::ops::BitAnd<Output = <T as ConstMachineWord>::ConstDoubleWord>
196 + [c0nst] core::ops::Shr<usize, Output = <T as ConstMachineWord>::ConstDoubleWord>,
197 {
198 type Unsigned = FixedUInt<T, N, P>;
199 fn carrying_mul(self, rhs: Self, carry: Self) -> (FixedUInt<T, N, P>, FixedUInt<T, N, P>) {
200 <FixedUInt<T, N, P> as CarryingMul>::carrying_mul(*self, *rhs, *carry)
201 }
202 fn carrying_mul_add(self, rhs: Self, addend: Self, carry: Self) -> (FixedUInt<T, N, P>, FixedUInt<T, N, P>) {
203 <FixedUInt<T, N, P> as CarryingMul>::carrying_mul_add(*self, *rhs, *addend, *carry)
204 }
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 type U16 = FixedUInt<u8, 2>;
213 type U32 = FixedUInt<u8, 4>;
214
215 c0nst::c0nst! {
216 pub c0nst fn const_carrying_add<T: [c0nst] ConstMachineWord + [c0nst] CarryingAdd + [c0nst] BorrowingSub + MachineWord, const N: usize, P: Personality>(
217 a: FixedUInt<T, N, P>,
218 b: FixedUInt<T, N, P>,
219 carry: bool,
220 ) -> (FixedUInt<T, N, P>, bool) {
221 CarryingAdd::carrying_add(a, b, carry)
222 }
223
224 pub c0nst fn const_borrowing_sub<T: [c0nst] ConstMachineWord + [c0nst] CarryingAdd + [c0nst] BorrowingSub + MachineWord, const N: usize, P: Personality>(
225 a: FixedUInt<T, N, P>,
226 b: FixedUInt<T, N, P>,
227 borrow: bool,
228 ) -> (FixedUInt<T, N, P>, bool) {
229 BorrowingSub::borrowing_sub(a, b, borrow)
230 }
231
232 pub c0nst fn const_widening_mul<T: [c0nst] ConstMachineWord + [c0nst] CarryingAdd + [c0nst] BorrowingSub + MachineWord, const N: usize, P: Personality>(
235 a: FixedUInt<T, N, P>,
236 b: FixedUInt<T, N, P>,
237 ) -> (FixedUInt<T, N, P>, FixedUInt<T, N, P>)
238 where
239 <T as ConstMachineWord>::ConstDoubleWord:
240 [c0nst] core::ops::Mul<Output = <T as ConstMachineWord>::ConstDoubleWord>
241 + [c0nst] core::ops::BitAnd<Output = <T as ConstMachineWord>::ConstDoubleWord>
242 + [c0nst] core::ops::Shr<usize, Output = <T as ConstMachineWord>::ConstDoubleWord>,
243 {
244 CarryingMul::carrying_mul(a, b, <FixedUInt<T, N, P> as Zero>::zero())
245 }
246
247 pub c0nst fn const_carrying_mul<T: [c0nst] ConstMachineWord + [c0nst] CarryingAdd + [c0nst] BorrowingSub + MachineWord, const N: usize, P: Personality>(
248 a: FixedUInt<T, N, P>,
249 b: FixedUInt<T, N, P>,
250 carry: FixedUInt<T, N, P>,
251 ) -> (FixedUInt<T, N, P>, FixedUInt<T, N, P>)
252 where
253 <T as ConstMachineWord>::ConstDoubleWord:
254 [c0nst] core::ops::Mul<Output = <T as ConstMachineWord>::ConstDoubleWord>
255 + [c0nst] core::ops::BitAnd<Output = <T as ConstMachineWord>::ConstDoubleWord>
256 + [c0nst] core::ops::Shr<usize, Output = <T as ConstMachineWord>::ConstDoubleWord>,
257 {
258 CarryingMul::carrying_mul(a, b, carry)
259 }
260
261 pub c0nst fn const_carrying_mul_add<T: [c0nst] ConstMachineWord + [c0nst] CarryingAdd + [c0nst] BorrowingSub + MachineWord, const N: usize, P: Personality>(
262 a: FixedUInt<T, N, P>,
263 b: FixedUInt<T, N, P>,
264 addend: FixedUInt<T, N, P>,
265 carry: FixedUInt<T, N, P>,
266 ) -> (FixedUInt<T, N, P>, FixedUInt<T, N, P>)
267 where
268 <T as ConstMachineWord>::ConstDoubleWord:
269 [c0nst] core::ops::Mul<Output = <T as ConstMachineWord>::ConstDoubleWord>
270 + [c0nst] core::ops::BitAnd<Output = <T as ConstMachineWord>::ConstDoubleWord>
271 + [c0nst] core::ops::Shr<usize, Output = <T as ConstMachineWord>::ConstDoubleWord>,
272 {
273 CarryingMul::carrying_mul_add(a, b, addend, carry)
274 }
275 }
276
277 #[test]
278 fn test_carrying_add_no_carry() {
279 let a = U16::from(100u8);
280 let b = U16::from(50u8);
281
282 let (sum, carry_out) = const_carrying_add(a, b, false);
284 assert_eq!(sum, U16::from(150u8));
285 assert!(!carry_out);
286
287 let (sum, carry_out) = const_carrying_add(a, b, true);
289 assert_eq!(sum, U16::from(151u8));
290 assert!(!carry_out);
291 }
292
293 #[test]
294 fn test_carrying_add_with_overflow() {
295 let max = U16::from(0xFFFFu16);
296 let one = U16::from(1u8);
297
298 let (sum, carry_out) = const_carrying_add(max, U16::from(0u8), true);
300 assert_eq!(sum, U16::from(0u8));
301 assert!(carry_out);
302
303 let (sum, carry_out) = const_carrying_add(max, one, false);
305 assert_eq!(sum, U16::from(0u8));
306 assert!(carry_out);
307
308 let (sum, carry_out) = const_carrying_add(max, max, false);
310 assert_eq!(sum, U16::from(0xFFFEu16));
311 assert!(carry_out);
312 }
313
314 #[test]
315 fn test_borrowing_sub_no_borrow() {
316 let a = U16::from(150u8);
317 let b = U16::from(50u8);
318
319 let (diff, borrow_out) = const_borrowing_sub(a, b, false);
321 assert_eq!(diff, U16::from(100u8));
322 assert!(!borrow_out);
323
324 let (diff, borrow_out) = const_borrowing_sub(a, b, true);
326 assert_eq!(diff, U16::from(99u8));
327 assert!(!borrow_out);
328 }
329
330 #[test]
331 fn test_borrowing_sub_with_underflow() {
332 let zero = U16::from(0u8);
333 let one = U16::from(1u8);
334
335 let (diff, borrow_out) = const_borrowing_sub(zero, one, false);
337 assert_eq!(diff, U16::from(0xFFFFu16));
338 assert!(borrow_out);
339
340 let (diff, borrow_out) = const_borrowing_sub(zero, zero, true);
342 assert_eq!(diff, U16::from(0xFFFFu16));
343 assert!(borrow_out);
344
345 let (diff, borrow_out) = const_borrowing_sub(one, one, true);
347 assert_eq!(diff, U16::from(0xFFFFu16));
348 assert!(borrow_out);
349 }
350
351 #[test]
352 fn test_widening_mul() {
353 let a = U16::from(100u8);
355 let (lo, hi) = const_widening_mul(a, a);
356 assert_eq!(lo, U16::from(10000u16));
357 assert_eq!(hi, U16::from(0u8));
358
359 let b = U16::from(256u16);
361 let (lo, hi) = const_widening_mul(b, b);
362 assert_eq!(lo, U16::from(0u8));
363 assert_eq!(hi, U16::from(1u8));
364
365 let max = U16::from(0xFFFFu16);
367 let (lo, hi) = const_widening_mul(max, max);
368 assert_eq!(lo, U16::from(0x0001u16)); assert_eq!(hi, U16::from(0xFFFEu16)); }
371
372 #[test]
373 fn test_widening_mul_larger() {
374 let a = U32::from(0x10000u32); let b = U32::from(0x10000u32); let (lo, hi) = const_widening_mul(a, b);
378 assert_eq!(lo, U32::from(0u8));
381 assert_eq!(hi, U32::from(1u8));
382 }
383
384 #[test]
385 fn test_carrying_mul() {
386 let a = U16::from(100u8);
387 let b = U16::from(100u8);
388 let carry = U16::from(5u8);
389
390 let (lo, hi) = const_carrying_mul(a, b, carry);
392 assert_eq!(lo, U16::from(10005u16));
393 assert_eq!(hi, U16::from(0u8));
394
395 let max = U16::from(0xFFFFu16);
397 let one = U16::from(1u8);
398 let (lo, hi) = const_carrying_mul(one, one, max);
400 assert_eq!(lo, U16::from(0u8));
401 assert_eq!(hi, U16::from(1u8));
402 }
403
404 #[test]
405 fn test_carrying_mul_add() {
406 let a = U16::from(100u8);
407 let b = U16::from(100u8);
408 let addend = U16::from(10u8);
409 let carry = U16::from(5u8);
410
411 let (lo, hi) = const_carrying_mul_add(a, b, addend, carry);
413 assert_eq!(lo, U16::from(10015u16));
414 assert_eq!(hi, U16::from(0u8));
415 }
416
417 #[test]
418 fn test_carrying_mul_add_double_overflow() {
419 let max = U16::from(0xFFFFu16);
421 let one = U16::from(1u8);
422
423 let (lo, hi) = const_carrying_mul_add(one, one, max, max);
425 assert_eq!(lo, U16::from(0xFFFFu16));
426 assert_eq!(hi, U16::from(1u8));
427 }
428
429 #[test]
430 fn test_const_context() {
431 #[cfg(feature = "nightly")]
432 {
433 const A: U16 = FixedUInt::from_array([100, 0]);
434 const B: U16 = FixedUInt::from_array([50, 0]);
435
436 const ADD_RESULT: (U16, bool) = const_carrying_add(A, B, false);
438 assert_eq!(ADD_RESULT.0, U16::from(150u8));
439 assert!(!ADD_RESULT.1);
440
441 const ADD_WITH_CARRY: (U16, bool) = const_carrying_add(A, B, true);
442 assert_eq!(ADD_WITH_CARRY.0, U16::from(151u8));
443
444 const SUB_RESULT: (U16, bool) = const_borrowing_sub(A, B, false);
446 assert_eq!(SUB_RESULT.0, U16::from(50u8));
447 assert!(!SUB_RESULT.1);
448
449 const C: U16 = FixedUInt::from_array([0, 1]); const MUL_RESULT: (U16, U16) = const_widening_mul(C, C);
452 assert_eq!(MUL_RESULT.0, U16::from(0u8)); assert_eq!(MUL_RESULT.1, U16::from(1u8)); }
455 }
456
457 #[test]
460 fn test_widening_mul_polymorphic() {
461 fn test_widening<T>(a: T, b: T, expected_lo: T, expected_hi: T)
463 where
464 T: CarryingMul<Unsigned = T>
465 + core::ops::Mul<T, Output = T>
466 + CarryingAdd
467 + BorrowingSub
468 + Eq
469 + core::fmt::Debug
470 + Copy
471 + Zero,
472 {
473 let (lo, hi) = CarryingMul::carrying_mul(a, b, <T as Zero>::zero());
474 assert_eq!(lo, expected_lo, "lo mismatch");
475 assert_eq!(hi, expected_hi, "hi mismatch");
476 }
477
478 test_widening(
481 U16::from(256u16),
482 U16::from(256u16),
483 U16::from(0u16),
484 U16::from(1u16),
485 );
486
487 test_widening(
489 U32::from(256u32),
490 U32::from(256u32),
491 U32::from(65536u32),
492 U32::from(0u32),
493 );
494
495 test_widening(
497 U16::from(0xFFFFu16),
498 U16::from(0xFFFFu16),
499 U16::from(0x0001u16),
500 U16::from(0xFFFEu16),
501 );
502
503 test_widening(
505 U32::from(0xFFFFFFFFu32),
506 U32::from(2u32),
507 U32::from(0xFFFFFFFEu32),
508 U32::from(1u32),
509 );
510 }
511
512 #[test]
514 fn test_carrying_mul_add_polymorphic() {
515 fn test_cma<T>(a: T, b: T, addend: T, carry: T, expected_lo: T, expected_hi: T)
516 where
517 T: CarryingMul<Unsigned = T>
518 + core::ops::Mul<T, Output = T>
519 + Eq
520 + core::fmt::Debug
521 + Copy,
522 {
523 let (lo, hi) = CarryingMul::carrying_mul_add(a, b, addend, carry);
524 assert_eq!(lo, expected_lo, "lo mismatch");
525 assert_eq!(hi, expected_hi, "hi mismatch");
526 }
527
528 let max16 = U16::from(0xFFFFu16);
532 test_cma(
533 max16,
534 max16,
535 max16,
536 max16,
537 U16::from(0xFFFFu16),
538 U16::from(0xFFFFu16),
539 );
540
541 let max32 = U32::from(0xFFFFFFFFu32);
543 let zero32 = U32::from(0u32);
544 test_cma(
546 max32,
547 U32::from(1u32),
548 zero32,
549 max32,
550 U32::from(0xFFFFFFFEu32),
551 U32::from(1u32),
552 );
553 }
554
555 #[test]
557 fn test_carrying_mul_full_product() {
558 assert_eq!(CarryingMul::carrying_mul(255u8, 255u8, 0u8), (1, 254)); assert_eq!(
560 CarryingMul::carrying_mul(0xFFFFu16, 0xFFFFu16, 0u16),
561 (0x0001, 0xFFFE)
562 );
563 assert_eq!(
564 CarryingMul::carrying_mul(0xFFFF_FFFFu32, 2u32, 0u32),
565 (0xFFFF_FFFE, 1)
566 );
567 assert_eq!(
568 CarryingMul::carrying_mul(0xFFFF_FFFF_FFFF_FFFFu64, 2u64, 0u64),
569 (0xFFFF_FFFF_FFFF_FFFE, 1)
570 );
571
572 let a = U16::from(0xFFFFu16);
573 let (lo, hi) = CarryingMul::carrying_mul(a, a, <U16 as Zero>::zero());
574 assert_eq!(lo, U16::from(0x0001u16));
575 assert_eq!(hi, U16::from(0xFFFEu16));
576 }
577
578 #[test]
581 fn test_carrying_mul_trait() {
582 assert_eq!(CarryingMul::carrying_mul(10u8, 10u8, 5u8), (105, 0));
583 assert_eq!(CarryingMul::carrying_mul(255u8, 255u8, 255u8), (0, 255));
584 assert_eq!(
585 CarryingMul::carrying_mul_add(10u8, 10u8, 3u8, 2u8),
586 (105, 0)
587 );
588 assert_eq!(
589 CarryingMul::carrying_mul_add(255u8, 255u8, 255u8, 255u8),
590 (255, 255)
591 );
592
593 let a = U16::from(100u8);
594 let b = U16::from(100u8);
595 let carry = U16::from(5u8);
596 let (lo, hi) = CarryingMul::carrying_mul(a, b, carry);
597 assert_eq!(lo, U16::from(10005u16));
598 assert_eq!(hi, U16::from(0u8));
599
600 let x = U32::from(0x1234u32);
602 let y = U32::from(0x5678u32);
603 let c = U32::from(0xABCDu32);
604 assert_eq!(
605 CarryingMul::carrying_mul(x, y, c),
606 const_carrying_mul(x, y, c),
607 );
608 let addend = U32::from(0x9999u32);
609 assert_eq!(
610 CarryingMul::carrying_mul_add(x, y, addend, c),
611 const_carrying_mul_add(x, y, addend, c),
612 );
613 }
614}