1#[cfg(feature = "num-traits")]
16use core::fmt::Write;
17
18use crate::machineword::{ConstMachineWord, MachineWord};
19use const_num_traits::ops::overflowing::{OverflowingAdd, OverflowingMul, OverflowingSub};
20use const_num_traits::{
21 BorrowingSub, Bounded, CarryingAdd, ConstOne, ConstZero, One, PrimBits, Zero,
22};
23
24mod abs_diff_impl;
25mod add_sub_impl;
26mod bit_ops_impl;
27mod byte_conversion_panic_free;
28mod checked_pow_impl;
29#[cfg(feature = "cios")]
30mod cios_row_ops_impl;
31mod div_ceil_impl;
32mod euclid;
33mod extended_precision_impl;
34mod from_byte_slice_impl;
35mod has_nonzero_impl;
36mod has_personality_impl;
37mod ilog_impl;
38mod isqrt_impl;
39mod iter_impl;
40mod midpoint_impl;
41mod mul_div_impl;
42mod multiple_impl;
43#[cfg(feature = "num-traits")]
44mod num_integer_impl;
45#[cfg(feature = "num-traits")]
46mod num_traits_casts;
47mod num_traits_identity;
48mod parity_impl;
49mod power_of_two_impl;
50mod power_of_two_ops_impl;
51mod prim_int_impl;
52#[cfg(feature = "num-traits")]
53mod roots_impl;
54mod strict_impl;
55#[cfg(feature = "num-traits")]
56mod string_conversion;
57#[cfg(feature = "nightly")]
59mod const_to_from_bytes;
60#[cfg(any(feature = "nightly", feature = "use-unsafe"))]
68mod to_from_bytes;
69
70#[cfg(any(feature = "nightly", feature = "use-unsafe"))]
73pub(crate) use to_from_bytes::{BytesHolder, holder_be_from_limbs, holder_le_from_limbs};
74
75pub use has_nonzero_impl::NonZeroFixedUInt;
76
77use const_num_traits::{Ct, Nct, Personality, PersonalityMarker, PersonalityTag};
78#[cfg(feature = "zeroize")]
79use zeroize::DefaultIsZeroes;
80
81#[derive(Copy)]
91pub struct FixedUInt<T, const N: usize, P: Personality = Nct>
92where
93 T: MachineWord,
94{
95 pub(super) array: [T; N],
97 pub(super) _p: PersonalityMarker<P>,
99}
100
101impl<T: MachineWord + core::fmt::Debug, const N: usize> core::fmt::Debug for FixedUInt<T, N, Nct> {
106 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
107 f.debug_struct("FixedUInt")
108 .field("array", &self.array)
109 .finish()
110 }
111}
112
113impl<T: MachineWord, const N: usize> core::fmt::Debug for FixedUInt<T, N, Ct> {
114 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
115 f.write_str("FixedUInt<…>")
116 }
117}
118
119#[cfg(feature = "zeroize")]
120impl<T: MachineWord, const N: usize, P: Personality> DefaultIsZeroes for FixedUInt<T, N, P> {}
121
122impl<T, const N: usize, P: Personality> From<[T; N]> for FixedUInt<T, N, P>
123where
124 T: MachineWord,
125{
126 fn from(array: [T; N]) -> Self {
127 Self {
128 array,
129 _p: core::marker::PhantomData,
130 }
131 }
132}
133
134impl<T: MachineWord, const N: usize, P: Personality> FixedUInt<T, N, P> {
137 pub(crate) const fn from_array(array: [T; N]) -> Self {
138 Self {
139 array,
140 _p: core::marker::PhantomData,
141 }
142 }
143}
144
145impl<T: MachineWord, const N: usize> From<FixedUInt<T, N, Nct>> for FixedUInt<T, N, Ct> {
153 fn from(v: FixedUInt<T, N, Nct>) -> Self {
154 FixedUInt::from_array(v.array)
155 }
156}
157
158impl<T: MachineWord, const N: usize> FixedUInt<T, N, Ct> {
159 pub const fn forget_ct(self) -> FixedUInt<T, N, Nct> {
166 FixedUInt::from_array(self.array)
167 }
168}
169
170#[inline]
175fn ct_checked_shift_valid(bits: u32, bit_size: usize) -> subtle::Choice {
176 if bit_size == 0 {
177 return subtle::Choice::from(0);
180 }
181 let bit_size_u32 = bit_size as u32;
182 let diff = bit_size_u32.wrapping_sub(1).wrapping_sub(bits);
185 let overflow = ((diff >> 31) & 1) as u8;
186 subtle::Choice::from(1 ^ overflow)
187}
188
189impl<T: MachineWord, const N: usize> FixedUInt<T, N, Ct> {
190 pub fn ct_checked_add(&self, other: &Self) -> subtle::CtOption<Self> {
196 let (res, overflow) = <&Self as OverflowingAdd>::overflowing_add(self, other);
200 let valid = subtle::Choice::from((!overflow) as u8);
201 subtle::CtOption::new(res, valid)
202 }
203
204 pub fn ct_checked_sub(&self, other: &Self) -> subtle::CtOption<Self> {
206 let (res, overflow) = <&Self as OverflowingSub>::overflowing_sub(self, other);
207 let valid = subtle::Choice::from((!overflow) as u8);
208 subtle::CtOption::new(res, valid)
209 }
210
211 pub fn ct_checked_mul(&self, other: &Self) -> subtle::CtOption<Self> {
213 let (res, overflow) = <&Self as OverflowingMul>::overflowing_mul(self, other);
214 let valid = subtle::Choice::from((!overflow) as u8);
215 subtle::CtOption::new(res, valid)
216 }
217
218 pub fn ct_checked_shl(&self, bits: u32) -> subtle::CtOption<Self> {
227 subtle::CtOption::new(
233 bit_ops_impl::const_unbounded_shl_u32::<T, N, Ct>(Self::from_array(self.array), bits),
234 ct_checked_shift_valid(bits, Self::BIT_SIZE),
235 )
236 }
237
238 pub fn ct_checked_shr(&self, bits: u32) -> subtle::CtOption<Self> {
243 subtle::CtOption::new(
244 bit_ops_impl::const_unbounded_shr_u32::<T, N, Ct>(Self::from_array(self.array), bits),
245 ct_checked_shift_valid(bits, Self::BIT_SIZE),
246 )
247 }
248
249 pub fn ct_checked_pow(self, exp: u32) -> subtle::CtOption<Self> {
250 let mut result = <Self as One>::one();
251 let mut base = self;
252 let mut e = exp;
253 let mut any_overflow: u8 = 0;
254 for _ in 0..u32::BITS {
255 let bit = core::hint::black_box((e & 1) as u8);
259 let (candidate, mul_ov) = <Self as OverflowingMul>::overflowing_mul(result, base);
260 any_overflow |= (mul_ov as u8) & bit;
262 let bit_t = <T as core::convert::From<u8>>::from(bit);
264 let mask = core::hint::black_box(bit_t * <T as Bounded>::max_value());
265 for i in 0..N {
266 let diff = result.array[i] ^ candidate.array[i];
267 result.array[i] ^= mask & diff;
268 }
269 e >>= 1;
270 let (new_base, base_ov) = <Self as OverflowingMul>::overflowing_mul(base, base);
271 let any_remaining: u8 = core::hint::black_box((e != 0) as u8);
273 any_overflow |= (base_ov as u8) & any_remaining;
274 base = new_base;
275 }
276 let valid = subtle::Choice::from(1u8 ^ any_overflow);
277 subtle::CtOption::new(result, valid)
278 }
279}
280
281impl<T: MachineWord + subtle::ConditionallySelectable, const N: usize> FixedUInt<T, N, Ct> {
288 pub fn ct_checked_next_power_of_two(self) -> subtle::CtOption<Self>
290 where
291 T: subtle::ConstantTimeEq,
292 {
293 let one = <Self as One>::one();
294 let m_one = <Self as const_num_traits::WrappingSub>::wrapping_sub(self, one);
295 let leading = <Self as PrimBits>::leading_zeros(m_one);
296 let bits = Self::BIT_SIZE as u32 - leading;
297 let shifted = one << (bits as usize);
298 let is_zero_choice =
299 <Self as subtle::ConstantTimeEq>::ct_eq(&self, &<Self as Zero>::zero());
300 let result = <Self as subtle::ConditionallySelectable>::conditional_select(
302 &shifted,
303 &one,
304 is_zero_choice,
305 );
306 let overflow = (bits >= Self::BIT_SIZE as u32) as u8;
309 let valid_otherwise = subtle::Choice::from(1u8 ^ overflow);
310 let valid = <subtle::Choice as subtle::ConditionallySelectable>::conditional_select(
311 &valid_otherwise,
312 &subtle::Choice::from(1u8),
313 is_zero_choice,
314 );
315 subtle::CtOption::new(result, valid)
316 }
317}
318
319impl<T: MachineWord + subtle::ConstantTimeEq, const N: usize> subtle::ConstantTimeEq
324 for FixedUInt<T, N, Ct>
325{
326 fn ct_eq(&self, other: &Self) -> subtle::Choice {
327 <[T] as subtle::ConstantTimeEq>::ct_eq(self.array.as_slice(), other.array.as_slice())
328 }
329}
330
331impl<T: MachineWord + subtle::ConditionallySelectable, const N: usize>
332 subtle::ConditionallySelectable for FixedUInt<T, N, Ct>
333{
334 fn conditional_select(a: &Self, b: &Self, choice: subtle::Choice) -> Self {
335 let mut array = a.array;
336 let mut i = 0;
337 while i < N {
338 array[i] = T::conditional_select(&a.array[i], &b.array[i], choice);
339 i += 1;
340 }
341 FixedUInt::from_array(array)
342 }
343}
344
345impl<T: MachineWord + subtle::ConstantTimeEq + subtle::ConstantTimeGreater, const N: usize>
352 subtle::ConstantTimeGreater for FixedUInt<T, N, Ct>
353{
354 fn ct_gt(&self, other: &Self) -> subtle::Choice {
355 let mut gt = subtle::Choice::from(0u8);
356 let mut undecided = subtle::Choice::from(1u8);
357 let mut i = N;
358 while i > 0 {
359 i -= 1;
360 let gt_here = self.array[i].ct_gt(&other.array[i]);
361 let eq_here = self.array[i].ct_eq(&other.array[i]);
362 gt |= undecided & gt_here;
363 undecided &= eq_here;
364 }
365 gt
366 }
367}
368
369impl<T: MachineWord + subtle::ConstantTimeEq + subtle::ConstantTimeGreater, const N: usize>
370 subtle::ConstantTimeLess for FixedUInt<T, N, Ct>
371{
372}
373
374const LONGEST_WORD_IN_BITS: usize = 128;
375
376impl<T: MachineWord, const N: usize, P: Personality> FixedUInt<T, N, P> {
377 const WORD_SIZE: usize = core::mem::size_of::<T>();
378 const WORD_BITS: usize = Self::WORD_SIZE * 8;
379 const BYTE_SIZE: usize = Self::WORD_SIZE * N;
380 const BIT_SIZE: usize = Self::BYTE_SIZE * 8;
381
382 pub const BYTE_WIDTH: usize = Self::BYTE_SIZE;
389
390 pub fn new() -> FixedUInt<T, N, P> {
392 FixedUInt::from_array([T::zero(); N])
393 }
394
395 pub fn words(&self) -> &[T; N] {
397 &self.array
398 }
399
400 pub fn bit_length(&self) -> u32 {
402 Self::BIT_SIZE as u32 - const_leading_zeros(&self.array)
406 }
407}
408
409impl<T: MachineWord, const N: usize> FixedUInt<T, N, Nct> {
410 pub fn div_rem(&self, divisor: &Self) -> (Self, Self) {
412 let (quotient, remainder) = const_div_rem(&self.array, &divisor.array);
413 (Self::from_array(quotient), Self::from_array(remainder))
414 }
415
416 pub fn to_radix_str<'a>(
418 &self,
419 result: &'a mut [u8],
420 radix: u8,
421 ) -> Result<&'a str, core::fmt::Error> {
422 type Error = core::fmt::Error;
423
424 if !(2..=16).contains(&radix) {
425 return Err(Error {}); }
427 for byte in result.iter_mut() {
428 *byte = b'0';
429 }
430 if <Self as Zero>::is_zero(self) {
431 if !result.is_empty() {
432 result[0] = b'0';
433 return core::str::from_utf8(&result[0..1]).map_err(|_| Error {});
434 } else {
435 return Err(Error {});
436 }
437 }
438
439 let mut number = *self;
440 let mut idx = result.len();
441
442 let radix_t = Self::from(radix);
443
444 while !<Self as Zero>::is_zero(&number) {
445 if idx == 0 {
446 return Err(Error {}); }
448
449 idx -= 1;
450 let (quotient, remainder) = number.div_rem(&radix_t);
451
452 let digit =
456 <T as const_num_traits::ToPrimitive>::to_u8(&remainder.array[0]).unwrap_or(0);
457 result[idx] = match digit {
458 0..=9 => b'0' + digit, 10..=16 => b'a' + (digit - 10), _ => return Err(Error {}),
461 };
462
463 number = quotient;
464 }
465
466 let start = result[idx..].iter().position(|&c| c != b'0').unwrap_or(0);
467 let radix_str = core::str::from_utf8(&result[idx + start..]).map_err(|_| Error {})?;
468 Ok(radix_str)
469 }
470}
471
472c0nst::c0nst! {
474 pub(crate) c0nst fn impl_from_le_bytes_slice<T: [c0nst] ConstMachineWord, const N: usize>(
477 bytes: &[u8],
478 ) -> [T; N] {
479 let word_size = core::mem::size_of::<T>();
480 let mut ret: [T; N] = [T::zero(); N];
481 let capacity = N * word_size;
482 let total_bytes = if bytes.len() < capacity { bytes.len() } else { capacity };
483
484 let mut byte_index = 0;
485 while byte_index < total_bytes {
486 let word_index = byte_index / word_size;
487 let byte_in_word = byte_index % word_size;
488
489 let byte_value: T = <T as core::convert::From<u8>>::from(bytes[byte_index]);
490 let shifted_value = byte_value.shl(byte_in_word * 8);
491 ret[word_index] = ret[word_index].bitor(shifted_value);
492 byte_index += 1;
493 }
494 ret
495 }
496
497 pub(crate) c0nst fn impl_from_be_bytes_slice<T: [c0nst] ConstMachineWord, const N: usize>(
500 bytes: &[u8],
501 ) -> [T; N] {
502 let word_size = core::mem::size_of::<T>();
503 let mut ret: [T; N] = [T::zero(); N];
504 let capacity_bytes = N * word_size;
505 let total_bytes = if bytes.len() < capacity_bytes { bytes.len() } else { capacity_bytes };
506
507 let start_offset = if bytes.len() > capacity_bytes {
510 bytes.len() - capacity_bytes
511 } else {
512 0
513 };
514
515 let mut byte_index = 0;
516 while byte_index < total_bytes {
517 let be_byte_index = start_offset + total_bytes - 1 - byte_index;
519 let word_index = byte_index / word_size;
520 let byte_in_word = byte_index % word_size;
521
522 let byte_value: T = <T as core::convert::From<u8>>::from(bytes[be_byte_index]);
523 let shifted_value = byte_value.shl(byte_in_word * 8);
524 ret[word_index] = ret[word_index].bitor(shifted_value);
525 byte_index += 1;
526 }
527 ret
528 }
529}
530
531impl<T: MachineWord, const N: usize, P: Personality> FixedUInt<T, N, P> {
533 pub fn from_le_bytes(bytes: &[u8]) -> Self {
535 Self::from_array(impl_from_le_bytes_slice::<T, N>(bytes))
536 }
537
538 pub fn from_be_bytes(bytes: &[u8]) -> Self {
540 Self::from_array(impl_from_be_bytes_slice::<T, N>(bytes))
541 }
542}
543
544impl<T: MachineWord, const N: usize, P: Personality> FixedUInt<T, N, P> {
545 pub fn to_le_bytes<'a>(&self, output_buffer: &'a mut [u8]) -> Result<&'a [u8], bool> {
547 let total_bytes = N * Self::WORD_SIZE;
548 if output_buffer.len() < total_bytes {
549 return Err(false); }
551 for (i, word) in self.array.iter().enumerate() {
552 let start = i * Self::WORD_SIZE;
553 let end = start + Self::WORD_SIZE;
554 let word_bytes = word.to_le_bytes();
555 output_buffer[start..end].copy_from_slice(word_bytes.as_ref());
556 }
557 Ok(&output_buffer[..total_bytes])
558 }
559
560 pub fn to_be_bytes<'a>(&self, output_buffer: &'a mut [u8]) -> Result<&'a [u8], bool> {
562 let total_bytes = N * Self::WORD_SIZE;
563 if output_buffer.len() < total_bytes {
564 return Err(false); }
566 for (i, word) in self.array.iter().rev().enumerate() {
567 let start = i * Self::WORD_SIZE;
568 let end = start + Self::WORD_SIZE;
569 let word_bytes = word.to_be_bytes();
570 output_buffer[start..end].copy_from_slice(word_bytes.as_ref());
571 }
572 Ok(&output_buffer[..total_bytes])
573 }
574
575 pub fn to_hex_str<'a>(&self, result: &'a mut [u8]) -> Result<&'a str, core::fmt::Error> {
577 type Error = core::fmt::Error;
578
579 let word_size = Self::WORD_SIZE;
580 let need_bits = self.bit_length() as usize;
582 let need_chars = if need_bits > 0 { need_bits / 4 } else { 0 };
584
585 if result.len() < need_chars {
586 return Err(Error {});
588 }
589 let offset = result.len() - need_chars;
590 for i in result.iter_mut() {
591 *i = b'0';
592 }
593
594 for iter_words in 0..self.array.len() {
595 let word = self.array[iter_words];
596 let mut encoded = [0u8; LONGEST_WORD_IN_BITS / 4];
597 let encode_slice = &mut encoded[0..word_size * 2];
598 let mut wordbytes = word.to_le_bytes();
599 wordbytes.as_mut().reverse();
600 let wordslice = wordbytes.as_ref();
601 to_slice_hex(wordslice, encode_slice).map_err(|_| Error {})?;
602 for iter_chars in 0..encode_slice.len() {
603 let copy_char_to = (iter_words * word_size * 2) + iter_chars;
604 if copy_char_to <= need_chars {
605 let reverse_index = offset + (need_chars - copy_char_to);
606 if reverse_index <= result.len() && reverse_index > 0 {
607 let current_char = encode_slice[(encode_slice.len() - 1) - iter_chars];
608 result[reverse_index - 1] = current_char;
609 }
610 }
611 }
612 }
613
614 let convert = core::str::from_utf8(result).map_err(|_| Error {})?;
615 let pos = convert.find(|c: char| c != '0');
616 match pos {
617 Some(x) => Ok(&convert[x..convert.len()]),
618 None => {
619 if convert.starts_with('0') {
620 Ok("0")
621 } else {
622 Ok(convert)
623 }
624 }
625 }
626 }
627
628 #[must_use]
633 pub fn resize<const N2: usize>(&self) -> FixedUInt<T, N2, P> {
634 let mut array = [T::zero(); N2];
635 let min_size = N.min(N2);
636 array[..min_size].copy_from_slice(&self.array[..min_size]);
637 FixedUInt::<T, N2, P>::from_array(array)
638 }
639
640 #[cfg(feature = "num-traits")]
641 fn hex_fmt(
642 &self,
643 formatter: &mut core::fmt::Formatter<'_>,
644 uppercase: bool,
645 ) -> Result<(), core::fmt::Error>
646 where
647 u8: core::convert::TryFrom<T>,
648 {
649 type Err = core::fmt::Error;
650
651 fn to_casedigit(byte: u8, uppercase: bool) -> Result<char, core::fmt::Error> {
652 let digit = core::char::from_digit(byte as u32, 16).ok_or(Err {})?;
653 if uppercase {
654 digit.to_uppercase().next().ok_or(Err {})
655 } else {
656 digit.to_lowercase().next().ok_or(Err {})
657 }
658 }
659
660 let mut leading_zero: bool = true;
661
662 let mut maybe_write = |nibble: char| -> Result<(), core::fmt::Error> {
663 leading_zero &= nibble == '0';
664 if !leading_zero {
665 formatter.write_char(nibble)?;
666 }
667 Ok(())
668 };
669
670 for index in (0..N).rev() {
671 let val = self.array[index];
672 let mask: T = 0xff.into();
673 for j in (0..Self::WORD_SIZE as u32).rev() {
674 let masked = val & mask.shl((j * 8) as usize);
675
676 let byte = u8::try_from(masked.shr((j * 8) as usize)).map_err(|_| Err {})?;
677
678 maybe_write(to_casedigit((byte & 0xf0) >> 4, uppercase)?)?;
679 maybe_write(to_casedigit(byte & 0x0f, uppercase)?)?;
680 }
681 }
682 Ok(())
683 }
684}
685
686c0nst::c0nst! {
687 pub(crate) c0nst fn add_with_carry<T: [c0nst] ConstMachineWord, const N: usize>(
692 a: &[T; N],
693 b: &[T; N],
694 carry_in: bool,
695 ) -> ([T; N], bool) {
696 let mut result = [T::zero(); N];
697 let mut carry = carry_in;
698 let mut i = 0usize;
699 while i < N {
700 let (sum, c) = CarryingAdd::carrying_add(a[i], b[i], carry);
701 result[i] = sum;
702 carry = c;
703 i += 1;
704 }
705 (result, carry)
706 }
707
708 pub(crate) c0nst fn sub_with_borrow<T: [c0nst] ConstMachineWord, const N: usize>(
710 a: &[T; N],
711 b: &[T; N],
712 borrow_in: bool,
713 ) -> ([T; N], bool) {
714 let mut result = [T::zero(); N];
715 let mut borrow = borrow_in;
716 let mut i = 0usize;
717 while i < N {
718 let (diff, br) = BorrowingSub::borrowing_sub(a[i], b[i], borrow);
719 result[i] = diff;
720 borrow = br;
721 i += 1;
722 }
723 (result, borrow)
724 }
725
726 pub(crate) c0nst fn add_impl<T: [c0nst] ConstMachineWord, const N: usize>(
731 target: &mut [T; N],
732 other: &[T; N]
733 ) -> bool {
734 let mut carry = false;
735 let mut i = 0usize;
736 while i < N {
737 let (sum, c) = CarryingAdd::carrying_add(target[i], other[i], carry);
738 target[i] = sum;
739 carry = c;
740 i += 1;
741 }
742 carry
743 }
744
745 pub(crate) c0nst fn sub_impl<T: [c0nst] ConstMachineWord, const N: usize>(
747 target: &mut [T; N],
748 other: &[T; N]
749 ) -> bool {
750 let mut borrow = false;
751 let mut i = 0usize;
752 while i < N {
753 let (diff, br) = BorrowingSub::borrowing_sub(target[i], other[i], borrow);
754 target[i] = diff;
755 borrow = br;
756 i += 1;
757 }
758 borrow
759 }
760}
761
762c0nst::c0nst! {
763 pub(crate) c0nst fn const_shl_impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
765 target: &mut FixedUInt<T, N, P>,
766 bits: usize,
767 ) {
768 if N == 0 {
769 return;
770 }
771 let word_bits = FixedUInt::<T, N>::WORD_BITS;
772 let nwords = bits / word_bits;
773 let nbits = bits - nwords * word_bits;
774
775 if nwords >= N {
777 let mut i = 0;
778 while i < N {
779 target.array[i] = T::zero();
780 i += 1;
781 }
782 return;
783 }
784
785 let mut i = N;
787 while i > nwords {
788 i -= 1;
789 target.array[i] = target.array[i - nwords];
790 }
791 let mut i = 0;
793 while i < nwords {
794 target.array[i] = T::zero();
795 i += 1;
796 }
797
798 if nbits != 0 {
799 let mut i = N;
801 while i > 1 {
802 i -= 1;
803 let right = target.array[i] << nbits;
804 let left = target.array[i - 1] >> (word_bits - nbits);
805 target.array[i] = right | left;
806 }
807 target.array[0] <<= nbits;
808 }
809 }
810
811 pub(crate) c0nst fn const_shr_impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
813 target: &mut FixedUInt<T, N, P>,
814 bits: usize,
815 ) {
816 if N == 0 {
817 return;
818 }
819 let word_bits = FixedUInt::<T, N>::WORD_BITS;
820 let nwords = bits / word_bits;
821 let nbits = bits - nwords * word_bits;
822
823 if nwords >= N {
825 let mut i = 0;
826 while i < N {
827 target.array[i] = T::zero();
828 i += 1;
829 }
830 return;
831 }
832
833 let last_index = N - 1;
834 let last_word = N - nwords;
835
836 let mut i = 0;
838 while i < last_word {
839 target.array[i] = target.array[i + nwords];
840 i += 1;
841 }
842
843 let mut i = last_word;
845 while i < N {
846 target.array[i] = T::zero();
847 i += 1;
848 }
849
850 if nbits != 0 {
851 let mut i = 0;
853 while i < last_index {
854 let left = target.array[i] >> nbits;
855 let right = target.array[i + 1] << (word_bits - nbits);
856 target.array[i] = left | right;
857 i += 1;
858 }
859 target.array[last_index] >>= nbits;
860 }
861 }
862
863 pub(crate) c0nst fn const_shl_ct<
872 T: [c0nst] ConstMachineWord + MachineWord,
873 const N: usize,
874 P: Personality,
875 >(
876 target: &mut FixedUInt<T, N, P>,
877 bits: usize,
878 ) {
879 if N == 0 {
880 return;
881 }
882 let layers = core::mem::size_of::<usize>() * 8;
885 let mut k = 0;
886 while k < layers {
887 let amount = 1usize << k;
888 let mut shifted = *target;
890 const_shl_impl(&mut shifted, amount);
891 let bit_k = core::hint::black_box(((bits >> k) & 1) as u8);
895 let bit_k_t = <T as core::convert::From<u8>>::from(bit_k);
896 let mask = <T as core::ops::Mul>::mul(bit_k_t, <T as Bounded>::max_value());
897 let mut i = 0;
899 while i < N {
900 let diff =
901 <T as core::ops::BitXor>::bitxor(target.array[i], shifted.array[i]);
902 let masked = <T as core::ops::BitAnd>::bitand(mask, diff);
903 target.array[i] = <T as core::ops::BitXor>::bitxor(target.array[i], masked);
904 i += 1;
905 }
906 k += 1;
907 }
908 }
909
910 pub(crate) c0nst fn const_shr_ct<
913 T: [c0nst] ConstMachineWord + MachineWord,
914 const N: usize,
915 P: Personality,
916 >(
917 target: &mut FixedUInt<T, N, P>,
918 bits: usize,
919 ) {
920 if N == 0 {
921 return;
922 }
923 let layers = core::mem::size_of::<usize>() * 8;
926 let mut k = 0;
927 while k < layers {
928 let amount = 1usize << k;
929 let mut shifted = *target;
930 const_shr_impl(&mut shifted, amount);
931 let bit_k = core::hint::black_box(((bits >> k) & 1) as u8);
933 let bit_k_t = <T as core::convert::From<u8>>::from(bit_k);
934 let mask = <T as core::ops::Mul>::mul(bit_k_t, <T as Bounded>::max_value());
935 let mut i = 0;
936 while i < N {
937 let diff =
938 <T as core::ops::BitXor>::bitxor(target.array[i], shifted.array[i]);
939 let masked = <T as core::ops::BitAnd>::bitand(mask, diff);
940 target.array[i] = <T as core::ops::BitXor>::bitxor(target.array[i], masked);
941 i += 1;
942 }
943 k += 1;
944 }
945 }
946
947 pub(crate) c0nst fn const_mul<T: [c0nst] ConstMachineWord, const N: usize, const CHECK_OVERFLOW: bool, P: Personality>(
957 op1: &[T; N],
958 op2: &[T; N],
959 word_bits: usize,
960 ) -> ([T; N], bool) {
961 let mut result: [T; N] = [<T as ConstZero>::ZERO; N];
962 let mut overflowed = false;
963 let t_max = <T as ConstMachineWord>::to_double(<T as Bounded>::max_value());
964 let dw_zero = <<T as ConstMachineWord>::ConstDoubleWord as ConstZero>::ZERO;
965
966 let mut i = 0;
967 while i < N {
968 let mut carry = dw_zero;
969 let mut j = 0;
970 while j < N {
971 let round = i + j;
972 let op1_dw = <T as ConstMachineWord>::to_double(op1[i]);
973 let op2_dw = <T as ConstMachineWord>::to_double(op2[j]);
974 let mul_res = op1_dw * op2_dw;
975 let mut accumulator = if round < N {
976 <T as ConstMachineWord>::to_double(result[round])
977 } else {
978 dw_zero
979 };
980 accumulator += mul_res + carry;
981
982 match P::TAG {
983 PersonalityTag::Nct => {
984 if accumulator > t_max {
985 carry = accumulator >> word_bits;
986 accumulator &= t_max;
987 } else {
988 carry = dw_zero;
989 }
990 }
991 PersonalityTag::Ct => {
992 carry = accumulator >> word_bits;
993 accumulator &= t_max;
994 }
995 }
996 if round < N {
997 result[round] = <T as ConstMachineWord>::from_double(accumulator);
998 } else if CHECK_OVERFLOW {
999 overflowed |= accumulator != dw_zero;
1000 }
1001 j += 1;
1002 }
1003 if CHECK_OVERFLOW {
1004 overflowed |= carry != dw_zero;
1005 }
1006 i += 1;
1007 }
1008 (result, overflowed)
1009 }
1010
1011 pub(crate) c0nst fn const_word_bits<T>() -> usize {
1013 core::mem::size_of::<T>() * 8
1014 }
1015
1016 pub(crate) c0nst fn const_cmp_words<T: [c0nst] ConstMachineWord>(a: T, b: T) -> Option<core::cmp::Ordering> {
1018 if a > b {
1019 Some(core::cmp::Ordering::Greater)
1020 } else if a < b {
1021 Some(core::cmp::Ordering::Less)
1022 } else {
1023 None
1024 }
1025 }
1026
1027 pub(crate) c0nst fn const_leading_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1029 array: &[T; N],
1030 ) -> u32 {
1031 let mut ret = 0u32;
1032 let mut index = N;
1033 while index > 0 {
1034 index -= 1;
1035 let v = array[index];
1036 ret += <T as PrimBits>::leading_zeros(v);
1037 if !<T as Zero>::is_zero(&v) {
1038 break;
1039 }
1040 }
1041 ret
1042 }
1043
1044 #[inline]
1057 pub(crate) c0nst fn const_leading_zeros_ct<T: [c0nst] ConstMachineWord>(array: &[T]) -> u32 {
1058 let mut total: u32 = 0;
1059 let mut decided: u32 = 0;
1061 let mut index = array.len();
1062 while index > 0 {
1063 index -= 1;
1064 let v = array[index];
1065 let v_lz = <T as PrimBits>::leading_zeros(v);
1066 let undecided = core::hint::black_box(!decided);
1070 total += undecided & v_lz;
1071 let v_nz_bit = (!<T as Zero>::is_zero(&v)) as u32;
1073 let v_nz_mask = core::hint::black_box(v_nz_bit.wrapping_neg());
1074 decided |= v_nz_mask;
1075 }
1076 total
1077 }
1078
1079 pub(crate) c0nst fn const_trailing_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1081 array: &[T; N],
1082 ) -> u32 {
1083 let mut ret = 0u32;
1084 let mut index = 0;
1085 while index < N {
1086 let v = array[index];
1087 ret += <T as PrimBits>::trailing_zeros(v);
1088 if !<T as Zero>::is_zero(&v) {
1089 break;
1090 }
1091 index += 1;
1092 }
1093 ret
1094 }
1095
1096 #[inline]
1104 pub(crate) c0nst fn const_trailing_zeros_ct<T: [c0nst] ConstMachineWord>(array: &[T]) -> u32 {
1105 let mut total: u32 = 0;
1106 let mut decided: u32 = 0;
1108 let mut index = 0;
1109 while index < array.len() {
1110 let v = array[index];
1111 let v_tz = <T as PrimBits>::trailing_zeros(v);
1112 let undecided = core::hint::black_box(!decided);
1115 total += undecided & v_tz;
1116 let v_nz_bit = (!<T as Zero>::is_zero(&v)) as u32;
1117 let v_nz_mask = core::hint::black_box(v_nz_bit.wrapping_neg());
1118 decided |= v_nz_mask;
1119 index += 1;
1120 }
1121 total
1122 }
1123
1124 pub(crate) c0nst fn const_bit_length<T: [c0nst] ConstMachineWord, const N: usize>(
1126 array: &[T; N],
1127 ) -> usize {
1128 let word_bits = const_word_bits::<T>();
1129 let bit_size = N * word_bits;
1130 bit_size - const_leading_zeros::<T, N>(array) as usize
1131 }
1132
1133 pub(crate) c0nst fn const_is_zero<T: [c0nst] ConstMachineWord, const N: usize>(
1135 array: &[T; N],
1136 ) -> bool {
1137 let mut index = 0;
1138 while index < N {
1139 if !<T as Zero>::is_zero(&array[index]) {
1140 return false;
1141 }
1142 index += 1;
1143 }
1144 true
1145 }
1146
1147 pub(crate) c0nst fn const_is_zero_ct<T: [c0nst] ConstMachineWord, const N: usize>(
1152 array: &[T; N],
1153 ) -> bool {
1154 let mut acc = <T as ConstZero>::ZERO;
1155 let mut index = 0;
1156 while index < N {
1157 acc = <T as core::ops::BitOr>::bitor(acc, array[index]);
1158 index += 1;
1159 }
1160 <T as Zero>::is_zero(&acc)
1161 }
1162
1163 pub(crate) c0nst fn const_is_one<T: [c0nst] ConstMachineWord, const N: usize>(
1168 array: &[T; N],
1169 ) -> bool {
1170 if N == 0 || !array[0].is_one() {
1171 return false;
1172 }
1173 let mut i = 1;
1174 while i < N {
1175 if !<T as Zero>::is_zero(&array[i]) {
1176 return false;
1177 }
1178 i += 1;
1179 }
1180 true
1181 }
1182
1183 pub(crate) c0nst fn const_is_one_ct<T: [c0nst] ConstMachineWord, const N: usize>(
1188 array: &[T; N],
1189 ) -> bool {
1190 if N == 0 {
1191 return false;
1192 }
1193 let mut acc = <T as core::ops::BitXor>::bitxor(array[0], <T as ConstOne>::ONE);
1194 let mut index = 1;
1195 while index < N {
1196 acc = <T as core::ops::BitOr>::bitor(acc, array[index]);
1197 index += 1;
1198 }
1199 <T as Zero>::is_zero(&acc)
1200 }
1201
1202 pub(crate) c0nst fn const_eq_ct<T: [c0nst] ConstMachineWord, const N: usize>(
1208 a: &[T; N],
1209 b: &[T; N],
1210 ) -> bool {
1211 let mut diff = <T as ConstZero>::ZERO;
1212 let mut i = 0;
1213 while i < N {
1214 let x = <T as core::ops::BitXor>::bitxor(a[i], b[i]);
1215 diff = <T as core::ops::BitOr>::bitor(diff, x);
1216 i += 1;
1217 }
1218 <T as Zero>::is_zero(&diff)
1219 }
1220
1221 pub(crate) c0nst fn const_set_bit<T: [c0nst] ConstMachineWord, const N: usize>(
1227 array: &mut [T; N],
1228 pos: usize,
1229 ) {
1230 let word_bits = const_word_bits::<T>();
1231 let word_idx = pos / word_bits;
1232 if word_idx >= N {
1233 return;
1234 }
1235 let bit_idx = pos % word_bits;
1236 array[word_idx] |= <T as ConstOne>::ONE << bit_idx;
1237 }
1238
1239 pub(crate) c0nst fn const_cmp<T: [c0nst] ConstMachineWord, const N: usize>(
1244 a: &[T; N],
1245 b: &[T; N],
1246 ) -> core::cmp::Ordering {
1247 let mut index = N;
1248 while index > 0 {
1249 index -= 1;
1250 if let Some(ord) = const_cmp_words(a[index], b[index]) {
1251 return ord;
1252 }
1253 }
1254 core::cmp::Ordering::Equal
1255 }
1256
1257 #[inline]
1266 pub(crate) c0nst fn const_cmp_ct<T: [c0nst] ConstMachineWord>(
1267 a: &[T],
1268 b: &[T],
1269 ) -> core::cmp::Ordering {
1270 let b = match b.get(..a.len()) {
1278 Some(b) => b,
1279 None => return core::cmp::Ordering::Equal,
1280 };
1281 let mut result: u8 = 0;
1283 let mut decided: u8 = 0;
1285 let mut index = a.len();
1286 while index > 0 {
1287 index -= 1;
1288 let gt = (a[index] > b[index]) as u8;
1289 let lt = (a[index] < b[index]) as u8;
1290 let here = (gt << 1) | lt;
1292 let undecided_mask = core::hint::black_box(!decided);
1294 result |= undecided_mask & here;
1295 let here_nz_mask = core::hint::black_box(((here != 0) as u8).wrapping_neg());
1297 decided |= here_nz_mask;
1298 }
1299 match result {
1300 2 => core::cmp::Ordering::Greater,
1301 1 => core::cmp::Ordering::Less,
1302 _ => core::cmp::Ordering::Equal,
1303 }
1304 }
1305
1306 pub(crate) c0nst fn const_get_shifted_word<T: [c0nst] ConstMachineWord, const N: usize>(
1311 array: &[T; N],
1312 word_idx: usize,
1313 word_shift: usize,
1314 bit_shift: usize,
1315 ) -> T {
1316 let word_bits = const_word_bits::<T>();
1317
1318 if bit_shift >= word_bits {
1320 return <T as ConstZero>::ZERO;
1321 }
1322
1323 if word_idx < word_shift {
1324 return <T as ConstZero>::ZERO;
1325 }
1326
1327 let source_idx = word_idx - word_shift;
1328
1329 if bit_shift == 0 {
1330 if source_idx < N {
1331 array[source_idx]
1332 } else {
1333 <T as ConstZero>::ZERO
1334 }
1335 } else {
1336 let mut result = <T as ConstZero>::ZERO;
1337
1338 if source_idx < N {
1340 result |= array[source_idx] << bit_shift;
1341 }
1342
1343 if source_idx > 0 && source_idx - 1 < N {
1345 let high_bits = array[source_idx - 1] >> (word_bits - bit_shift);
1346 result |= high_bits;
1347 }
1348
1349 result
1350 }
1351 }
1352
1353 pub(crate) c0nst fn const_cmp_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
1358 array: &[T; N],
1359 other: &[T; N],
1360 shift_bits: usize,
1361 ) -> core::cmp::Ordering {
1362 let word_bits = const_word_bits::<T>();
1363
1364 if shift_bits == 0 {
1365 return const_cmp::<T, N>(array, other);
1366 }
1367
1368 let word_shift = shift_bits / word_bits;
1369 if word_shift >= N {
1370 if const_is_zero::<T, N>(array) {
1372 return core::cmp::Ordering::Equal;
1373 } else {
1374 return core::cmp::Ordering::Greater;
1375 }
1376 }
1377
1378 let bit_shift = shift_bits % word_bits;
1379
1380 let mut index = N;
1382 while index > 0 {
1383 index -= 1;
1384 let self_word = array[index];
1385 let other_shifted_word = const_get_shifted_word::<T, N>(
1386 other, index, word_shift, bit_shift
1387 );
1388
1389 if let Some(ord) = const_cmp_words(self_word, other_shifted_word) {
1390 return ord;
1391 }
1392 }
1393
1394 core::cmp::Ordering::Equal
1395 }
1396
1397 pub(crate) c0nst fn const_sub_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
1402 array: &mut [T; N],
1403 other: &[T; N],
1404 shift_bits: usize,
1405 ) {
1406 let word_bits = const_word_bits::<T>();
1407
1408 if shift_bits == 0 {
1409 sub_impl::<T, N>(array, other);
1410 return;
1411 }
1412
1413 let word_shift = shift_bits / word_bits;
1414 if word_shift >= N {
1415 return;
1416 }
1417
1418 let bit_shift = shift_bits % word_bits;
1419 let mut borrow = T::zero();
1420 let mut index = 0;
1421 while index < N {
1422 let other_word = const_get_shifted_word::<T, N>(other, index, word_shift, bit_shift);
1423 let (res, borrow1) = array[index].overflowing_sub(other_word);
1424 let (res, borrow2) = res.overflowing_sub(borrow);
1425 borrow = if borrow1 || borrow2 { T::one() } else { T::zero() };
1426 array[index] = res;
1427 index += 1;
1428 }
1429 }
1430
1431 pub(crate) c0nst fn const_div<T: [c0nst] ConstMachineWord, const N: usize>(
1435 dividend: &mut [T; N],
1436 divisor: &[T; N],
1437 ) -> [T; N] {
1438 use core::cmp::Ordering;
1439
1440 match const_cmp::<T, N>(dividend, divisor) {
1441 Ordering::Less => {
1443 let remainder = *dividend;
1444 let mut i = 0;
1445 while i < N {
1446 dividend[i] = <T as ConstZero>::ZERO;
1447 i += 1;
1448 }
1449 return remainder;
1450 }
1451 Ordering::Equal => {
1453 let mut i = 0;
1454 while i < N {
1455 dividend[i] = <T as ConstZero>::ZERO;
1456 i += 1;
1457 }
1458 if N > 0 {
1459 dividend[0] = <T as ConstOne>::ONE;
1460 }
1461 return [<T as ConstZero>::ZERO; N];
1462 }
1463 Ordering::Greater => {}
1464 }
1465
1466 let mut quotient = [<T as ConstZero>::ZERO; N];
1467
1468 let dividend_bits = const_bit_length::<T, N>(dividend);
1470 let divisor_bits = const_bit_length::<T, N>(divisor);
1471
1472 let mut bit_pos = if dividend_bits >= divisor_bits {
1473 dividend_bits - divisor_bits
1474 } else {
1475 0
1476 };
1477
1478 while bit_pos > 0 {
1480 let cmp = const_cmp_shifted::<T, N>(dividend, divisor, bit_pos);
1481 if !matches!(cmp, Ordering::Less) {
1482 break;
1483 }
1484 bit_pos -= 1;
1485 }
1486
1487 loop {
1489 let cmp = const_cmp_shifted::<T, N>(dividend, divisor, bit_pos);
1490 if !matches!(cmp, Ordering::Less) {
1491 const_sub_shifted::<T, N>(dividend, divisor, bit_pos);
1492 const_set_bit::<T, N>(&mut quotient, bit_pos);
1493 }
1494
1495 if bit_pos == 0 {
1496 break;
1497 }
1498 bit_pos -= 1;
1499 }
1500
1501 let remainder = *dividend;
1502 *dividend = quotient;
1503 remainder
1504 }
1505
1506 pub(crate) c0nst fn const_div_rem<T: [c0nst] ConstMachineWord, const N: usize>(
1510 dividend: &[T; N],
1511 divisor: &[T; N],
1512 ) -> ([T; N], [T; N]) {
1513 if const_is_zero(divisor) {
1514 maybe_panic(PanicReason::DivByZero)
1515 }
1516 let mut quotient = *dividend;
1517 let remainder = const_div(&mut quotient, divisor);
1518 (quotient, remainder)
1519 }
1520}
1521
1522c0nst::c0nst! {
1523 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> Default for FixedUInt<T, N, P> {
1524 fn default() -> Self {
1525 FixedUInt::from_array([<T as ConstZero>::ZERO; N])
1526 }
1527 }
1528
1529 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> Clone for FixedUInt<T, N, P> {
1530 fn clone(&self) -> Self {
1531 *self
1532 }
1533 }
1534}
1535
1536#[cfg(feature = "num-traits")]
1539impl<T: MachineWord, const N: usize> num_traits::Unsigned for FixedUInt<T, N, Nct> {}
1540
1541c0nst::c0nst! {
1544 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::cmp::PartialEq for FixedUInt<T, N, P> {
1545 fn eq(&self, other: &Self) -> bool {
1552 match P::TAG {
1553 PersonalityTag::Nct => self.array == other.array,
1554 PersonalityTag::Ct => const_eq_ct(&self.array, &other.array),
1555 }
1556 }
1557 }
1558
1559 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::cmp::Eq for FixedUInt<T, N, P> {}
1560
1561 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::cmp::Ord for FixedUInt<T, N, P> {
1562 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
1563 match P::TAG {
1564 PersonalityTag::Nct => const_cmp(&self.array, &other.array),
1565 PersonalityTag::Ct => const_cmp_ct(&self.array, &other.array),
1566 }
1567 }
1568 }
1569
1570 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::cmp::PartialOrd for FixedUInt<T, N, P> {
1571 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
1572 Some(self.cmp(other))
1573 }
1574 }
1575}
1576
1577c0nst::c0nst! {
1582 c0nst fn const_from_le_bytes<T: [c0nst] ConstMachineWord, const N: usize, const B: usize>(
1585 bytes: [u8; B],
1586 ) -> [T; N] {
1587 impl_from_le_bytes_slice::<T, N>(&bytes)
1588 }
1589
1590 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::convert::From<u8> for FixedUInt<T, N, P> {
1591 fn from(x: u8) -> Self {
1592 Self::from_array(const_from_le_bytes(x.to_le_bytes()))
1593 }
1594 }
1595
1596 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::convert::From<u16> for FixedUInt<T, N, P> {
1597 fn from(x: u16) -> Self {
1598 Self::from_array(const_from_le_bytes(x.to_le_bytes()))
1599 }
1600 }
1601
1602 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::convert::From<u32> for FixedUInt<T, N, P> {
1603 fn from(x: u32) -> Self {
1604 Self::from_array(const_from_le_bytes(x.to_le_bytes()))
1605 }
1606 }
1607
1608 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::convert::From<u64> for FixedUInt<T, N, P> {
1609 fn from(x: u64) -> Self {
1610 Self::from_array(const_from_le_bytes(x.to_le_bytes()))
1611 }
1612 }
1613}
1614
1615pub(crate) fn make_parse_int_err() -> core::num::ParseIntError {
1622 <u8>::from_str_radix("-", 2).err().unwrap()
1623}
1624#[cfg(feature = "num-traits")]
1625pub(crate) fn make_overflow_err() -> core::num::ParseIntError {
1626 <u8>::from_str_radix("101", 16).err().unwrap()
1627}
1628#[cfg(feature = "num-traits")]
1629pub(crate) fn make_empty_error() -> core::num::ParseIntError {
1630 <u8>::from_str_radix("", 8).err().unwrap()
1631}
1632
1633fn to_slice_hex<T: AsRef<[u8]>>(
1634 input: T,
1635 output: &mut [u8],
1636) -> Result<(), core::num::ParseIntError> {
1637 fn from_digit(byte: u8) -> Option<char> {
1638 core::char::from_digit(byte as u32, 16)
1639 }
1640 let r = input.as_ref();
1641 if r.len() * 2 != output.len() {
1642 return Err(make_parse_int_err());
1643 }
1644 for i in 0..r.len() {
1645 let byte = r[i];
1646 output[i * 2] = from_digit((byte & 0xf0) >> 4).ok_or_else(make_parse_int_err)? as u8;
1647 output[i * 2 + 1] = from_digit(byte & 0x0f).ok_or_else(make_parse_int_err)? as u8;
1648 }
1649
1650 Ok(())
1651}
1652
1653pub(super) enum PanicReason {
1654 Add,
1655 Sub,
1656 Mul,
1657 DivByZero,
1658}
1659
1660c0nst::c0nst! {
1661 pub(super) c0nst fn maybe_panic(r: PanicReason) {
1662 match r {
1663 PanicReason::Add => panic!("attempt to add with overflow"),
1664 PanicReason::Sub => panic!("attempt to subtract with overflow"),
1665 PanicReason::Mul => panic!("attempt to multiply with overflow"),
1666 PanicReason::DivByZero => panic!("attempt to divide by zero"),
1667 }
1668 }
1669
1670 pub(crate) c0nst fn const_ct_select<
1684 T: [c0nst] ConstMachineWord + MachineWord,
1685 const N: usize,
1686 P: Personality,
1687 >(
1688 if_zero: FixedUInt<T, N, P>,
1689 if_one: FixedUInt<T, N, P>,
1690 choice: u8,
1691 ) -> FixedUInt<T, N, P> {
1692 let choice = core::hint::black_box(choice);
1693 let bit_t = <T as core::convert::From<u8>>::from(choice);
1694 let mask = <T as core::ops::Mul>::mul(bit_t, <T as Bounded>::max_value());
1695 let mut result = if_zero;
1696 let mut i = 0;
1697 while i < N {
1698 let diff = <T as core::ops::BitXor>::bitxor(if_zero.array[i], if_one.array[i]);
1699 let masked = <T as core::ops::BitAnd>::bitand(mask, diff);
1700 result.array[i] = <T as core::ops::BitXor>::bitxor(if_zero.array[i], masked);
1701 i += 1;
1702 }
1703 result
1704 }
1705
1706 pub(super) c0nst fn maybe_panic_if<P: Personality>(
1707 overflow: bool,
1708 reason: PanicReason,
1709 ) {
1710 match P::TAG {
1711 PersonalityTag::Nct => {
1712 if overflow {
1713 maybe_panic(reason);
1714 }
1715 }
1716 PersonalityTag::Ct => {
1717 let _ = overflow;
1718 let _ = reason;
1719 }
1720 }
1721 }
1722}
1723
1724#[cfg(test)]
1727#[cfg(feature = "num-traits")]
1728mod tests {
1729 use super::FixedUInt as Bn;
1730 use super::*;
1731 use const_num_traits::{One, Zero};
1732 use num_traits::{FromPrimitive, Num, ToPrimitive};
1733
1734 type Bn8 = Bn<u8, 8>;
1735 type Bn16 = Bn<u16, 4>;
1736 type Bn32 = Bn<u32, 2>;
1737
1738 c0nst::c0nst! {
1739 pub c0nst fn test_add<T: [c0nst] ConstMachineWord, const N: usize>(
1740 a: &mut [T; N],
1741 b: &[T; N]
1742 ) -> bool {
1743 add_impl(a, b)
1744 }
1745
1746 pub c0nst fn test_sub<T: [c0nst] ConstMachineWord, const N: usize>(
1747 a: &mut [T; N],
1748 b: &[T; N]
1749 ) -> bool {
1750 sub_impl(a, b)
1751 }
1752
1753 pub c0nst fn test_mul<T: [c0nst] ConstMachineWord, const N: usize>(
1754 a: &[T; N],
1755 b: &[T; N],
1756 word_bits: usize,
1757 ) -> ([T; N], bool) {
1758 const_mul::<T, N, true, const_num_traits::Nct>(a, b, word_bits)
1759 }
1760
1761 pub c0nst fn arr_leading_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1762 a: &[T; N],
1763 ) -> u32 {
1764 const_leading_zeros::<T, N>(a)
1765 }
1766
1767 pub c0nst fn arr_trailing_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1768 a: &[T; N],
1769 ) -> u32 {
1770 const_trailing_zeros::<T, N>(a)
1771 }
1772
1773 pub c0nst fn arr_bit_length<T: [c0nst] ConstMachineWord, const N: usize>(
1774 a: &[T; N],
1775 ) -> usize {
1776 const_bit_length::<T, N>(a)
1777 }
1778
1779 pub c0nst fn arr_is_zero<T: [c0nst] ConstMachineWord, const N: usize>(
1780 a: &[T; N],
1781 ) -> bool {
1782 const_is_zero::<T, N>(a)
1783 }
1784
1785 pub c0nst fn arr_set_bit<T: [c0nst] ConstMachineWord, const N: usize>(
1786 a: &mut [T; N],
1787 pos: usize,
1788 ) {
1789 const_set_bit::<T, N>(a, pos)
1790 }
1791
1792 pub c0nst fn arr_cmp<T: [c0nst] ConstMachineWord, const N: usize>(
1793 a: &[T; N],
1794 b: &[T; N],
1795 ) -> core::cmp::Ordering {
1796 const_cmp::<T, N>(a, b)
1797 }
1798
1799 pub c0nst fn arr_cmp_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
1800 a: &[T; N],
1801 b: &[T; N],
1802 shift_bits: usize,
1803 ) -> core::cmp::Ordering {
1804 const_cmp_shifted::<T, N>(a, b, shift_bits)
1805 }
1806
1807 pub c0nst fn arr_get_shifted_word<T: [c0nst] ConstMachineWord, const N: usize>(
1808 a: &[T; N],
1809 word_idx: usize,
1810 word_shift: usize,
1811 bit_shift: usize,
1812 ) -> T {
1813 const_get_shifted_word::<T, N>(a, word_idx, word_shift, bit_shift)
1814 }
1815 }
1816
1817 #[test]
1818 fn test_const_add_impl() {
1819 let mut a: [u8; 4] = [1, 0, 0, 0];
1821 let b: [u8; 4] = [2, 0, 0, 0];
1822 let overflow = test_add(&mut a, &b);
1823 assert_eq!(a, [3, 0, 0, 0]);
1824 assert!(!overflow);
1825
1826 let mut a: [u8; 4] = [255, 0, 0, 0];
1828 let b: [u8; 4] = [1, 0, 0, 0];
1829 let overflow = test_add(&mut a, &b);
1830 assert_eq!(a, [0, 1, 0, 0]);
1831 assert!(!overflow);
1832
1833 let mut a: [u8; 4] = [255, 255, 255, 255];
1835 let b: [u8; 4] = [1, 0, 0, 0];
1836 let overflow = test_add(&mut a, &b);
1837 assert_eq!(a, [0, 0, 0, 0]);
1838 assert!(overflow);
1839
1840 let mut a: [u32; 2] = [0xFFFFFFFF, 0];
1842 let b: [u32; 2] = [1, 0];
1843 let overflow = test_add(&mut a, &b);
1844 assert_eq!(a, [0, 1]);
1845 assert!(!overflow);
1846
1847 #[cfg(feature = "nightly")]
1848 {
1849 const ADD_RESULT: ([u8; 4], bool) = {
1850 let mut a = [1u8, 0, 0, 0];
1851 let b = [2u8, 0, 0, 0];
1852 let overflow = test_add(&mut a, &b);
1853 (a, overflow)
1854 };
1855 assert_eq!(ADD_RESULT, ([3, 0, 0, 0], false));
1856 }
1857 }
1858
1859 #[test]
1860 fn test_const_sub_impl() {
1861 let mut a: [u8; 4] = [3, 0, 0, 0];
1863 let b: [u8; 4] = [1, 0, 0, 0];
1864 let overflow = test_sub(&mut a, &b);
1865 assert_eq!(a, [2, 0, 0, 0]);
1866 assert!(!overflow);
1867
1868 let mut a: [u8; 4] = [0, 1, 0, 0];
1870 let b: [u8; 4] = [1, 0, 0, 0];
1871 let overflow = test_sub(&mut a, &b);
1872 assert_eq!(a, [255, 0, 0, 0]);
1873 assert!(!overflow);
1874
1875 let mut a: [u8; 4] = [0, 0, 0, 0];
1877 let b: [u8; 4] = [1, 0, 0, 0];
1878 let overflow = test_sub(&mut a, &b);
1879 assert_eq!(a, [255, 255, 255, 255]);
1880 assert!(overflow);
1881
1882 let mut a: [u32; 2] = [0, 1];
1884 let b: [u32; 2] = [1, 0];
1885 let overflow = test_sub(&mut a, &b);
1886 assert_eq!(a, [0xFFFFFFFF, 0]);
1887 assert!(!overflow);
1888
1889 #[cfg(feature = "nightly")]
1890 {
1891 const SUB_RESULT: ([u8; 4], bool) = {
1892 let mut a = [3u8, 0, 0, 0];
1893 let b = [1u8, 0, 0, 0];
1894 let overflow = test_sub(&mut a, &b);
1895 (a, overflow)
1896 };
1897 assert_eq!(SUB_RESULT, ([2, 0, 0, 0], false));
1898 }
1899 }
1900
1901 #[test]
1902 fn test_const_mul_impl() {
1903 let a: [u8; 2] = [3, 0];
1905 let b: [u8; 2] = [4, 0];
1906 let (result, overflow) = test_mul(&a, &b, 8);
1907 assert_eq!(result, [12, 0]);
1908 assert!(!overflow);
1909
1910 let a: [u8; 2] = [200, 0];
1912 let b: [u8; 2] = [2, 0];
1913 let (result, overflow) = test_mul(&a, &b, 8);
1914 assert_eq!(result, [0x90, 0x01]);
1915 assert!(!overflow);
1916
1917 let a: [u8; 2] = [0, 1]; let b: [u8; 2] = [0, 1]; let (_result, overflow) = test_mul(&a, &b, 8);
1921 assert!(overflow);
1922
1923 let a: [u8; 3] = [0, 0, 1];
1927 let b: [u8; 3] = [0, 0, 1];
1928 let (_result, overflow) = test_mul(&a, &b, 8);
1929 assert!(overflow, "N=3 high-position overflow not detected");
1930
1931 let a: [u8; 3] = [0, 0, 2];
1935 let b: [u8; 3] = [0, 0, 2];
1936 let (_result, overflow) = test_mul(&a, &b, 8);
1937 assert!(
1938 overflow,
1939 "N=3 high-position overflow with larger values not detected"
1940 );
1941
1942 let a: [u8; 3] = [0, 1, 0];
1946 let b: [u8; 3] = [0, 1, 0];
1947 let (result, overflow) = test_mul(&a, &b, 8);
1948 assert_eq!(result, [0, 0, 1]);
1949 assert!(
1950 !overflow,
1951 "N=3 non-overflow incorrectly detected as overflow"
1952 );
1953
1954 let a: [u8; 3] = [255, 0, 0];
1958 let b: [u8; 3] = [255, 0, 0];
1959 let (result, overflow) = test_mul(&a, &b, 8);
1960 assert_eq!(result, [0x01, 0xFE, 0x00]);
1961 assert!(!overflow);
1962
1963 #[cfg(feature = "nightly")]
1964 {
1965 const MUL_RESULT: ([u8; 2], bool) = test_mul(&[3u8, 0], &[4u8, 0], 8);
1966 assert_eq!(MUL_RESULT, ([12, 0], false));
1967 }
1968 }
1969
1970 #[test]
1971 fn test_const_helpers() {
1972 assert_eq!(arr_leading_zeros(&[0u8, 0, 0, 0]), 32); assert_eq!(arr_leading_zeros(&[1u8, 0, 0, 0]), 31); assert_eq!(arr_leading_zeros(&[0u8, 0, 0, 1]), 7); assert_eq!(arr_leading_zeros(&[0u8, 0, 0, 0x80]), 0); assert_eq!(arr_leading_zeros(&[255u8, 255, 255, 255]), 0); assert_eq!(arr_trailing_zeros(&[0u8, 0, 0, 0]), 32); assert_eq!(arr_trailing_zeros(&[1u8, 0, 0, 0]), 0); assert_eq!(arr_trailing_zeros(&[0u8, 1, 0, 0]), 8); assert_eq!(arr_trailing_zeros(&[0u8, 0, 0, 1]), 24); assert_eq!(arr_trailing_zeros(&[0x80u8, 0, 0, 0]), 7); assert_eq!(arr_bit_length(&[0u8, 0, 0, 0]), 0); assert_eq!(arr_bit_length(&[1u8, 0, 0, 0]), 1); assert_eq!(arr_bit_length(&[2u8, 0, 0, 0]), 2); assert_eq!(arr_bit_length(&[3u8, 0, 0, 0]), 2); assert_eq!(arr_bit_length(&[0u8, 1, 0, 0]), 9); assert_eq!(arr_bit_length(&[0xF0u8, 0, 0, 0]), 8); assert_eq!(arr_bit_length(&[255u8, 255, 255, 255]), 32); assert!(arr_is_zero(&[0u8, 0, 0, 0]));
1997 assert!(!arr_is_zero(&[1u8, 0, 0, 0]));
1998 assert!(!arr_is_zero(&[0u8, 0, 0, 1]));
1999 assert!(!arr_is_zero(&[0u8, 1, 0, 0]));
2000
2001 let mut arr: [u8; 4] = [0, 0, 0, 0];
2003 arr_set_bit(&mut arr, 0);
2004 assert_eq!(arr, [1, 0, 0, 0]);
2005
2006 let mut arr: [u8; 4] = [0, 0, 0, 0];
2007 arr_set_bit(&mut arr, 8);
2008 assert_eq!(arr, [0, 1, 0, 0]);
2009
2010 let mut arr: [u8; 4] = [0, 0, 0, 0];
2011 arr_set_bit(&mut arr, 31);
2012 assert_eq!(arr, [0, 0, 0, 0x80]);
2013
2014 let mut arr: [u8; 4] = [0, 0, 0, 0];
2016 arr_set_bit(&mut arr, 0);
2017 arr_set_bit(&mut arr, 3);
2018 arr_set_bit(&mut arr, 8);
2019 assert_eq!(arr, [0b00001001, 1, 0, 0]);
2020
2021 let mut arr: [u8; 4] = [0, 0, 0, 0];
2023 arr_set_bit(&mut arr, 32);
2024 assert_eq!(arr, [0, 0, 0, 0]);
2025
2026 assert_eq!(arr_leading_zeros(&[0u32, 0]), 64);
2028 assert_eq!(arr_leading_zeros(&[1u32, 0]), 63);
2029 assert_eq!(arr_leading_zeros(&[0u32, 1]), 31);
2030 assert_eq!(arr_trailing_zeros(&[0u32, 0]), 64);
2031 assert_eq!(arr_trailing_zeros(&[0u32, 1]), 32);
2032 assert_eq!(arr_bit_length(&[0u32, 0]), 0);
2033 assert_eq!(arr_bit_length(&[1u32, 0]), 1);
2034 assert_eq!(arr_bit_length(&[0u32, 1]), 33);
2035
2036 #[cfg(feature = "nightly")]
2037 {
2038 const LEADING: u32 = arr_leading_zeros(&[0u8, 0, 1, 0]);
2039 assert_eq!(LEADING, 15);
2040
2041 const TRAILING: u32 = arr_trailing_zeros(&[0u8, 0, 1, 0]);
2042 assert_eq!(TRAILING, 16);
2043
2044 const BIT_LEN: usize = arr_bit_length(&[0u8, 0, 1, 0]);
2045 assert_eq!(BIT_LEN, 17);
2046
2047 const IS_ZERO: bool = arr_is_zero(&[0u8, 0, 0, 0]);
2048 assert!(IS_ZERO);
2049
2050 const NOT_ZERO: bool = arr_is_zero(&[0u8, 1, 0, 0]);
2051 assert!(!NOT_ZERO);
2052
2053 const SET_BIT_RESULT: [u8; 4] = {
2054 let mut arr = [0u8, 0, 0, 0];
2055 arr_set_bit(&mut arr, 10);
2056 arr
2057 };
2058 assert_eq!(SET_BIT_RESULT, [0, 0b00000100, 0, 0]);
2059 }
2060 }
2061
2062 #[test]
2063 fn test_const_cmp() {
2064 use core::cmp::Ordering;
2065
2066 assert_eq!(arr_cmp(&[1u8, 2, 3, 4], &[1u8, 2, 3, 4]), Ordering::Equal);
2068 assert_eq!(arr_cmp(&[0u8, 0, 0, 0], &[0u8, 0, 0, 0]), Ordering::Equal);
2069
2070 assert_eq!(arr_cmp(&[0u8, 0, 0, 2], &[0u8, 0, 0, 1]), Ordering::Greater);
2072
2073 assert_eq!(arr_cmp(&[0u8, 0, 0, 1], &[0u8, 0, 0, 2]), Ordering::Less);
2075
2076 assert_eq!(arr_cmp(&[2u8, 0, 0, 0], &[1u8, 0, 0, 0]), Ordering::Greater);
2078
2079 assert_eq!(arr_cmp(&[1u8, 0, 0, 0], &[2u8, 0, 0, 0]), Ordering::Less);
2081
2082 assert_eq!(arr_cmp(&[0u32, 1], &[0u32, 1]), Ordering::Equal);
2084 assert_eq!(arr_cmp(&[0u32, 2], &[0u32, 1]), Ordering::Greater);
2085 assert_eq!(arr_cmp(&[0u32, 1], &[0u32, 2]), Ordering::Less);
2086
2087 #[cfg(feature = "nightly")]
2088 {
2089 const CMP_EQ: Ordering = arr_cmp(&[1u8, 2, 3, 4], &[1u8, 2, 3, 4]);
2090 const CMP_GT: Ordering = arr_cmp(&[0u8, 0, 0, 2], &[0u8, 0, 0, 1]);
2091 const CMP_LT: Ordering = arr_cmp(&[0u8, 0, 0, 1], &[0u8, 0, 0, 2]);
2092 assert_eq!(CMP_EQ, Ordering::Equal);
2093 assert_eq!(CMP_GT, Ordering::Greater);
2094 assert_eq!(CMP_LT, Ordering::Less);
2095 }
2096 }
2097
2098 #[test]
2102 fn test_ct_eq() {
2103 type C = Bn<u8, 4, const_num_traits::Ct>;
2104 assert!(C::from(0x0102_0304u32) == C::from(0x0102_0304u32));
2105 assert!(C::from(0u32) == C::from(0u32));
2106 assert!(C::from(0x0102_0304u32) != C::from(0x8102_0304u32));
2108 assert!(C::from(0x0102_0304u32) != C::from(0x0102_0384u32));
2110 assert!(C::from(1u32) != C::from(0u32));
2111 }
2112
2113 #[test]
2114 fn test_const_cmp_shifted() {
2115 use core::cmp::Ordering;
2116
2117 assert_eq!(
2119 arr_cmp_shifted(&[1u8, 0, 0, 0], &[1u8, 0, 0, 0], 0),
2120 Ordering::Equal
2121 );
2122
2123 assert_eq!(
2125 arr_cmp_shifted(&[0u8, 1, 0, 0], &[1u8, 0, 0, 0], 8),
2126 Ordering::Equal
2127 );
2128
2129 assert_eq!(
2131 arr_cmp_shifted(&[0u8, 2, 0, 0], &[1u8, 0, 0, 0], 8),
2132 Ordering::Greater
2133 );
2134
2135 assert_eq!(
2137 arr_cmp_shifted(&[0u8, 0, 0, 0], &[1u8, 0, 0, 0], 8),
2138 Ordering::Less
2139 );
2140
2141 assert_eq!(
2144 arr_cmp_shifted(&[1u8, 0, 0, 0], &[1u8, 0, 0, 0], 32),
2145 Ordering::Greater
2146 );
2147
2148 assert_eq!(
2150 arr_cmp_shifted(&[0u8, 0, 0, 0], &[255u8, 255, 255, 255], 32),
2151 Ordering::Equal
2152 );
2153
2154 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 0, 1, 0), 0);
2158 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 1, 1, 0), 1);
2159 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 2, 1, 0), 2);
2160
2161 assert_eq!(arr_get_shifted_word(&[0x0Fu8, 0xF0, 0, 0], 0, 0, 4), 0xF0);
2165 assert_eq!(arr_get_shifted_word(&[0x0Fu8, 0xF0, 0, 0], 1, 0, 4), 0x00);
2167
2168 assert_eq!(arr_get_shifted_word(&[0xFFu8, 0x00, 0, 0], 0, 0, 4), 0xF0);
2171 assert_eq!(arr_get_shifted_word(&[0xFFu8, 0x00, 0, 0], 1, 0, 4), 0x0F);
2173
2174 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 0, 1, 4), 0);
2178 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 1, 1, 4), 0xB0);
2180 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 2, 1, 4), 0xDA);
2182
2183 #[cfg(feature = "nightly")]
2184 {
2185 const CMP_SHIFTED_EQ: Ordering = arr_cmp_shifted(&[0u8, 1, 0, 0], &[1u8, 0, 0, 0], 8);
2186 const CMP_SHIFTED_GT: Ordering = arr_cmp_shifted(&[0u8, 2, 0, 0], &[1u8, 0, 0, 0], 8);
2187 assert_eq!(CMP_SHIFTED_EQ, Ordering::Equal);
2188 assert_eq!(CMP_SHIFTED_GT, Ordering::Greater);
2189 }
2190 }
2191
2192 #[test]
2193 fn test_core_convert_u8() {
2194 let f = Bn::<u8, 1>::from(1u8);
2195 assert_eq!(f.array, [1]);
2196 let f = Bn::<u8, 2>::from(1u8);
2197 assert_eq!(f.array, [1, 0]);
2198
2199 let f = Bn::<u16, 1>::from(1u8);
2200 assert_eq!(f.array, [1]);
2201 let f = Bn::<u16, 2>::from(1u8);
2202 assert_eq!(f.array, [1, 0]);
2203
2204 #[cfg(feature = "nightly")]
2205 {
2206 const F1: Bn<u8, 2> = Bn::<u8, 2>::from(42u8);
2207 assert_eq!(F1.array, [42, 0]);
2208 }
2209 }
2210
2211 #[test]
2212 fn test_core_convert_u16() {
2213 let f = Bn::<u8, 1>::from(1u16);
2214 assert_eq!(f.array, [1]);
2215 let f = Bn::<u8, 2>::from(1u16);
2216 assert_eq!(f.array, [1, 0]);
2217
2218 let f = Bn::<u8, 1>::from(256u16);
2219 assert_eq!(f.array, [0]);
2220 let f = Bn::<u8, 2>::from(257u16);
2221 assert_eq!(f.array, [1, 1]);
2222 let f = Bn::<u8, 2>::from(65535u16);
2223 assert_eq!(f.array, [255, 255]);
2224
2225 let f = Bn::<u16, 1>::from(1u16);
2226 assert_eq!(f.array, [1]);
2227 let f = Bn::<u16, 2>::from(1u16);
2228 assert_eq!(f.array, [1, 0]);
2229
2230 let f = Bn::<u16, 1>::from(65535u16);
2231 assert_eq!(f.array, [65535]);
2232
2233 #[cfg(feature = "nightly")]
2234 {
2235 const F1: Bn<u8, 2> = Bn::<u8, 2>::from(0x0102u16);
2236 assert_eq!(F1.array, [0x02, 0x01]);
2237 }
2238 }
2239
2240 #[test]
2241 fn test_core_convert_u32() {
2242 let f = Bn::<u8, 1>::from(1u32);
2243 assert_eq!(f.array, [1]);
2244 let f = Bn::<u8, 1>::from(256u32);
2245 assert_eq!(f.array, [0]);
2246
2247 let f = Bn::<u8, 2>::from(1u32);
2248 assert_eq!(f.array, [1, 0]);
2249 let f = Bn::<u8, 2>::from(257u32);
2250 assert_eq!(f.array, [1, 1]);
2251 let f = Bn::<u8, 2>::from(65535u32);
2252 assert_eq!(f.array, [255, 255]);
2253
2254 let f = Bn::<u8, 4>::from(1u32);
2255 assert_eq!(f.array, [1, 0, 0, 0]);
2256 let f = Bn::<u8, 4>::from(257u32);
2257 assert_eq!(f.array, [1, 1, 0, 0]);
2258 let f = Bn::<u8, 4>::from(u32::MAX);
2259 assert_eq!(f.array, [255, 255, 255, 255]);
2260
2261 let f = Bn::<u8, 1>::from(1u32);
2262 assert_eq!(f.array, [1]);
2263 let f = Bn::<u8, 1>::from(256u32);
2264 assert_eq!(f.array, [0]);
2265
2266 let f = Bn::<u16, 2>::from(65537u32);
2267 assert_eq!(f.array, [1, 1]);
2268
2269 let f = Bn::<u32, 1>::from(1u32);
2270 assert_eq!(f.array, [1]);
2271 let f = Bn::<u32, 2>::from(1u32);
2272 assert_eq!(f.array, [1, 0]);
2273
2274 let f = Bn::<u32, 1>::from(65537u32);
2275 assert_eq!(f.array, [65537]);
2276
2277 let f = Bn::<u32, 1>::from(u32::MAX);
2278 assert_eq!(f.array, [4294967295]);
2279
2280 #[cfg(feature = "nightly")]
2281 {
2282 const F1: Bn<u8, 4> = Bn::<u8, 4>::from(0x01020304u32);
2283 assert_eq!(F1.array, [0x04, 0x03, 0x02, 0x01]);
2284 }
2285 }
2286
2287 #[test]
2288 fn test_core_convert_u64() {
2289 let f = Bn::<u8, 8>::from(0x0102030405060708u64);
2290 assert_eq!(f.array, [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
2291
2292 let f = Bn::<u16, 4>::from(0x0102030405060708u64);
2293 assert_eq!(f.array, [0x0708, 0x0506, 0x0304, 0x0102]);
2294
2295 let f = Bn::<u32, 2>::from(0x0102030405060708u64);
2296 assert_eq!(f.array, [0x05060708, 0x01020304]);
2297
2298 let f = Bn::<u64, 1>::from(0x0102030405060708u64);
2299 assert_eq!(f.array, [0x0102030405060708]);
2300
2301 #[cfg(feature = "nightly")]
2302 {
2303 const F1: Bn<u8, 8> = Bn::<u8, 8>::from(0x0102030405060708u64);
2304 assert_eq!(F1.array, [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
2305 }
2306 }
2307
2308 #[test]
2309 fn testsimple() {
2310 assert_eq!(Bn::<u8, 8>::new(), Bn::<u8, 8>::new());
2311
2312 assert_eq!(Bn::<u8, 8>::from_u8(3).unwrap().to_u32(), Some(3));
2313 assert_eq!(Bn::<u16, 4>::from_u8(3).unwrap().to_u32(), Some(3));
2314 assert_eq!(Bn::<u32, 2>::from_u8(3).unwrap().to_u32(), Some(3));
2315 assert_eq!(Bn::<u32, 2>::from_u64(3).unwrap().to_u32(), Some(3));
2316 assert_eq!(Bn::<u8, 8>::from_u64(255).unwrap().to_u32(), Some(255));
2317 assert_eq!(Bn::<u8, 8>::from_u64(256).unwrap().to_u32(), Some(256));
2318 assert_eq!(Bn::<u8, 8>::from_u64(65536).unwrap().to_u32(), Some(65536));
2319 }
2320 #[test]
2321 fn testfrom() {
2322 let mut n1 = Bn::<u8, 8>::new();
2323 n1.array[0] = 1;
2324 assert_eq!(Some(1), n1.to_u32());
2325 n1.array[1] = 1;
2326 assert_eq!(Some(257), n1.to_u32());
2327
2328 let mut n2 = Bn::<u16, 8>::new();
2329 n2.array[0] = 0xffff;
2330 assert_eq!(Some(65535), n2.to_u32());
2331 n2.array[0] = 0x0;
2332 n2.array[2] = 0x1;
2333 assert_eq!(None, n2.to_u32());
2335 assert_eq!(Some(0x100000000), n2.to_u64());
2336 }
2337
2338 #[test]
2339 fn test_from_str_bitlengths() {
2340 let test_s64 = "81906f5e4d3c2c01";
2341 let test_u64: u64 = 0x81906f5e4d3c2c01;
2342 let bb = Bn8::from_str_radix(test_s64, 16).unwrap();
2343 let cc = Bn8::from_u64(test_u64).unwrap();
2344 assert_eq!(cc.array, [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81]);
2345 assert_eq!(bb.array, [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81]);
2346 let dd = Bn16::from_u64(test_u64).unwrap();
2347 let ff = Bn16::from_str_radix(test_s64, 16).unwrap();
2348 assert_eq!(dd.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190]);
2349 assert_eq!(ff.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190]);
2350 let ee = Bn32::from_u64(test_u64).unwrap();
2351 let gg = Bn32::from_str_radix(test_s64, 16).unwrap();
2352 assert_eq!(ee.array, [0x4d3c2c01, 0x81906f5e]);
2353 assert_eq!(gg.array, [0x4d3c2c01, 0x81906f5e]);
2354 }
2355
2356 #[test]
2357 fn test_from_str_stringlengths() {
2358 let ab = Bn::<u8, 9>::from_str_radix("2281906f5e4d3c2c01", 16).unwrap();
2359 assert_eq!(
2360 ab.array,
2361 [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81, 0x22]
2362 );
2363 assert_eq!(
2364 [0x2c01, 0x4d3c, 0x6f5e, 0],
2365 Bn::<u16, 4>::from_str_radix("6f5e4d3c2c01", 16)
2366 .unwrap()
2367 .array
2368 );
2369 assert_eq!(
2370 [0x2c01, 0x4d3c, 0x6f5e, 0x190],
2371 Bn::<u16, 4>::from_str_radix("1906f5e4d3c2c01", 16)
2372 .unwrap()
2373 .array
2374 );
2375 assert_eq!(
2376 Err(make_overflow_err()),
2377 Bn::<u16, 4>::from_str_radix("f81906f5e4d3c2c01", 16)
2378 );
2379 assert_eq!(
2380 Err(make_overflow_err()),
2381 Bn::<u16, 4>::from_str_radix("af81906f5e4d3c2c01", 16)
2382 );
2383 assert_eq!(
2384 Err(make_overflow_err()),
2385 Bn::<u16, 4>::from_str_radix("baaf81906f5e4d3c2c01", 16)
2386 );
2387 let ac = Bn::<u16, 5>::from_str_radix("baaf81906f5e4d3c2c01", 16).unwrap();
2388 assert_eq!(ac.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190, 0xbaaf]);
2389 }
2390
2391 #[test]
2392 fn test_resize() {
2393 type TestInt1 = FixedUInt<u32, 1>;
2394 type TestInt2 = FixedUInt<u32, 2>;
2395
2396 let a = TestInt1::from(u32::MAX);
2397 let b: TestInt2 = a.resize();
2398 assert_eq!(b, TestInt2::from([u32::MAX, 0]));
2399
2400 let a = TestInt2::from([u32::MAX, u32::MAX]);
2401 let b: TestInt1 = a.resize();
2402 assert_eq!(b, TestInt1::from(u32::MAX));
2403 }
2404
2405 #[test]
2406 fn test_bit_length() {
2407 assert_eq!(0, Bn8::from_u8(0).unwrap().bit_length());
2408 assert_eq!(1, Bn8::from_u8(1).unwrap().bit_length());
2409 assert_eq!(2, Bn8::from_u8(2).unwrap().bit_length());
2410 assert_eq!(2, Bn8::from_u8(3).unwrap().bit_length());
2411 assert_eq!(7, Bn8::from_u8(0x70).unwrap().bit_length());
2412 assert_eq!(8, Bn8::from_u8(0xF0).unwrap().bit_length());
2413 assert_eq!(9, Bn8::from_u16(0x1F0).unwrap().bit_length());
2414
2415 assert_eq!(20, Bn8::from_u64(990223).unwrap().bit_length());
2416 assert_eq!(32, Bn8::from_u64(0xefffffff).unwrap().bit_length());
2417 assert_eq!(32, Bn8::from_u64(0x8fffffff).unwrap().bit_length());
2418 assert_eq!(31, Bn8::from_u64(0x7fffffff).unwrap().bit_length());
2419 assert_eq!(34, Bn8::from_u64(0x3ffffffff).unwrap().bit_length());
2420
2421 assert_eq!(0, Bn32::from_u8(0).unwrap().bit_length());
2422 assert_eq!(1, Bn32::from_u8(1).unwrap().bit_length());
2423 assert_eq!(2, Bn32::from_u8(2).unwrap().bit_length());
2424 assert_eq!(2, Bn32::from_u8(3).unwrap().bit_length());
2425 assert_eq!(7, Bn32::from_u8(0x70).unwrap().bit_length());
2426 assert_eq!(8, Bn32::from_u8(0xF0).unwrap().bit_length());
2427 assert_eq!(9, Bn32::from_u16(0x1F0).unwrap().bit_length());
2428
2429 assert_eq!(20, Bn32::from_u64(990223).unwrap().bit_length());
2430 assert_eq!(32, Bn32::from_u64(0xefffffff).unwrap().bit_length());
2431 assert_eq!(32, Bn32::from_u64(0x8fffffff).unwrap().bit_length());
2432 assert_eq!(31, Bn32::from_u64(0x7fffffff).unwrap().bit_length());
2433 assert_eq!(34, Bn32::from_u64(0x3ffffffff).unwrap().bit_length());
2434 }
2435
2436 #[test]
2437 fn test_bit_length_1000() {
2438 let value = Bn32::from_u16(1000).unwrap();
2440
2441 assert_eq!(value.to_u32().unwrap(), 1000);
2444 assert_eq!(value.bit_length(), 10);
2445
2446 assert_eq!(Bn32::from_u16(512).unwrap().bit_length(), 10); assert_eq!(Bn32::from_u16(1023).unwrap().bit_length(), 10); assert_eq!(Bn32::from_u16(1024).unwrap().bit_length(), 11); assert_eq!(Bn8::from_u16(1000).unwrap().bit_length(), 10);
2453 assert_eq!(Bn16::from_u16(1000).unwrap().bit_length(), 10);
2454
2455 let value_from_str = Bn32::from_str_radix("1000", 10).unwrap();
2457 assert_eq!(value_from_str.bit_length(), 10);
2458
2459 let value_from_bytes = Bn32::from_le_bytes(&1000u16.to_le_bytes());
2461 assert_eq!(
2463 value_from_bytes.to_u32().unwrap_or(0),
2464 1000,
2465 "from_le_bytes didn't create the correct value"
2466 );
2467 assert_eq!(value_from_bytes.bit_length(), 10);
2468 }
2469 #[test]
2470 fn test_cmp() {
2471 let f0 = <Bn8 as Zero>::zero();
2472 let f1 = <Bn8 as Zero>::zero();
2473 let f2 = <Bn8 as One>::one();
2474 assert_eq!(f0, f1);
2475 assert!(f2 > f0);
2476 assert!(f0 < f2);
2477 let f3 = Bn32::from_u64(990223).unwrap();
2478 assert_eq!(f3, Bn32::from_u64(990223).unwrap());
2479 let f4 = Bn32::from_u64(990224).unwrap();
2480 assert!(f4 > Bn32::from_u64(990223).unwrap());
2481
2482 let f3 = Bn8::from_u64(990223).unwrap();
2483 assert_eq!(f3, Bn8::from_u64(990223).unwrap());
2484 let f4 = Bn8::from_u64(990224).unwrap();
2485 assert!(f4 > Bn8::from_u64(990223).unwrap());
2486
2487 #[cfg(feature = "nightly")]
2488 {
2489 use core::cmp::Ordering;
2490
2491 const A: FixedUInt<u8, 2> = FixedUInt::from_array([10, 0]);
2492 const B: FixedUInt<u8, 2> = FixedUInt::from_array([20, 0]);
2493 const C: FixedUInt<u8, 2> = FixedUInt::from_array([10, 0]);
2494
2495 const CMP_LT: Ordering = A.cmp(&B);
2496 const CMP_GT: Ordering = B.cmp(&A);
2497 const CMP_EQ: Ordering = A.cmp(&C);
2498 const EQ_TRUE: bool = A.eq(&C);
2499 const EQ_FALSE: bool = A.eq(&B);
2500
2501 assert_eq!(CMP_LT, Ordering::Less);
2502 assert_eq!(CMP_GT, Ordering::Greater);
2503 assert_eq!(CMP_EQ, Ordering::Equal);
2504 assert!(EQ_TRUE);
2505 assert!(!EQ_FALSE);
2506 }
2507 }
2508
2509 #[test]
2510 fn test_default() {
2511 let d: Bn8 = Default::default();
2512 assert!(<Bn8 as const_num_traits::Zero>::is_zero(&d));
2513
2514 #[cfg(feature = "nightly")]
2515 {
2516 const D: FixedUInt<u8, 2> = <FixedUInt<u8, 2> as Default>::default();
2517 assert!(<FixedUInt<u8, 2> as const_num_traits::Zero>::is_zero(&D));
2518 }
2519 }
2520
2521 #[test]
2522 fn test_clone() {
2523 let a: Bn8 = 42u8.into();
2524 let b = a;
2525 assert_eq!(a, b);
2526
2527 #[cfg(feature = "nightly")]
2528 {
2529 const A: FixedUInt<u8, 2> = FixedUInt::from_array([42, 0]);
2530 const B: FixedUInt<u8, 2> = A.clone();
2531 assert_eq!(A.array, B.array);
2532 }
2533 }
2534
2535 #[test]
2536 fn test_le_be_bytes() {
2537 let le_bytes = [1, 2, 3, 4];
2538 let be_bytes = [4, 3, 2, 1];
2539 let u8_ver = FixedUInt::<u8, 4>::from_le_bytes(&le_bytes);
2540 let u16_ver = FixedUInt::<u16, 2>::from_le_bytes(&le_bytes);
2541 let u32_ver = FixedUInt::<u32, 1>::from_le_bytes(&le_bytes);
2542 let u8_ver_be = FixedUInt::<u8, 4>::from_be_bytes(&be_bytes);
2543 let u16_ver_be = FixedUInt::<u16, 2>::from_be_bytes(&be_bytes);
2544 let u32_ver_be = FixedUInt::<u32, 1>::from_be_bytes(&be_bytes);
2545
2546 assert_eq!(u8_ver.array, [1, 2, 3, 4]);
2547 assert_eq!(u16_ver.array, [0x0201, 0x0403]);
2548 assert_eq!(u32_ver.array, [0x04030201]);
2549 assert_eq!(u8_ver_be.array, [1, 2, 3, 4]);
2550 assert_eq!(u16_ver_be.array, [0x0201, 0x0403]);
2551 assert_eq!(u32_ver_be.array, [0x04030201]);
2552
2553 let mut output_buffer = [0u8; 16];
2554 assert_eq!(u8_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
2555 assert_eq!(u8_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
2556 assert_eq!(u16_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
2557 assert_eq!(u16_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
2558 assert_eq!(u32_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
2559 assert_eq!(u32_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
2560 }
2561
2562 #[test]
2564 fn test_div_small() {
2565 type TestInt = FixedUInt<u8, 2>;
2566
2567 let test_cases = [
2569 (20u16, 3u16, 6u16), (100u16, 7u16, 14u16), (255u16, 5u16, 51u16), (65535u16, 256u16, 255u16), ];
2574
2575 for (dividend_val, divisor_val, expected) in test_cases {
2576 let dividend = TestInt::from(dividend_val);
2577 let divisor = TestInt::from(divisor_val);
2578 let expected_result = TestInt::from(expected);
2579
2580 assert_eq!(
2581 dividend / divisor,
2582 expected_result,
2583 "Division failed for {} / {} = {}",
2584 dividend_val,
2585 divisor_val,
2586 expected
2587 );
2588 }
2589 }
2590
2591 #[test]
2592 fn test_div_edge_cases() {
2593 type TestInt = FixedUInt<u16, 2>;
2594
2595 let dividend = TestInt::from(1000u16);
2597 let divisor = TestInt::from(1u16);
2598 assert_eq!(dividend / divisor, TestInt::from(1000u16));
2599
2600 let dividend = TestInt::from(42u16);
2602 let divisor = TestInt::from(42u16);
2603 assert_eq!(dividend / divisor, TestInt::from(1u16));
2604
2605 let dividend = TestInt::from(5u16);
2607 let divisor = TestInt::from(10u16);
2608 assert_eq!(dividend / divisor, TestInt::from(0u16));
2609
2610 let dividend = TestInt::from(1024u16);
2612 let divisor = TestInt::from(4u16);
2613 assert_eq!(dividend / divisor, TestInt::from(256u16));
2614 }
2615
2616 #[test]
2617 fn test_helper_methods() {
2618 type TestInt = FixedUInt<u8, 2>;
2619
2620 let mut val = <TestInt as Zero>::zero();
2622 const_set_bit(&mut val.array, 0);
2623 assert_eq!(val, TestInt::from(1u8));
2624
2625 const_set_bit(&mut val.array, 8);
2626 assert_eq!(val, TestInt::from(257u16)); let a = TestInt::from(8u8); let b = TestInt::from(1u8); assert_eq!(
2634 const_cmp_shifted(&a.array, &b.array, 3),
2635 core::cmp::Ordering::Equal
2636 );
2637
2638 assert_eq!(
2640 const_cmp_shifted(&a.array, &b.array, 2),
2641 core::cmp::Ordering::Greater
2642 );
2643
2644 assert_eq!(
2646 const_cmp_shifted(&a.array, &b.array, 4),
2647 core::cmp::Ordering::Less
2648 );
2649
2650 let mut val = TestInt::from(10u8);
2652 let one = TestInt::from(1u8);
2653 const_sub_shifted(&mut val.array, &one.array, 2); assert_eq!(val, TestInt::from(6u8)); }
2656
2657 #[test]
2658 fn test_shifted_operations_comprehensive() {
2659 type TestInt = FixedUInt<u32, 2>;
2660
2661 let a = TestInt::from(0x12345678u32);
2663 let b = TestInt::from(0x12345678u32);
2664
2665 assert_eq!(
2667 const_cmp_shifted(&a.array, &b.array, 0),
2668 core::cmp::Ordering::Equal
2669 );
2670
2671 let c = TestInt::from(0x123u32); let d = TestInt::from(0x48d159e2u32); assert_eq!(
2677 const_cmp_shifted(&d.array, &c.array, 16),
2678 core::cmp::Ordering::Greater
2679 );
2680
2681 let e = TestInt::from(1u32);
2683 let zero = TestInt::from(0u32);
2684 assert_eq!(
2685 const_cmp_shifted(&e.array, &zero.array, 100),
2686 core::cmp::Ordering::Greater
2687 );
2688 assert_eq!(
2690 const_cmp_shifted(&zero.array, &e.array, 100),
2691 core::cmp::Ordering::Equal
2692 );
2693
2694 let mut val = TestInt::from(0x10000u32); let one = TestInt::from(1u32);
2697 const_sub_shifted(&mut val.array, &one.array, 15); assert_eq!(val, TestInt::from(0x8000u32)); let mut big_val = TestInt::from(0x100000000u64); const_sub_shifted(&mut big_val.array, &one.array, 31); assert_eq!(big_val, TestInt::from(0x80000000u64)); }
2705
2706 #[test]
2707 fn test_shifted_operations_edge_cases() {
2708 type TestInt = FixedUInt<u32, 2>;
2709
2710 let a = TestInt::from(42u32);
2712 let a2 = TestInt::from(42u32);
2713 assert_eq!(
2714 const_cmp_shifted(&a.array, &a2.array, 0),
2715 core::cmp::Ordering::Equal
2716 );
2717
2718 let mut b = TestInt::from(42u32);
2719 let ten = TestInt::from(10u32);
2720 const_sub_shifted(&mut b.array, &ten.array, 0);
2721 assert_eq!(b, TestInt::from(32u32));
2722
2723 let c = TestInt::from(123u32);
2725 let large = TestInt::from(456u32);
2726 assert_eq!(
2727 const_cmp_shifted(&c.array, &large.array, 200),
2728 core::cmp::Ordering::Greater
2729 );
2730
2731 let mut d = TestInt::from(123u32);
2732 const_sub_shifted(&mut d.array, &large.array, 200); assert_eq!(d, TestInt::from(123u32));
2734
2735 let zero = TestInt::from(0u32);
2737 let one = TestInt::from(1u32);
2738 assert_eq!(
2739 const_cmp_shifted(&zero.array, &zero.array, 10),
2740 core::cmp::Ordering::Equal
2741 );
2742 assert_eq!(
2743 const_cmp_shifted(&one.array, &zero.array, 10),
2744 core::cmp::Ordering::Greater
2745 );
2746 }
2747
2748 #[test]
2749 fn test_shifted_operations_equivalence() {
2750 type TestInt = FixedUInt<u32, 2>;
2751
2752 let test_cases = [
2754 (0x12345u32, 0x678u32, 4),
2755 (0x1000u32, 0x10u32, 8),
2756 (0xABCDu32, 0x1u32, 16),
2757 (0x80000000u32, 0x1u32, 1),
2758 ];
2759
2760 for (a_val, b_val, shift) in test_cases {
2761 let a = TestInt::from(a_val);
2762 let b = TestInt::from(b_val);
2763
2764 let optimized_cmp = const_cmp_shifted(&a.array, &b.array, shift);
2766 let naive_cmp = a.cmp(&(b << shift));
2767 assert_eq!(
2768 optimized_cmp, naive_cmp,
2769 "cmp_shifted mismatch: {} vs ({} << {})",
2770 a_val, b_val, shift
2771 );
2772
2773 if a >= (b << shift) {
2775 let mut optimized_result = a;
2776 const_sub_shifted(&mut optimized_result.array, &b.array, shift);
2777
2778 let naive_result = a - (b << shift);
2779 assert_eq!(
2780 optimized_result, naive_result,
2781 "sub_shifted mismatch: {} - ({} << {})",
2782 a_val, b_val, shift
2783 );
2784 }
2785 }
2786 }
2787
2788 #[test]
2789 fn test_div_assign_in_place_optimization() {
2790 type TestInt = FixedUInt<u32, 2>;
2791
2792 let test_cases = [
2794 (100u32, 10u32, 10u32, 0u32), (123u32, 7u32, 17u32, 4u32), (1000u32, 13u32, 76u32, 12u32), (65535u32, 255u32, 257u32, 0u32), ];
2799
2800 for (dividend_val, divisor_val, expected_quotient, expected_remainder) in test_cases {
2801 let mut dividend = TestInt::from(dividend_val);
2803 let divisor = TestInt::from(divisor_val);
2804
2805 dividend /= divisor;
2806 assert_eq!(
2807 dividend,
2808 TestInt::from(expected_quotient),
2809 "div_assign: {} / {} should be {}",
2810 dividend_val,
2811 divisor_val,
2812 expected_quotient
2813 );
2814
2815 let dividend2 = TestInt::from(dividend_val);
2817 let (quotient, remainder) = dividend2.div_rem(&divisor);
2818 assert_eq!(
2819 quotient,
2820 TestInt::from(expected_quotient),
2821 "div_rem quotient: {} / {} should be {}",
2822 dividend_val,
2823 divisor_val,
2824 expected_quotient
2825 );
2826 assert_eq!(
2827 remainder,
2828 TestInt::from(expected_remainder),
2829 "div_rem remainder: {} % {} should be {}",
2830 dividend_val,
2831 divisor_val,
2832 expected_remainder
2833 );
2834
2835 assert_eq!(
2837 quotient * divisor + remainder,
2838 TestInt::from(dividend_val),
2839 "Property check failed for {}",
2840 dividend_val
2841 );
2842 }
2843 }
2844
2845 #[test]
2846 fn test_div_assign_stack_efficiency() {
2847 type TestInt = FixedUInt<u32, 4>; let mut dividend = TestInt::from(0x123456789ABCDEFu64);
2851 let divisor = TestInt::from(0x12345u32);
2852 let original_dividend = dividend;
2853
2854 dividend /= divisor;
2856
2857 let remainder = original_dividend % divisor;
2859 assert_eq!(dividend * divisor + remainder, original_dividend);
2860 }
2861
2862 #[test]
2863 fn test_rem_assign_optimization() {
2864 type TestInt = FixedUInt<u32, 2>;
2865
2866 let test_cases = [
2867 (100u32, 10u32, 0u32), (123u32, 7u32, 4u32), (1000u32, 13u32, 12u32), (65535u32, 255u32, 0u32), ];
2872
2873 for (dividend_val, divisor_val, expected_remainder) in test_cases {
2874 let mut dividend = TestInt::from(dividend_val);
2875 let divisor = TestInt::from(divisor_val);
2876
2877 dividend %= divisor;
2878 assert_eq!(
2879 dividend,
2880 TestInt::from(expected_remainder),
2881 "rem_assign: {} % {} should be {}",
2882 dividend_val,
2883 divisor_val,
2884 expected_remainder
2885 );
2886 }
2887 }
2888
2889 #[test]
2890 fn test_div_with_remainder_property() {
2891 type TestInt = FixedUInt<u32, 2>;
2892
2893 let test_cases = [
2895 (100u32, 10u32, 10u32), (123u32, 7u32, 17u32), (1000u32, 13u32, 76u32), (65535u32, 255u32, 257u32), ];
2900
2901 for (dividend_val, divisor_val, expected_quotient) in test_cases {
2902 let dividend = TestInt::from(dividend_val);
2903 let divisor = TestInt::from(divisor_val);
2904
2905 let quotient = dividend / divisor;
2907 assert_eq!(
2908 quotient,
2909 TestInt::from(expected_quotient),
2910 "Division: {} / {} should be {}",
2911 dividend_val,
2912 divisor_val,
2913 expected_quotient
2914 );
2915
2916 let remainder = dividend % divisor;
2918 assert_eq!(
2919 quotient * divisor + remainder,
2920 dividend,
2921 "Division property check failed for {}",
2922 dividend_val
2923 );
2924 }
2925 }
2926
2927 #[test]
2928 fn test_code_simplification_benefits() {
2929 type TestInt = FixedUInt<u32, 2>;
2930
2931 let dividend = TestInt::from(12345u32);
2933 let divisor = TestInt::from(67u32);
2934 let quotient = dividend / divisor;
2935 let remainder = dividend % divisor;
2936
2937 assert_eq!(quotient * divisor + remainder, dividend);
2939 }
2940
2941 #[test]
2942 fn test_rem_assign_correctness_after_fix() {
2943 type TestInt = FixedUInt<u32, 2>;
2944
2945 let mut a = TestInt::from(17u32);
2947 let b = TestInt::from(5u32);
2948
2949 a %= b;
2952 assert_eq!(a, TestInt::from(2u32), "17 % 5 should be 2");
2953
2954 let mut test_val = TestInt::from(100u32);
2956 test_val %= TestInt::from(7u32);
2957 assert_eq!(
2958 test_val,
2959 TestInt::from(2u32),
2960 "100 % 7 should be 2 (not 14, the quotient)"
2961 );
2962 }
2963
2964 #[test]
2965 fn test_div_property_based() {
2966 type TestInt = FixedUInt<u16, 2>;
2967
2968 let test_pairs = [
2970 (12345u16, 67u16),
2971 (1000u16, 13u16),
2972 (65535u16, 255u16),
2973 (5000u16, 7u16),
2974 ];
2975
2976 for (dividend_val, divisor_val) in test_pairs {
2977 let dividend = TestInt::from(dividend_val);
2978 let divisor = TestInt::from(divisor_val);
2979
2980 let quotient = dividend / divisor;
2981
2982 let remainder = dividend - (quotient * divisor);
2984 let reconstructed = quotient * divisor + remainder;
2985
2986 assert_eq!(
2987 reconstructed,
2988 dividend,
2989 "Property failed for {} / {}: {} * {} + {} != {}",
2990 dividend_val,
2991 divisor_val,
2992 quotient.to_u32().unwrap_or(0),
2993 divisor_val,
2994 remainder.to_u32().unwrap_or(0),
2995 dividend_val
2996 );
2997
2998 assert!(
3000 remainder < divisor,
3001 "Remainder {} >= divisor {} for {} / {}",
3002 remainder.to_u32().unwrap_or(0),
3003 divisor_val,
3004 dividend_val,
3005 divisor_val
3006 );
3007 }
3008 }
3009}