1use super::{FixedUInt, MachineWord, add_with_carry, sub_with_borrow};
21use crate::machineword::ConstMachineWord;
22use const_num_traits::{
23 BorrowingSub, Bounded, CarryingAdd, CarryingMul, Personality, PersonalityTag, PrimInt, Zero,
24};
25
26c0nst::c0nst! {
27 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> CarryingAdd for FixedUInt<T, N, P> {
28 type Output = FixedUInt<T, N, P>;
29 fn carrying_add(self, rhs: Self, carry: bool) -> (Self, bool) {
30 let (array, carry_out) = add_with_carry(&self.array, &rhs.array, carry);
31 (Self::from_array(array), carry_out)
32 }
33 }
34
35 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> BorrowingSub for FixedUInt<T, N, P> {
36 type Output = FixedUInt<T, N, P>;
37 fn borrowing_sub(self, rhs: Self, borrow: bool) -> (Self, bool) {
38 let (array, borrow_out) = sub_with_borrow(&self.array, &rhs.array, borrow);
39 (Self::from_array(array), borrow_out)
40 }
41 }
42
43 c0nst fn get_at<T: [c0nst] ConstMachineWord, const N: usize>(
45 lo: &[T; N], hi: &[T; N], pos: usize
46 ) -> T {
47 if pos < N { lo[pos] } else if pos < 2 * N { hi[pos - N] } else { T::zero() }
48 }
49
50 c0nst fn set_at<T: [c0nst] ConstMachineWord, const N: usize>(
52 lo: &mut [T; N], hi: &mut [T; N], pos: usize, val: T
53 ) {
54 if pos < N { lo[pos] = val; } else if pos < 2 * N { hi[pos - N] = val; }
55 }
56
57 c0nst fn schoolbook_mul<
68 T: [c0nst] ConstMachineWord + MachineWord,
69 const N: usize, P: Personality,
70 >(
71 a: FixedUInt<T, N, P>, b: FixedUInt<T, N, P>,
72 ) -> (FixedUInt<T, N, P>, FixedUInt<T, N, P>)
73 where
74 <T as ConstMachineWord>::ConstDoubleWord: [c0nst] PrimInt,
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 + MachineWord, const N: usize, P: Personality> CarryingMul for FixedUInt<T, N, P>
143 where
144 <T as ConstMachineWord>::ConstDoubleWord: [c0nst] PrimInt,
145 {
146 type Unsigned = Self;
147 type Output = FixedUInt<T, N, P>;
148 fn carrying_mul(self, rhs: Self, carry: Self) -> (Self, Self) {
149 let (lo, hi) = schoolbook_mul(self, rhs);
151
152 let (lo2, c) = add_with_carry(&lo.array, &carry.array, false);
153 let zeros = [T::zero(); N];
154 let (hi2, _) = add_with_carry(&hi.array, &zeros, c);
155
156 (Self::from_array(lo2), Self::from_array(hi2))
157 }
158
159 fn carrying_mul_add(self, rhs: Self, addend: Self, carry: Self) -> (Self, Self) {
160 let (lo, hi) = schoolbook_mul(self, rhs);
162
163 let (lo2, c1) = add_with_carry(&lo.array, &carry.array, false);
164 let (lo3, c2) = add_with_carry(&lo2, &addend.array, false);
165
166 let zeros = [T::zero(); N];
168 let (hi2, _) = add_with_carry(&hi.array, &zeros, c1);
169 let (hi3, _) = add_with_carry(&hi2, &zeros, c2);
170
171 (Self::from_array(lo3), Self::from_array(hi3))
172 }
173 }
174
175 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> CarryingAdd for &FixedUInt<T, N, P> {
178 type Output = FixedUInt<T, N, P>;
179 fn carrying_add(self, rhs: Self, carry: bool) -> (FixedUInt<T, N, P>, bool) {
180 <FixedUInt<T, N, P> as CarryingAdd>::carrying_add(FixedUInt::from_array(self.array), FixedUInt::from_array(rhs.array), carry)
181 }
182 }
183
184 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> BorrowingSub for &FixedUInt<T, N, P> {
185 type Output = 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(FixedUInt::from_array(self.array), FixedUInt::from_array(rhs.array), borrow)
188 }
189 }
190
191 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> CarryingMul for &FixedUInt<T, N, P>
192 where
193 <T as ConstMachineWord>::ConstDoubleWord: [c0nst] PrimInt,
194 {
195 type Unsigned = FixedUInt<T, N, P>;
196 type Output = FixedUInt<T, N, P>;
197 fn carrying_mul(self, rhs: Self, carry: Self) -> (FixedUInt<T, N, P>, FixedUInt<T, N, P>) {
198 <FixedUInt<T, N, P> as CarryingMul>::carrying_mul(FixedUInt::from_array(self.array), FixedUInt::from_array(rhs.array), FixedUInt::from_array(carry.array))
199 }
200 fn carrying_mul_add(self, rhs: Self, addend: Self, carry: Self) -> (FixedUInt<T, N, P>, FixedUInt<T, N, P>) {
201 <FixedUInt<T, N, P> as CarryingMul>::carrying_mul_add(FixedUInt::from_array(self.array), FixedUInt::from_array(rhs.array), FixedUInt::from_array(addend.array), FixedUInt::from_array(carry.array))
202 }
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 type U16 = FixedUInt<u8, 2>;
211 type U32 = FixedUInt<u8, 4>;
212
213 c0nst::c0nst! {
214 pub c0nst fn const_carrying_add<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
215 a: FixedUInt<T, N, P>,
216 b: FixedUInt<T, N, P>,
217 carry: bool,
218 ) -> (FixedUInt<T, N, P>, bool) {
219 CarryingAdd::carrying_add(a, b, carry)
220 }
221
222 pub c0nst fn const_borrowing_sub<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
223 a: FixedUInt<T, N, P>,
224 b: FixedUInt<T, N, P>,
225 borrow: bool,
226 ) -> (FixedUInt<T, N, P>, bool) {
227 BorrowingSub::borrowing_sub(a, b, borrow)
228 }
229
230 pub c0nst fn const_widening_mul<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
233 a: FixedUInt<T, N, P>,
234 b: FixedUInt<T, N, P>,
235 ) -> (FixedUInt<T, N, P>, FixedUInt<T, N, P>)
236 where
237 <T as ConstMachineWord>::ConstDoubleWord: [c0nst] PrimInt,
238 {
239 CarryingMul::carrying_mul(a, b, <FixedUInt<T, N, P> as Zero>::zero())
240 }
241
242 pub c0nst fn const_carrying_mul<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
243 a: FixedUInt<T, N, P>,
244 b: FixedUInt<T, N, P>,
245 carry: FixedUInt<T, N, P>,
246 ) -> (FixedUInt<T, N, P>, FixedUInt<T, N, P>)
247 where
248 <T as ConstMachineWord>::ConstDoubleWord: [c0nst] PrimInt,
249 {
250 CarryingMul::carrying_mul(a, b, carry)
251 }
252
253 pub c0nst fn const_carrying_mul_add<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
254 a: FixedUInt<T, N, P>,
255 b: FixedUInt<T, N, P>,
256 addend: FixedUInt<T, N, P>,
257 carry: FixedUInt<T, N, P>,
258 ) -> (FixedUInt<T, N, P>, FixedUInt<T, N, P>)
259 where
260 <T as ConstMachineWord>::ConstDoubleWord: [c0nst] PrimInt,
261 {
262 CarryingMul::carrying_mul_add(a, b, addend, carry)
263 }
264 }
265
266 #[test]
267 fn test_carrying_add_no_carry() {
268 let a = U16::from(100u8);
269 let b = U16::from(50u8);
270
271 let (sum, carry_out) = const_carrying_add(a, b, false);
273 assert_eq!(sum, U16::from(150u8));
274 assert!(!carry_out);
275
276 let (sum, carry_out) = const_carrying_add(a, b, true);
278 assert_eq!(sum, U16::from(151u8));
279 assert!(!carry_out);
280 }
281
282 #[test]
283 fn test_carrying_add_with_overflow() {
284 let max = U16::from(0xFFFFu16);
285 let one = U16::from(1u8);
286
287 let (sum, carry_out) = const_carrying_add(max, U16::from(0u8), true);
289 assert_eq!(sum, U16::from(0u8));
290 assert!(carry_out);
291
292 let (sum, carry_out) = const_carrying_add(max, one, false);
294 assert_eq!(sum, U16::from(0u8));
295 assert!(carry_out);
296
297 let (sum, carry_out) = const_carrying_add(max, max, false);
299 assert_eq!(sum, U16::from(0xFFFEu16));
300 assert!(carry_out);
301 }
302
303 #[test]
304 fn test_borrowing_sub_no_borrow() {
305 let a = U16::from(150u8);
306 let b = U16::from(50u8);
307
308 let (diff, borrow_out) = const_borrowing_sub(a, b, false);
310 assert_eq!(diff, U16::from(100u8));
311 assert!(!borrow_out);
312
313 let (diff, borrow_out) = const_borrowing_sub(a, b, true);
315 assert_eq!(diff, U16::from(99u8));
316 assert!(!borrow_out);
317 }
318
319 #[test]
320 fn test_borrowing_sub_with_underflow() {
321 let zero = U16::from(0u8);
322 let one = U16::from(1u8);
323
324 let (diff, borrow_out) = const_borrowing_sub(zero, one, false);
326 assert_eq!(diff, U16::from(0xFFFFu16));
327 assert!(borrow_out);
328
329 let (diff, borrow_out) = const_borrowing_sub(zero, zero, true);
331 assert_eq!(diff, U16::from(0xFFFFu16));
332 assert!(borrow_out);
333
334 let (diff, borrow_out) = const_borrowing_sub(one, one, true);
336 assert_eq!(diff, U16::from(0xFFFFu16));
337 assert!(borrow_out);
338 }
339
340 #[test]
341 fn test_widening_mul() {
342 let a = U16::from(100u8);
344 let (lo, hi) = const_widening_mul(a, a);
345 assert_eq!(lo, U16::from(10000u16));
346 assert_eq!(hi, U16::from(0u8));
347
348 let b = U16::from(256u16);
350 let (lo, hi) = const_widening_mul(b, b);
351 assert_eq!(lo, U16::from(0u8));
352 assert_eq!(hi, U16::from(1u8));
353
354 let max = U16::from(0xFFFFu16);
356 let (lo, hi) = const_widening_mul(max, max);
357 assert_eq!(lo, U16::from(0x0001u16)); assert_eq!(hi, U16::from(0xFFFEu16)); }
360
361 #[test]
362 fn test_widening_mul_larger() {
363 let a = U32::from(0x10000u32); let b = U32::from(0x10000u32); let (lo, hi) = const_widening_mul(a, b);
367 assert_eq!(lo, U32::from(0u8));
370 assert_eq!(hi, U32::from(1u8));
371 }
372
373 #[test]
374 fn test_carrying_mul() {
375 let a = U16::from(100u8);
376 let b = U16::from(100u8);
377 let carry = U16::from(5u8);
378
379 let (lo, hi) = const_carrying_mul(a, b, carry);
381 assert_eq!(lo, U16::from(10005u16));
382 assert_eq!(hi, U16::from(0u8));
383
384 let max = U16::from(0xFFFFu16);
386 let one = U16::from(1u8);
387 let (lo, hi) = const_carrying_mul(one, one, max);
389 assert_eq!(lo, U16::from(0u8));
390 assert_eq!(hi, U16::from(1u8));
391 }
392
393 #[test]
394 fn test_carrying_mul_add() {
395 let a = U16::from(100u8);
396 let b = U16::from(100u8);
397 let addend = U16::from(10u8);
398 let carry = U16::from(5u8);
399
400 let (lo, hi) = const_carrying_mul_add(a, b, addend, carry);
402 assert_eq!(lo, U16::from(10015u16));
403 assert_eq!(hi, U16::from(0u8));
404 }
405
406 #[test]
407 fn test_carrying_mul_add_double_overflow() {
408 let max = U16::from(0xFFFFu16);
410 let one = U16::from(1u8);
411
412 let (lo, hi) = const_carrying_mul_add(one, one, max, max);
414 assert_eq!(lo, U16::from(0xFFFFu16));
415 assert_eq!(hi, U16::from(1u8));
416 }
417
418 #[test]
419 fn test_const_context() {
420 #[cfg(feature = "nightly")]
421 {
422 const A: U16 = FixedUInt::from_array([100, 0]);
423 const B: U16 = FixedUInt::from_array([50, 0]);
424
425 const ADD_RESULT: (U16, bool) = const_carrying_add(A, B, false);
427 assert_eq!(ADD_RESULT.0, U16::from(150u8));
428 assert!(!ADD_RESULT.1);
429
430 const ADD_WITH_CARRY: (U16, bool) = const_carrying_add(A, B, true);
431 assert_eq!(ADD_WITH_CARRY.0, U16::from(151u8));
432
433 const SUB_RESULT: (U16, bool) = const_borrowing_sub(A, B, false);
435 assert_eq!(SUB_RESULT.0, U16::from(50u8));
436 assert!(!SUB_RESULT.1);
437
438 const C: U16 = FixedUInt::from_array([0, 1]); const MUL_RESULT: (U16, U16) = const_widening_mul(C, C);
441 assert_eq!(MUL_RESULT.0, U16::from(0u8)); assert_eq!(MUL_RESULT.1, U16::from(1u8)); }
444 }
445
446 #[test]
449 fn test_widening_mul_polymorphic() {
450 fn test_widening<T>(a: T, b: T, expected_lo: T, expected_hi: T)
452 where
453 T: CarryingMul<Unsigned = T, Output = T>
454 + CarryingAdd
455 + BorrowingSub
456 + Eq
457 + core::fmt::Debug
458 + Copy
459 + Zero,
460 {
461 let (lo, hi) = CarryingMul::carrying_mul(a, b, <T as Zero>::zero());
462 assert_eq!(lo, expected_lo, "lo mismatch");
463 assert_eq!(hi, expected_hi, "hi mismatch");
464 }
465
466 test_widening(
469 U16::from(256u16),
470 U16::from(256u16),
471 U16::from(0u16),
472 U16::from(1u16),
473 );
474
475 test_widening(
477 U32::from(256u32),
478 U32::from(256u32),
479 U32::from(65536u32),
480 U32::from(0u32),
481 );
482
483 test_widening(
485 U16::from(0xFFFFu16),
486 U16::from(0xFFFFu16),
487 U16::from(0x0001u16),
488 U16::from(0xFFFEu16),
489 );
490
491 test_widening(
493 U32::from(0xFFFFFFFFu32),
494 U32::from(2u32),
495 U32::from(0xFFFFFFFEu32),
496 U32::from(1u32),
497 );
498 }
499
500 #[test]
502 fn test_carrying_mul_add_polymorphic() {
503 fn test_cma<T>(a: T, b: T, addend: T, carry: T, expected_lo: T, expected_hi: T)
504 where
505 T: CarryingMul<Unsigned = T, Output = T> + Eq + core::fmt::Debug + Copy,
506 {
507 let (lo, hi) = CarryingMul::carrying_mul_add(a, b, addend, carry);
508 assert_eq!(lo, expected_lo, "lo mismatch");
509 assert_eq!(hi, expected_hi, "hi mismatch");
510 }
511
512 let max16 = U16::from(0xFFFFu16);
516 test_cma(
517 max16,
518 max16,
519 max16,
520 max16,
521 U16::from(0xFFFFu16),
522 U16::from(0xFFFFu16),
523 );
524
525 let max32 = U32::from(0xFFFFFFFFu32);
527 let zero32 = U32::from(0u32);
528 test_cma(
530 max32,
531 U32::from(1u32),
532 zero32,
533 max32,
534 U32::from(0xFFFFFFFEu32),
535 U32::from(1u32),
536 );
537 }
538
539 #[test]
541 fn test_carrying_mul_full_product() {
542 assert_eq!(CarryingMul::carrying_mul(255u8, 255u8, 0u8), (1, 254)); assert_eq!(
544 CarryingMul::carrying_mul(0xFFFFu16, 0xFFFFu16, 0u16),
545 (0x0001, 0xFFFE)
546 );
547 assert_eq!(
548 CarryingMul::carrying_mul(0xFFFF_FFFFu32, 2u32, 0u32),
549 (0xFFFF_FFFE, 1)
550 );
551 assert_eq!(
552 CarryingMul::carrying_mul(0xFFFF_FFFF_FFFF_FFFFu64, 2u64, 0u64),
553 (0xFFFF_FFFF_FFFF_FFFE, 1)
554 );
555
556 let a = U16::from(0xFFFFu16);
557 let (lo, hi) = CarryingMul::carrying_mul(a, a, <U16 as Zero>::zero());
558 assert_eq!(lo, U16::from(0x0001u16));
559 assert_eq!(hi, U16::from(0xFFFEu16));
560 }
561
562 #[test]
565 fn test_carrying_mul_trait() {
566 assert_eq!(CarryingMul::carrying_mul(10u8, 10u8, 5u8), (105, 0));
567 assert_eq!(CarryingMul::carrying_mul(255u8, 255u8, 255u8), (0, 255));
568 assert_eq!(
569 CarryingMul::carrying_mul_add(10u8, 10u8, 3u8, 2u8),
570 (105, 0)
571 );
572 assert_eq!(
573 CarryingMul::carrying_mul_add(255u8, 255u8, 255u8, 255u8),
574 (255, 255)
575 );
576
577 let a = U16::from(100u8);
578 let b = U16::from(100u8);
579 let carry = U16::from(5u8);
580 let (lo, hi) = CarryingMul::carrying_mul(a, b, carry);
581 assert_eq!(lo, U16::from(10005u16));
582 assert_eq!(hi, U16::from(0u8));
583
584 let x = U32::from(0x1234u32);
586 let y = U32::from(0x5678u32);
587 let c = U32::from(0xABCDu32);
588 assert_eq!(
589 CarryingMul::carrying_mul(x, y, c),
590 const_carrying_mul(x, y, c),
591 );
592 let addend = U32::from(0x9999u32);
593 assert_eq!(
594 CarryingMul::carrying_mul_add(x, y, addend, c),
595 const_carrying_mul_add(x, y, addend, c),
596 );
597 }
598}