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
70pub use has_nonzero_impl::NonZeroFixedUInt;
71
72use const_num_traits::{Ct, Nct, Personality, PersonalityMarker, PersonalityTag};
73#[cfg(feature = "zeroize")]
74use zeroize::DefaultIsZeroes;
75
76#[derive(Copy)]
86pub struct FixedUInt<T, const N: usize, P: Personality = Nct>
87where
88 T: MachineWord,
89{
90 pub(super) array: [T; N],
92 pub(super) _p: PersonalityMarker<P>,
94}
95
96impl<T: MachineWord + core::fmt::Debug, const N: usize> core::fmt::Debug for FixedUInt<T, N, Nct> {
101 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
102 f.debug_struct("FixedUInt")
103 .field("array", &self.array)
104 .finish()
105 }
106}
107
108impl<T: MachineWord, const N: usize> core::fmt::Debug for FixedUInt<T, N, Ct> {
109 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
110 f.write_str("FixedUInt<…>")
111 }
112}
113
114#[cfg(feature = "zeroize")]
115impl<T: MachineWord, const N: usize, P: Personality> DefaultIsZeroes for FixedUInt<T, N, P> {}
116
117impl<T, const N: usize, P: Personality> From<[T; N]> for FixedUInt<T, N, P>
118where
119 T: MachineWord,
120{
121 fn from(array: [T; N]) -> Self {
122 Self {
123 array,
124 _p: core::marker::PhantomData,
125 }
126 }
127}
128
129impl<T: MachineWord, const N: usize, P: Personality> FixedUInt<T, N, P> {
132 pub(crate) const fn from_array(array: [T; N]) -> Self {
133 Self {
134 array,
135 _p: core::marker::PhantomData,
136 }
137 }
138}
139
140impl<T: MachineWord, const N: usize> From<FixedUInt<T, N, Nct>> for FixedUInt<T, N, Ct> {
148 fn from(v: FixedUInt<T, N, Nct>) -> Self {
149 FixedUInt::from_array(v.array)
150 }
151}
152
153impl<T: MachineWord, const N: usize> FixedUInt<T, N, Ct> {
154 pub const fn forget_ct(self) -> FixedUInt<T, N, Nct> {
161 FixedUInt::from_array(self.array)
162 }
163}
164
165#[inline]
170fn ct_checked_shift_valid(bits: u32, bit_size: usize) -> subtle::Choice {
171 if bit_size == 0 {
172 return subtle::Choice::from(0);
175 }
176 let bit_size_u32 = bit_size as u32;
177 let diff = bit_size_u32.wrapping_sub(1).wrapping_sub(bits);
180 let overflow = ((diff >> 31) & 1) as u8;
181 subtle::Choice::from(1 ^ overflow)
182}
183
184impl<T: MachineWord, const N: usize> FixedUInt<T, N, Ct> {
185 pub fn ct_checked_add(&self, other: &Self) -> subtle::CtOption<Self> {
191 let (res, overflow) = <&Self as OverflowingAdd>::overflowing_add(self, other);
195 let valid = subtle::Choice::from((!overflow) as u8);
196 subtle::CtOption::new(res, valid)
197 }
198
199 pub fn ct_checked_sub(&self, other: &Self) -> subtle::CtOption<Self> {
201 let (res, overflow) = <&Self as OverflowingSub>::overflowing_sub(self, other);
202 let valid = subtle::Choice::from((!overflow) as u8);
203 subtle::CtOption::new(res, valid)
204 }
205
206 pub fn ct_checked_mul(&self, other: &Self) -> subtle::CtOption<Self> {
208 let (res, overflow) = <&Self as OverflowingMul>::overflowing_mul(self, other);
209 let valid = subtle::Choice::from((!overflow) as u8);
210 subtle::CtOption::new(res, valid)
211 }
212
213 pub fn ct_checked_shl(&self, bits: u32) -> subtle::CtOption<Self> {
222 subtle::CtOption::new(
228 bit_ops_impl::const_unbounded_shl_u32::<T, N, Ct>(Self::from_array(self.array), bits),
229 ct_checked_shift_valid(bits, Self::BIT_SIZE),
230 )
231 }
232
233 pub fn ct_checked_shr(&self, bits: u32) -> subtle::CtOption<Self> {
238 subtle::CtOption::new(
239 bit_ops_impl::const_unbounded_shr_u32::<T, N, Ct>(Self::from_array(self.array), bits),
240 ct_checked_shift_valid(bits, Self::BIT_SIZE),
241 )
242 }
243
244 pub fn ct_checked_pow(self, exp: u32) -> subtle::CtOption<Self> {
245 let mut result = <Self as One>::one();
246 let mut base = self;
247 let mut e = exp;
248 let mut any_overflow: u8 = 0;
249 for _ in 0..u32::BITS {
250 let bit = core::hint::black_box((e & 1) as u8);
254 let (candidate, mul_ov) = <Self as OverflowingMul>::overflowing_mul(result, base);
255 any_overflow |= (mul_ov as u8) & bit;
257 let bit_t = <T as core::convert::From<u8>>::from(bit);
259 let mask = core::hint::black_box(bit_t * <T as Bounded>::max_value());
260 for i in 0..N {
261 let diff = result.array[i] ^ candidate.array[i];
262 result.array[i] ^= mask & diff;
263 }
264 e >>= 1;
265 let (new_base, base_ov) = <Self as OverflowingMul>::overflowing_mul(base, base);
266 let any_remaining: u8 = core::hint::black_box((e != 0) as u8);
268 any_overflow |= (base_ov as u8) & any_remaining;
269 base = new_base;
270 }
271 let valid = subtle::Choice::from(1u8 ^ any_overflow);
272 subtle::CtOption::new(result, valid)
273 }
274}
275
276impl<T: MachineWord + subtle::ConditionallySelectable, const N: usize> FixedUInt<T, N, Ct> {
283 pub fn ct_checked_next_power_of_two(self) -> subtle::CtOption<Self>
285 where
286 T: subtle::ConstantTimeEq,
287 {
288 let one = <Self as One>::one();
289 let m_one = <Self as const_num_traits::WrappingSub>::wrapping_sub(self, one);
290 let leading = <Self as PrimBits>::leading_zeros(m_one);
291 let bits = Self::BIT_SIZE as u32 - leading;
292 let shifted = one << (bits as usize);
293 let is_zero_choice =
294 <Self as subtle::ConstantTimeEq>::ct_eq(&self, &<Self as Zero>::zero());
295 let result = <Self as subtle::ConditionallySelectable>::conditional_select(
297 &shifted,
298 &one,
299 is_zero_choice,
300 );
301 let overflow = (bits >= Self::BIT_SIZE as u32) as u8;
304 let valid_otherwise = subtle::Choice::from(1u8 ^ overflow);
305 let valid = <subtle::Choice as subtle::ConditionallySelectable>::conditional_select(
306 &valid_otherwise,
307 &subtle::Choice::from(1u8),
308 is_zero_choice,
309 );
310 subtle::CtOption::new(result, valid)
311 }
312}
313
314impl<T: MachineWord + subtle::ConstantTimeEq, const N: usize> subtle::ConstantTimeEq
319 for FixedUInt<T, N, Ct>
320{
321 fn ct_eq(&self, other: &Self) -> subtle::Choice {
322 <[T] as subtle::ConstantTimeEq>::ct_eq(self.array.as_slice(), other.array.as_slice())
323 }
324}
325
326impl<T: MachineWord + subtle::ConditionallySelectable, const N: usize>
327 subtle::ConditionallySelectable for FixedUInt<T, N, Ct>
328{
329 fn conditional_select(a: &Self, b: &Self, choice: subtle::Choice) -> Self {
330 let mut array = a.array;
331 let mut i = 0;
332 while i < N {
333 array[i] = T::conditional_select(&a.array[i], &b.array[i], choice);
334 i += 1;
335 }
336 FixedUInt::from_array(array)
337 }
338}
339
340impl<T: MachineWord + subtle::ConstantTimeEq + subtle::ConstantTimeGreater, const N: usize>
347 subtle::ConstantTimeGreater for FixedUInt<T, N, Ct>
348{
349 fn ct_gt(&self, other: &Self) -> subtle::Choice {
350 let mut gt = subtle::Choice::from(0u8);
351 let mut undecided = subtle::Choice::from(1u8);
352 let mut i = N;
353 while i > 0 {
354 i -= 1;
355 let gt_here = self.array[i].ct_gt(&other.array[i]);
356 let eq_here = self.array[i].ct_eq(&other.array[i]);
357 gt |= undecided & gt_here;
358 undecided &= eq_here;
359 }
360 gt
361 }
362}
363
364impl<T: MachineWord + subtle::ConstantTimeEq + subtle::ConstantTimeGreater, const N: usize>
365 subtle::ConstantTimeLess for FixedUInt<T, N, Ct>
366{
367}
368
369const LONGEST_WORD_IN_BITS: usize = 128;
370
371impl<T: MachineWord, const N: usize, P: Personality> FixedUInt<T, N, P> {
372 const WORD_SIZE: usize = core::mem::size_of::<T>();
373 const WORD_BITS: usize = Self::WORD_SIZE * 8;
374 const BYTE_SIZE: usize = Self::WORD_SIZE * N;
375 const BIT_SIZE: usize = Self::BYTE_SIZE * 8;
376
377 pub const BYTE_WIDTH: usize = Self::BYTE_SIZE;
384
385 pub fn new() -> FixedUInt<T, N, P> {
387 FixedUInt::from_array([T::zero(); N])
388 }
389
390 pub fn words(&self) -> &[T; N] {
392 &self.array
393 }
394
395 pub fn bit_length(&self) -> u32 {
397 Self::BIT_SIZE as u32 - const_leading_zeros(&self.array)
401 }
402}
403
404impl<T: MachineWord, const N: usize> FixedUInt<T, N, Nct> {
405 pub fn div_rem(&self, divisor: &Self) -> (Self, Self) {
407 let (quotient, remainder) = const_div_rem(&self.array, &divisor.array);
408 (Self::from_array(quotient), Self::from_array(remainder))
409 }
410
411 pub fn to_radix_str<'a>(
413 &self,
414 result: &'a mut [u8],
415 radix: u8,
416 ) -> Result<&'a str, core::fmt::Error> {
417 type Error = core::fmt::Error;
418
419 if !(2..=16).contains(&radix) {
420 return Err(Error {}); }
422 for byte in result.iter_mut() {
423 *byte = b'0';
424 }
425 if <Self as Zero>::is_zero(self) {
426 if !result.is_empty() {
427 result[0] = b'0';
428 return core::str::from_utf8(&result[0..1]).map_err(|_| Error {});
429 } else {
430 return Err(Error {});
431 }
432 }
433
434 let mut number = *self;
435 let mut idx = result.len();
436
437 let radix_t = Self::from(radix);
438
439 while !<Self as Zero>::is_zero(&number) {
440 if idx == 0 {
441 return Err(Error {}); }
443
444 idx -= 1;
445 let (quotient, remainder) = number.div_rem(&radix_t);
446
447 let digit =
451 <T as const_num_traits::ToPrimitive>::to_u8(&remainder.array[0]).unwrap_or(0);
452 result[idx] = match digit {
453 0..=9 => b'0' + digit, 10..=16 => b'a' + (digit - 10), _ => return Err(Error {}),
456 };
457
458 number = quotient;
459 }
460
461 let start = result[idx..].iter().position(|&c| c != b'0').unwrap_or(0);
462 let radix_str = core::str::from_utf8(&result[idx + start..]).map_err(|_| Error {})?;
463 Ok(radix_str)
464 }
465}
466
467c0nst::c0nst! {
469 pub(crate) c0nst fn impl_from_le_bytes_slice<T: [c0nst] ConstMachineWord, const N: usize>(
472 bytes: &[u8],
473 ) -> [T; N] {
474 let word_size = core::mem::size_of::<T>();
475 let mut ret: [T; N] = [T::zero(); N];
476 let capacity = N * word_size;
477 let total_bytes = if bytes.len() < capacity { bytes.len() } else { capacity };
478
479 let mut byte_index = 0;
480 while byte_index < total_bytes {
481 let word_index = byte_index / word_size;
482 let byte_in_word = byte_index % word_size;
483
484 let byte_value: T = <T as core::convert::From<u8>>::from(bytes[byte_index]);
485 let shifted_value = byte_value.shl(byte_in_word * 8);
486 ret[word_index] = ret[word_index].bitor(shifted_value);
487 byte_index += 1;
488 }
489 ret
490 }
491
492 pub(crate) c0nst fn impl_from_be_bytes_slice<T: [c0nst] ConstMachineWord, const N: usize>(
495 bytes: &[u8],
496 ) -> [T; N] {
497 let word_size = core::mem::size_of::<T>();
498 let mut ret: [T; N] = [T::zero(); N];
499 let capacity_bytes = N * word_size;
500 let total_bytes = if bytes.len() < capacity_bytes { bytes.len() } else { capacity_bytes };
501
502 let start_offset = if bytes.len() > capacity_bytes {
505 bytes.len() - capacity_bytes
506 } else {
507 0
508 };
509
510 let mut byte_index = 0;
511 while byte_index < total_bytes {
512 let be_byte_index = start_offset + total_bytes - 1 - byte_index;
514 let word_index = byte_index / word_size;
515 let byte_in_word = byte_index % word_size;
516
517 let byte_value: T = <T as core::convert::From<u8>>::from(bytes[be_byte_index]);
518 let shifted_value = byte_value.shl(byte_in_word * 8);
519 ret[word_index] = ret[word_index].bitor(shifted_value);
520 byte_index += 1;
521 }
522 ret
523 }
524}
525
526impl<T: MachineWord, const N: usize, P: Personality> FixedUInt<T, N, P> {
528 pub fn from_le_bytes(bytes: &[u8]) -> Self {
530 Self::from_array(impl_from_le_bytes_slice::<T, N>(bytes))
531 }
532
533 pub fn from_be_bytes(bytes: &[u8]) -> Self {
535 Self::from_array(impl_from_be_bytes_slice::<T, N>(bytes))
536 }
537}
538
539impl<T: MachineWord, const N: usize, P: Personality> FixedUInt<T, N, P> {
540 pub fn to_le_bytes<'a>(&self, output_buffer: &'a mut [u8]) -> Result<&'a [u8], bool> {
542 let total_bytes = N * Self::WORD_SIZE;
543 if output_buffer.len() < total_bytes {
544 return Err(false); }
546 for (i, word) in self.array.iter().enumerate() {
547 let start = i * Self::WORD_SIZE;
548 let end = start + Self::WORD_SIZE;
549 let word_bytes = word.to_le_bytes();
550 output_buffer[start..end].copy_from_slice(word_bytes.as_ref());
551 }
552 Ok(&output_buffer[..total_bytes])
553 }
554
555 pub fn to_be_bytes<'a>(&self, output_buffer: &'a mut [u8]) -> Result<&'a [u8], bool> {
557 let total_bytes = N * Self::WORD_SIZE;
558 if output_buffer.len() < total_bytes {
559 return Err(false); }
561 for (i, word) in self.array.iter().rev().enumerate() {
562 let start = i * Self::WORD_SIZE;
563 let end = start + Self::WORD_SIZE;
564 let word_bytes = word.to_be_bytes();
565 output_buffer[start..end].copy_from_slice(word_bytes.as_ref());
566 }
567 Ok(&output_buffer[..total_bytes])
568 }
569
570 pub fn to_hex_str<'a>(&self, result: &'a mut [u8]) -> Result<&'a str, core::fmt::Error> {
572 type Error = core::fmt::Error;
573
574 let word_size = Self::WORD_SIZE;
575 let need_bits = self.bit_length() as usize;
577 let need_chars = if need_bits > 0 { need_bits / 4 } else { 0 };
579
580 if result.len() < need_chars {
581 return Err(Error {});
583 }
584 let offset = result.len() - need_chars;
585 for i in result.iter_mut() {
586 *i = b'0';
587 }
588
589 for iter_words in 0..self.array.len() {
590 let word = self.array[iter_words];
591 let mut encoded = [0u8; LONGEST_WORD_IN_BITS / 4];
592 let encode_slice = &mut encoded[0..word_size * 2];
593 let mut wordbytes = word.to_le_bytes();
594 wordbytes.as_mut().reverse();
595 let wordslice = wordbytes.as_ref();
596 to_slice_hex(wordslice, encode_slice).map_err(|_| Error {})?;
597 for iter_chars in 0..encode_slice.len() {
598 let copy_char_to = (iter_words * word_size * 2) + iter_chars;
599 if copy_char_to <= need_chars {
600 let reverse_index = offset + (need_chars - copy_char_to);
601 if reverse_index <= result.len() && reverse_index > 0 {
602 let current_char = encode_slice[(encode_slice.len() - 1) - iter_chars];
603 result[reverse_index - 1] = current_char;
604 }
605 }
606 }
607 }
608
609 let convert = core::str::from_utf8(result).map_err(|_| Error {})?;
610 let pos = convert.find(|c: char| c != '0');
611 match pos {
612 Some(x) => Ok(&convert[x..convert.len()]),
613 None => {
614 if convert.starts_with('0') {
615 Ok("0")
616 } else {
617 Ok(convert)
618 }
619 }
620 }
621 }
622
623 #[must_use]
628 pub fn resize<const N2: usize>(&self) -> FixedUInt<T, N2, P> {
629 let mut array = [T::zero(); N2];
630 let min_size = N.min(N2);
631 array[..min_size].copy_from_slice(&self.array[..min_size]);
632 FixedUInt::<T, N2, P>::from_array(array)
633 }
634
635 #[cfg(feature = "num-traits")]
636 fn hex_fmt(
637 &self,
638 formatter: &mut core::fmt::Formatter<'_>,
639 uppercase: bool,
640 ) -> Result<(), core::fmt::Error>
641 where
642 u8: core::convert::TryFrom<T>,
643 {
644 type Err = core::fmt::Error;
645
646 fn to_casedigit(byte: u8, uppercase: bool) -> Result<char, core::fmt::Error> {
647 let digit = core::char::from_digit(byte as u32, 16).ok_or(Err {})?;
648 if uppercase {
649 digit.to_uppercase().next().ok_or(Err {})
650 } else {
651 digit.to_lowercase().next().ok_or(Err {})
652 }
653 }
654
655 let mut leading_zero: bool = true;
656
657 let mut maybe_write = |nibble: char| -> Result<(), core::fmt::Error> {
658 leading_zero &= nibble == '0';
659 if !leading_zero {
660 formatter.write_char(nibble)?;
661 }
662 Ok(())
663 };
664
665 for index in (0..N).rev() {
666 let val = self.array[index];
667 let mask: T = 0xff.into();
668 for j in (0..Self::WORD_SIZE as u32).rev() {
669 let masked = val & mask.shl((j * 8) as usize);
670
671 let byte = u8::try_from(masked.shr((j * 8) as usize)).map_err(|_| Err {})?;
672
673 maybe_write(to_casedigit((byte & 0xf0) >> 4, uppercase)?)?;
674 maybe_write(to_casedigit(byte & 0x0f, uppercase)?)?;
675 }
676 }
677 Ok(())
678 }
679}
680
681c0nst::c0nst! {
682 pub(crate) c0nst fn add_with_carry<T: [c0nst] ConstMachineWord, const N: usize>(
687 a: &[T; N],
688 b: &[T; N],
689 carry_in: bool,
690 ) -> ([T; N], bool) {
691 let mut result = [T::zero(); N];
692 let mut carry = carry_in;
693 let mut i = 0usize;
694 while i < N {
695 let (sum, c) = CarryingAdd::carrying_add(a[i], b[i], carry);
696 result[i] = sum;
697 carry = c;
698 i += 1;
699 }
700 (result, carry)
701 }
702
703 pub(crate) c0nst fn sub_with_borrow<T: [c0nst] ConstMachineWord, const N: usize>(
705 a: &[T; N],
706 b: &[T; N],
707 borrow_in: bool,
708 ) -> ([T; N], bool) {
709 let mut result = [T::zero(); N];
710 let mut borrow = borrow_in;
711 let mut i = 0usize;
712 while i < N {
713 let (diff, br) = BorrowingSub::borrowing_sub(a[i], b[i], borrow);
714 result[i] = diff;
715 borrow = br;
716 i += 1;
717 }
718 (result, borrow)
719 }
720
721 pub(crate) c0nst fn add_impl<T: [c0nst] ConstMachineWord, const N: usize>(
726 target: &mut [T; N],
727 other: &[T; N]
728 ) -> bool {
729 let mut carry = false;
730 let mut i = 0usize;
731 while i < N {
732 let (sum, c) = CarryingAdd::carrying_add(target[i], other[i], carry);
733 target[i] = sum;
734 carry = c;
735 i += 1;
736 }
737 carry
738 }
739
740 pub(crate) c0nst fn sub_impl<T: [c0nst] ConstMachineWord, const N: usize>(
742 target: &mut [T; N],
743 other: &[T; N]
744 ) -> bool {
745 let mut borrow = false;
746 let mut i = 0usize;
747 while i < N {
748 let (diff, br) = BorrowingSub::borrowing_sub(target[i], other[i], borrow);
749 target[i] = diff;
750 borrow = br;
751 i += 1;
752 }
753 borrow
754 }
755}
756
757c0nst::c0nst! {
758 pub(crate) c0nst fn const_shl_impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
760 target: &mut FixedUInt<T, N, P>,
761 bits: usize,
762 ) {
763 if N == 0 {
764 return;
765 }
766 let word_bits = FixedUInt::<T, N>::WORD_BITS;
767 let nwords = bits / word_bits;
768 let nbits = bits - nwords * word_bits;
769
770 if nwords >= N {
772 let mut i = 0;
773 while i < N {
774 target.array[i] = T::zero();
775 i += 1;
776 }
777 return;
778 }
779
780 let mut i = N;
782 while i > nwords {
783 i -= 1;
784 target.array[i] = target.array[i - nwords];
785 }
786 let mut i = 0;
788 while i < nwords {
789 target.array[i] = T::zero();
790 i += 1;
791 }
792
793 if nbits != 0 {
794 let mut i = N;
796 while i > 1 {
797 i -= 1;
798 let right = target.array[i] << nbits;
799 let left = target.array[i - 1] >> (word_bits - nbits);
800 target.array[i] = right | left;
801 }
802 target.array[0] <<= nbits;
803 }
804 }
805
806 pub(crate) c0nst fn const_shr_impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
808 target: &mut FixedUInt<T, N, P>,
809 bits: usize,
810 ) {
811 if N == 0 {
812 return;
813 }
814 let word_bits = FixedUInt::<T, N>::WORD_BITS;
815 let nwords = bits / word_bits;
816 let nbits = bits - nwords * word_bits;
817
818 if nwords >= N {
820 let mut i = 0;
821 while i < N {
822 target.array[i] = T::zero();
823 i += 1;
824 }
825 return;
826 }
827
828 let last_index = N - 1;
829 let last_word = N - nwords;
830
831 let mut i = 0;
833 while i < last_word {
834 target.array[i] = target.array[i + nwords];
835 i += 1;
836 }
837
838 let mut i = last_word;
840 while i < N {
841 target.array[i] = T::zero();
842 i += 1;
843 }
844
845 if nbits != 0 {
846 let mut i = 0;
848 while i < last_index {
849 let left = target.array[i] >> nbits;
850 let right = target.array[i + 1] << (word_bits - nbits);
851 target.array[i] = left | right;
852 i += 1;
853 }
854 target.array[last_index] >>= nbits;
855 }
856 }
857
858 pub(crate) c0nst fn const_shl_ct<
867 T: [c0nst] ConstMachineWord + MachineWord,
868 const N: usize,
869 P: Personality,
870 >(
871 target: &mut FixedUInt<T, N, P>,
872 bits: usize,
873 ) {
874 if N == 0 {
875 return;
876 }
877 let layers = core::mem::size_of::<usize>() * 8;
880 let mut k = 0;
881 while k < layers {
882 let amount = 1usize << k;
883 let mut shifted = *target;
885 const_shl_impl(&mut shifted, amount);
886 let bit_k = core::hint::black_box(((bits >> k) & 1) as u8);
890 let bit_k_t = <T as core::convert::From<u8>>::from(bit_k);
891 let mask = <T as core::ops::Mul>::mul(bit_k_t, <T as Bounded>::max_value());
892 let mut i = 0;
894 while i < N {
895 let diff =
896 <T as core::ops::BitXor>::bitxor(target.array[i], shifted.array[i]);
897 let masked = <T as core::ops::BitAnd>::bitand(mask, diff);
898 target.array[i] = <T as core::ops::BitXor>::bitxor(target.array[i], masked);
899 i += 1;
900 }
901 k += 1;
902 }
903 }
904
905 pub(crate) c0nst fn const_shr_ct<
908 T: [c0nst] ConstMachineWord + MachineWord,
909 const N: usize,
910 P: Personality,
911 >(
912 target: &mut FixedUInt<T, N, P>,
913 bits: usize,
914 ) {
915 if N == 0 {
916 return;
917 }
918 let layers = core::mem::size_of::<usize>() * 8;
921 let mut k = 0;
922 while k < layers {
923 let amount = 1usize << k;
924 let mut shifted = *target;
925 const_shr_impl(&mut shifted, amount);
926 let bit_k = core::hint::black_box(((bits >> k) & 1) as u8);
928 let bit_k_t = <T as core::convert::From<u8>>::from(bit_k);
929 let mask = <T as core::ops::Mul>::mul(bit_k_t, <T as Bounded>::max_value());
930 let mut i = 0;
931 while i < N {
932 let diff =
933 <T as core::ops::BitXor>::bitxor(target.array[i], shifted.array[i]);
934 let masked = <T as core::ops::BitAnd>::bitand(mask, diff);
935 target.array[i] = <T as core::ops::BitXor>::bitxor(target.array[i], masked);
936 i += 1;
937 }
938 k += 1;
939 }
940 }
941
942 pub(crate) c0nst fn const_mul<T: [c0nst] ConstMachineWord, const N: usize, const CHECK_OVERFLOW: bool, P: Personality>(
952 op1: &[T; N],
953 op2: &[T; N],
954 word_bits: usize,
955 ) -> ([T; N], bool) {
956 let mut result: [T; N] = [<T as ConstZero>::ZERO; N];
957 let mut overflowed = false;
958 let t_max = <T as ConstMachineWord>::to_double(<T as Bounded>::max_value());
959 let dw_zero = <<T as ConstMachineWord>::ConstDoubleWord as ConstZero>::ZERO;
960
961 let mut i = 0;
962 while i < N {
963 let mut carry = dw_zero;
964 let mut j = 0;
965 while j < N {
966 let round = i + j;
967 let op1_dw = <T as ConstMachineWord>::to_double(op1[i]);
968 let op2_dw = <T as ConstMachineWord>::to_double(op2[j]);
969 let mul_res = op1_dw * op2_dw;
970 let mut accumulator = if round < N {
971 <T as ConstMachineWord>::to_double(result[round])
972 } else {
973 dw_zero
974 };
975 accumulator += mul_res + carry;
976
977 match P::TAG {
978 PersonalityTag::Nct => {
979 if accumulator > t_max {
980 carry = accumulator >> word_bits;
981 accumulator &= t_max;
982 } else {
983 carry = dw_zero;
984 }
985 }
986 PersonalityTag::Ct => {
987 carry = accumulator >> word_bits;
988 accumulator &= t_max;
989 }
990 }
991 if round < N {
992 result[round] = <T as ConstMachineWord>::from_double(accumulator);
993 } else if CHECK_OVERFLOW {
994 overflowed |= accumulator != dw_zero;
995 }
996 j += 1;
997 }
998 if CHECK_OVERFLOW {
999 overflowed |= carry != dw_zero;
1000 }
1001 i += 1;
1002 }
1003 (result, overflowed)
1004 }
1005
1006 pub(crate) c0nst fn const_word_bits<T>() -> usize {
1008 core::mem::size_of::<T>() * 8
1009 }
1010
1011 pub(crate) c0nst fn const_cmp_words<T: [c0nst] ConstMachineWord>(a: T, b: T) -> Option<core::cmp::Ordering> {
1013 if a > b {
1014 Some(core::cmp::Ordering::Greater)
1015 } else if a < b {
1016 Some(core::cmp::Ordering::Less)
1017 } else {
1018 None
1019 }
1020 }
1021
1022 pub(crate) c0nst fn const_leading_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1024 array: &[T; N],
1025 ) -> u32 {
1026 let mut ret = 0u32;
1027 let mut index = N;
1028 while index > 0 {
1029 index -= 1;
1030 let v = array[index];
1031 ret += <T as PrimBits>::leading_zeros(v);
1032 if !<T as Zero>::is_zero(&v) {
1033 break;
1034 }
1035 }
1036 ret
1037 }
1038
1039 pub(crate) c0nst fn const_leading_zeros_ct<T: [c0nst] ConstMachineWord, const N: usize>(
1046 array: &[T; N],
1047 ) -> u32 {
1048 let mut total: u32 = 0;
1049 let mut decided: u32 = 0;
1051 let mut index = N;
1052 while index > 0 {
1053 index -= 1;
1054 let v = array[index];
1055 let v_lz = <T as PrimBits>::leading_zeros(v);
1056 let undecided = core::hint::black_box(!decided);
1060 total += undecided & v_lz;
1061 let v_nz_bit = (!<T as Zero>::is_zero(&v)) as u32;
1063 let v_nz_mask = core::hint::black_box(v_nz_bit.wrapping_neg());
1064 decided |= v_nz_mask;
1065 }
1066 total
1067 }
1068
1069 pub(crate) c0nst fn const_trailing_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1071 array: &[T; N],
1072 ) -> u32 {
1073 let mut ret = 0u32;
1074 let mut index = 0;
1075 while index < N {
1076 let v = array[index];
1077 ret += <T as PrimBits>::trailing_zeros(v);
1078 if !<T as Zero>::is_zero(&v) {
1079 break;
1080 }
1081 index += 1;
1082 }
1083 ret
1084 }
1085
1086 pub(crate) c0nst fn const_trailing_zeros_ct<T: [c0nst] ConstMachineWord, const N: usize>(
1091 array: &[T; N],
1092 ) -> u32 {
1093 let mut total: u32 = 0;
1094 let mut decided: u32 = 0;
1096 let mut index = 0;
1097 while index < N {
1098 let v = array[index];
1099 let v_tz = <T as PrimBits>::trailing_zeros(v);
1100 let undecided = core::hint::black_box(!decided);
1103 total += undecided & v_tz;
1104 let v_nz_bit = (!<T as Zero>::is_zero(&v)) as u32;
1105 let v_nz_mask = core::hint::black_box(v_nz_bit.wrapping_neg());
1106 decided |= v_nz_mask;
1107 index += 1;
1108 }
1109 total
1110 }
1111
1112 pub(crate) c0nst fn const_bit_length<T: [c0nst] ConstMachineWord, const N: usize>(
1114 array: &[T; N],
1115 ) -> usize {
1116 let word_bits = const_word_bits::<T>();
1117 let bit_size = N * word_bits;
1118 bit_size - const_leading_zeros::<T, N>(array) as usize
1119 }
1120
1121 pub(crate) c0nst fn const_is_zero<T: [c0nst] ConstMachineWord, const N: usize>(
1123 array: &[T; N],
1124 ) -> bool {
1125 let mut index = 0;
1126 while index < N {
1127 if !<T as Zero>::is_zero(&array[index]) {
1128 return false;
1129 }
1130 index += 1;
1131 }
1132 true
1133 }
1134
1135 pub(crate) c0nst fn const_is_zero_ct<T: [c0nst] ConstMachineWord, const N: usize>(
1140 array: &[T; N],
1141 ) -> bool {
1142 let mut acc = <T as ConstZero>::ZERO;
1143 let mut index = 0;
1144 while index < N {
1145 acc = <T as core::ops::BitOr>::bitor(acc, array[index]);
1146 index += 1;
1147 }
1148 <T as Zero>::is_zero(&acc)
1149 }
1150
1151 pub(crate) c0nst fn const_is_one<T: [c0nst] ConstMachineWord, const N: usize>(
1156 array: &[T; N],
1157 ) -> bool {
1158 if N == 0 || !array[0].is_one() {
1159 return false;
1160 }
1161 let mut i = 1;
1162 while i < N {
1163 if !<T as Zero>::is_zero(&array[i]) {
1164 return false;
1165 }
1166 i += 1;
1167 }
1168 true
1169 }
1170
1171 pub(crate) c0nst fn const_is_one_ct<T: [c0nst] ConstMachineWord, const N: usize>(
1176 array: &[T; N],
1177 ) -> bool {
1178 if N == 0 {
1179 return false;
1180 }
1181 let mut acc = <T as core::ops::BitXor>::bitxor(array[0], <T as ConstOne>::ONE);
1182 let mut index = 1;
1183 while index < N {
1184 acc = <T as core::ops::BitOr>::bitor(acc, array[index]);
1185 index += 1;
1186 }
1187 <T as Zero>::is_zero(&acc)
1188 }
1189
1190 pub(crate) c0nst fn const_set_bit<T: [c0nst] ConstMachineWord, const N: usize>(
1196 array: &mut [T; N],
1197 pos: usize,
1198 ) {
1199 let word_bits = const_word_bits::<T>();
1200 let word_idx = pos / word_bits;
1201 if word_idx >= N {
1202 return;
1203 }
1204 let bit_idx = pos % word_bits;
1205 array[word_idx] |= <T as ConstOne>::ONE << bit_idx;
1206 }
1207
1208 pub(crate) c0nst fn const_cmp<T: [c0nst] ConstMachineWord, const N: usize>(
1213 a: &[T; N],
1214 b: &[T; N],
1215 ) -> core::cmp::Ordering {
1216 let mut index = N;
1217 while index > 0 {
1218 index -= 1;
1219 if let Some(ord) = const_cmp_words(a[index], b[index]) {
1220 return ord;
1221 }
1222 }
1223 core::cmp::Ordering::Equal
1224 }
1225
1226 pub(crate) c0nst fn const_cmp_ct<T: [c0nst] ConstMachineWord, const N: usize>(
1231 a: &[T; N],
1232 b: &[T; N],
1233 ) -> core::cmp::Ordering {
1234 let mut result: u8 = 0;
1236 let mut decided: u8 = 0;
1238 let mut index = N;
1239 while index > 0 {
1240 index -= 1;
1241 let gt = (a[index] > b[index]) as u8;
1242 let lt = (a[index] < b[index]) as u8;
1243 let here = (gt << 1) | lt;
1245 let undecided_mask = core::hint::black_box(!decided);
1247 result |= undecided_mask & here;
1248 let here_nz_mask = core::hint::black_box(((here != 0) as u8).wrapping_neg());
1250 decided |= here_nz_mask;
1251 }
1252 match result {
1253 2 => core::cmp::Ordering::Greater,
1254 1 => core::cmp::Ordering::Less,
1255 _ => core::cmp::Ordering::Equal,
1256 }
1257 }
1258
1259 pub(crate) c0nst fn const_get_shifted_word<T: [c0nst] ConstMachineWord, const N: usize>(
1264 array: &[T; N],
1265 word_idx: usize,
1266 word_shift: usize,
1267 bit_shift: usize,
1268 ) -> T {
1269 let word_bits = const_word_bits::<T>();
1270
1271 if bit_shift >= word_bits {
1273 return <T as ConstZero>::ZERO;
1274 }
1275
1276 if word_idx < word_shift {
1277 return <T as ConstZero>::ZERO;
1278 }
1279
1280 let source_idx = word_idx - word_shift;
1281
1282 if bit_shift == 0 {
1283 if source_idx < N {
1284 array[source_idx]
1285 } else {
1286 <T as ConstZero>::ZERO
1287 }
1288 } else {
1289 let mut result = <T as ConstZero>::ZERO;
1290
1291 if source_idx < N {
1293 result |= array[source_idx] << bit_shift;
1294 }
1295
1296 if source_idx > 0 && source_idx - 1 < N {
1298 let high_bits = array[source_idx - 1] >> (word_bits - bit_shift);
1299 result |= high_bits;
1300 }
1301
1302 result
1303 }
1304 }
1305
1306 pub(crate) c0nst fn const_cmp_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
1311 array: &[T; N],
1312 other: &[T; N],
1313 shift_bits: usize,
1314 ) -> core::cmp::Ordering {
1315 let word_bits = const_word_bits::<T>();
1316
1317 if shift_bits == 0 {
1318 return const_cmp::<T, N>(array, other);
1319 }
1320
1321 let word_shift = shift_bits / word_bits;
1322 if word_shift >= N {
1323 if const_is_zero::<T, N>(array) {
1325 return core::cmp::Ordering::Equal;
1326 } else {
1327 return core::cmp::Ordering::Greater;
1328 }
1329 }
1330
1331 let bit_shift = shift_bits % word_bits;
1332
1333 let mut index = N;
1335 while index > 0 {
1336 index -= 1;
1337 let self_word = array[index];
1338 let other_shifted_word = const_get_shifted_word::<T, N>(
1339 other, index, word_shift, bit_shift
1340 );
1341
1342 if let Some(ord) = const_cmp_words(self_word, other_shifted_word) {
1343 return ord;
1344 }
1345 }
1346
1347 core::cmp::Ordering::Equal
1348 }
1349
1350 pub(crate) c0nst fn const_sub_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
1355 array: &mut [T; N],
1356 other: &[T; N],
1357 shift_bits: usize,
1358 ) {
1359 let word_bits = const_word_bits::<T>();
1360
1361 if shift_bits == 0 {
1362 sub_impl::<T, N>(array, other);
1363 return;
1364 }
1365
1366 let word_shift = shift_bits / word_bits;
1367 if word_shift >= N {
1368 return;
1369 }
1370
1371 let bit_shift = shift_bits % word_bits;
1372 let mut borrow = T::zero();
1373 let mut index = 0;
1374 while index < N {
1375 let other_word = const_get_shifted_word::<T, N>(other, index, word_shift, bit_shift);
1376 let (res, borrow1) = array[index].overflowing_sub(other_word);
1377 let (res, borrow2) = res.overflowing_sub(borrow);
1378 borrow = if borrow1 || borrow2 { T::one() } else { T::zero() };
1379 array[index] = res;
1380 index += 1;
1381 }
1382 }
1383
1384 pub(crate) c0nst fn const_div<T: [c0nst] ConstMachineWord, const N: usize>(
1388 dividend: &mut [T; N],
1389 divisor: &[T; N],
1390 ) -> [T; N] {
1391 use core::cmp::Ordering;
1392
1393 match const_cmp::<T, N>(dividend, divisor) {
1394 Ordering::Less => {
1396 let remainder = *dividend;
1397 let mut i = 0;
1398 while i < N {
1399 dividend[i] = <T as ConstZero>::ZERO;
1400 i += 1;
1401 }
1402 return remainder;
1403 }
1404 Ordering::Equal => {
1406 let mut i = 0;
1407 while i < N {
1408 dividend[i] = <T as ConstZero>::ZERO;
1409 i += 1;
1410 }
1411 if N > 0 {
1412 dividend[0] = <T as ConstOne>::ONE;
1413 }
1414 return [<T as ConstZero>::ZERO; N];
1415 }
1416 Ordering::Greater => {}
1417 }
1418
1419 let mut quotient = [<T as ConstZero>::ZERO; N];
1420
1421 let dividend_bits = const_bit_length::<T, N>(dividend);
1423 let divisor_bits = const_bit_length::<T, N>(divisor);
1424
1425 let mut bit_pos = if dividend_bits >= divisor_bits {
1426 dividend_bits - divisor_bits
1427 } else {
1428 0
1429 };
1430
1431 while bit_pos > 0 {
1433 let cmp = const_cmp_shifted::<T, N>(dividend, divisor, bit_pos);
1434 if !matches!(cmp, Ordering::Less) {
1435 break;
1436 }
1437 bit_pos -= 1;
1438 }
1439
1440 loop {
1442 let cmp = const_cmp_shifted::<T, N>(dividend, divisor, bit_pos);
1443 if !matches!(cmp, Ordering::Less) {
1444 const_sub_shifted::<T, N>(dividend, divisor, bit_pos);
1445 const_set_bit::<T, N>(&mut quotient, bit_pos);
1446 }
1447
1448 if bit_pos == 0 {
1449 break;
1450 }
1451 bit_pos -= 1;
1452 }
1453
1454 let remainder = *dividend;
1455 *dividend = quotient;
1456 remainder
1457 }
1458
1459 pub(crate) c0nst fn const_div_rem<T: [c0nst] ConstMachineWord, const N: usize>(
1463 dividend: &[T; N],
1464 divisor: &[T; N],
1465 ) -> ([T; N], [T; N]) {
1466 if const_is_zero(divisor) {
1467 maybe_panic(PanicReason::DivByZero)
1468 }
1469 let mut quotient = *dividend;
1470 let remainder = const_div(&mut quotient, divisor);
1471 (quotient, remainder)
1472 }
1473}
1474
1475c0nst::c0nst! {
1476 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> Default for FixedUInt<T, N, P> {
1477 fn default() -> Self {
1478 FixedUInt::from_array([<T as ConstZero>::ZERO; N])
1479 }
1480 }
1481
1482 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> Clone for FixedUInt<T, N, P> {
1483 fn clone(&self) -> Self {
1484 *self
1485 }
1486 }
1487}
1488
1489#[cfg(feature = "num-traits")]
1492impl<T: MachineWord, const N: usize> num_traits::Unsigned for FixedUInt<T, N, Nct> {}
1493
1494c0nst::c0nst! {
1497 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::cmp::PartialEq for FixedUInt<T, N, P> {
1498 fn eq(&self, other: &Self) -> bool {
1505 match P::TAG {
1506 PersonalityTag::Nct => self.array == other.array,
1507 PersonalityTag::Ct => {
1508 let mut diff = <T as ConstZero>::ZERO;
1509 let mut i = 0;
1510 while i < N {
1511 let x = <T as core::ops::BitXor>::bitxor(self.array[i], other.array[i]);
1512 diff = <T as core::ops::BitOr>::bitor(diff, x);
1513 i += 1;
1514 }
1515 <T as Zero>::is_zero(&diff)
1516 }
1517 }
1518 }
1519 }
1520
1521 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::cmp::Eq for FixedUInt<T, N, P> {}
1522
1523 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::cmp::Ord for FixedUInt<T, N, P> {
1524 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
1525 match P::TAG {
1526 PersonalityTag::Nct => const_cmp(&self.array, &other.array),
1527 PersonalityTag::Ct => const_cmp_ct(&self.array, &other.array),
1528 }
1529 }
1530 }
1531
1532 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::cmp::PartialOrd for FixedUInt<T, N, P> {
1533 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
1534 Some(self.cmp(other))
1535 }
1536 }
1537}
1538
1539c0nst::c0nst! {
1544 c0nst fn const_from_le_bytes<T: [c0nst] ConstMachineWord, const N: usize, const B: usize>(
1547 bytes: [u8; B],
1548 ) -> [T; N] {
1549 impl_from_le_bytes_slice::<T, N>(&bytes)
1550 }
1551
1552 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::convert::From<u8> for FixedUInt<T, N, P> {
1553 fn from(x: u8) -> Self {
1554 Self::from_array(const_from_le_bytes(x.to_le_bytes()))
1555 }
1556 }
1557
1558 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::convert::From<u16> for FixedUInt<T, N, P> {
1559 fn from(x: u16) -> Self {
1560 Self::from_array(const_from_le_bytes(x.to_le_bytes()))
1561 }
1562 }
1563
1564 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::convert::From<u32> for FixedUInt<T, N, P> {
1565 fn from(x: u32) -> Self {
1566 Self::from_array(const_from_le_bytes(x.to_le_bytes()))
1567 }
1568 }
1569
1570 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::convert::From<u64> for FixedUInt<T, N, P> {
1571 fn from(x: u64) -> Self {
1572 Self::from_array(const_from_le_bytes(x.to_le_bytes()))
1573 }
1574 }
1575}
1576
1577fn make_parse_int_err() -> core::num::ParseIntError {
1584 <u8>::from_str_radix("-", 2).err().unwrap()
1585}
1586#[cfg(feature = "num-traits")]
1587fn make_overflow_err() -> core::num::ParseIntError {
1588 <u8>::from_str_radix("101", 16).err().unwrap()
1589}
1590#[cfg(feature = "num-traits")]
1591fn make_empty_error() -> core::num::ParseIntError {
1592 <u8>::from_str_radix("", 8).err().unwrap()
1593}
1594
1595fn to_slice_hex<T: AsRef<[u8]>>(
1596 input: T,
1597 output: &mut [u8],
1598) -> Result<(), core::num::ParseIntError> {
1599 fn from_digit(byte: u8) -> Option<char> {
1600 core::char::from_digit(byte as u32, 16)
1601 }
1602 let r = input.as_ref();
1603 if r.len() * 2 != output.len() {
1604 return Err(make_parse_int_err());
1605 }
1606 for i in 0..r.len() {
1607 let byte = r[i];
1608 output[i * 2] = from_digit((byte & 0xf0) >> 4).ok_or_else(make_parse_int_err)? as u8;
1609 output[i * 2 + 1] = from_digit(byte & 0x0f).ok_or_else(make_parse_int_err)? as u8;
1610 }
1611
1612 Ok(())
1613}
1614
1615pub(super) enum PanicReason {
1616 Add,
1617 Sub,
1618 Mul,
1619 DivByZero,
1620}
1621
1622c0nst::c0nst! {
1623 pub(super) c0nst fn maybe_panic(r: PanicReason) {
1624 match r {
1625 PanicReason::Add => panic!("attempt to add with overflow"),
1626 PanicReason::Sub => panic!("attempt to subtract with overflow"),
1627 PanicReason::Mul => panic!("attempt to multiply with overflow"),
1628 PanicReason::DivByZero => panic!("attempt to divide by zero"),
1629 }
1630 }
1631
1632 pub(crate) c0nst fn const_ct_select<
1645 T: [c0nst] ConstMachineWord + MachineWord,
1646 const N: usize,
1647 P: Personality,
1648 >(
1649 if_zero: FixedUInt<T, N, P>,
1650 if_one: FixedUInt<T, N, P>,
1651 choice: u8,
1652 ) -> FixedUInt<T, N, P> {
1653 let choice = core::hint::black_box(choice);
1654 let bit_t = <T as core::convert::From<u8>>::from(choice);
1655 let mask = <T as core::ops::Mul>::mul(bit_t, <T as Bounded>::max_value());
1656 let mut result = if_zero;
1657 let mut i = 0;
1658 while i < N {
1659 let diff = <T as core::ops::BitXor>::bitxor(if_zero.array[i], if_one.array[i]);
1660 let masked = <T as core::ops::BitAnd>::bitand(mask, diff);
1661 result.array[i] = <T as core::ops::BitXor>::bitxor(if_zero.array[i], masked);
1662 i += 1;
1663 }
1664 result
1665 }
1666
1667 pub(super) c0nst fn maybe_panic_if<P: Personality>(
1668 overflow: bool,
1669 reason: PanicReason,
1670 ) {
1671 match P::TAG {
1672 PersonalityTag::Nct => {
1673 if overflow {
1674 maybe_panic(reason);
1675 }
1676 }
1677 PersonalityTag::Ct => {
1678 let _ = overflow;
1679 let _ = reason;
1680 }
1681 }
1682 }
1683}
1684
1685#[cfg(test)]
1688#[cfg(feature = "num-traits")]
1689mod tests {
1690 use super::FixedUInt as Bn;
1691 use super::*;
1692 use const_num_traits::{One, Zero};
1693 use num_traits::{FromPrimitive, Num, ToPrimitive};
1694
1695 type Bn8 = Bn<u8, 8>;
1696 type Bn16 = Bn<u16, 4>;
1697 type Bn32 = Bn<u32, 2>;
1698
1699 c0nst::c0nst! {
1700 pub c0nst fn test_add<T: [c0nst] ConstMachineWord, const N: usize>(
1701 a: &mut [T; N],
1702 b: &[T; N]
1703 ) -> bool {
1704 add_impl(a, b)
1705 }
1706
1707 pub c0nst fn test_sub<T: [c0nst] ConstMachineWord, const N: usize>(
1708 a: &mut [T; N],
1709 b: &[T; N]
1710 ) -> bool {
1711 sub_impl(a, b)
1712 }
1713
1714 pub c0nst fn test_mul<T: [c0nst] ConstMachineWord, const N: usize>(
1715 a: &[T; N],
1716 b: &[T; N],
1717 word_bits: usize,
1718 ) -> ([T; N], bool) {
1719 const_mul::<T, N, true, const_num_traits::Nct>(a, b, word_bits)
1720 }
1721
1722 pub c0nst fn arr_leading_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1723 a: &[T; N],
1724 ) -> u32 {
1725 const_leading_zeros::<T, N>(a)
1726 }
1727
1728 pub c0nst fn arr_trailing_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1729 a: &[T; N],
1730 ) -> u32 {
1731 const_trailing_zeros::<T, N>(a)
1732 }
1733
1734 pub c0nst fn arr_bit_length<T: [c0nst] ConstMachineWord, const N: usize>(
1735 a: &[T; N],
1736 ) -> usize {
1737 const_bit_length::<T, N>(a)
1738 }
1739
1740 pub c0nst fn arr_is_zero<T: [c0nst] ConstMachineWord, const N: usize>(
1741 a: &[T; N],
1742 ) -> bool {
1743 const_is_zero::<T, N>(a)
1744 }
1745
1746 pub c0nst fn arr_set_bit<T: [c0nst] ConstMachineWord, const N: usize>(
1747 a: &mut [T; N],
1748 pos: usize,
1749 ) {
1750 const_set_bit::<T, N>(a, pos)
1751 }
1752
1753 pub c0nst fn arr_cmp<T: [c0nst] ConstMachineWord, const N: usize>(
1754 a: &[T; N],
1755 b: &[T; N],
1756 ) -> core::cmp::Ordering {
1757 const_cmp::<T, N>(a, b)
1758 }
1759
1760 pub c0nst fn arr_cmp_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
1761 a: &[T; N],
1762 b: &[T; N],
1763 shift_bits: usize,
1764 ) -> core::cmp::Ordering {
1765 const_cmp_shifted::<T, N>(a, b, shift_bits)
1766 }
1767
1768 pub c0nst fn arr_get_shifted_word<T: [c0nst] ConstMachineWord, const N: usize>(
1769 a: &[T; N],
1770 word_idx: usize,
1771 word_shift: usize,
1772 bit_shift: usize,
1773 ) -> T {
1774 const_get_shifted_word::<T, N>(a, word_idx, word_shift, bit_shift)
1775 }
1776 }
1777
1778 #[test]
1779 fn test_const_add_impl() {
1780 let mut a: [u8; 4] = [1, 0, 0, 0];
1782 let b: [u8; 4] = [2, 0, 0, 0];
1783 let overflow = test_add(&mut a, &b);
1784 assert_eq!(a, [3, 0, 0, 0]);
1785 assert!(!overflow);
1786
1787 let mut a: [u8; 4] = [255, 0, 0, 0];
1789 let b: [u8; 4] = [1, 0, 0, 0];
1790 let overflow = test_add(&mut a, &b);
1791 assert_eq!(a, [0, 1, 0, 0]);
1792 assert!(!overflow);
1793
1794 let mut a: [u8; 4] = [255, 255, 255, 255];
1796 let b: [u8; 4] = [1, 0, 0, 0];
1797 let overflow = test_add(&mut a, &b);
1798 assert_eq!(a, [0, 0, 0, 0]);
1799 assert!(overflow);
1800
1801 let mut a: [u32; 2] = [0xFFFFFFFF, 0];
1803 let b: [u32; 2] = [1, 0];
1804 let overflow = test_add(&mut a, &b);
1805 assert_eq!(a, [0, 1]);
1806 assert!(!overflow);
1807
1808 #[cfg(feature = "nightly")]
1809 {
1810 const ADD_RESULT: ([u8; 4], bool) = {
1811 let mut a = [1u8, 0, 0, 0];
1812 let b = [2u8, 0, 0, 0];
1813 let overflow = test_add(&mut a, &b);
1814 (a, overflow)
1815 };
1816 assert_eq!(ADD_RESULT, ([3, 0, 0, 0], false));
1817 }
1818 }
1819
1820 #[test]
1821 fn test_const_sub_impl() {
1822 let mut a: [u8; 4] = [3, 0, 0, 0];
1824 let b: [u8; 4] = [1, 0, 0, 0];
1825 let overflow = test_sub(&mut a, &b);
1826 assert_eq!(a, [2, 0, 0, 0]);
1827 assert!(!overflow);
1828
1829 let mut a: [u8; 4] = [0, 1, 0, 0];
1831 let b: [u8; 4] = [1, 0, 0, 0];
1832 let overflow = test_sub(&mut a, &b);
1833 assert_eq!(a, [255, 0, 0, 0]);
1834 assert!(!overflow);
1835
1836 let mut a: [u8; 4] = [0, 0, 0, 0];
1838 let b: [u8; 4] = [1, 0, 0, 0];
1839 let overflow = test_sub(&mut a, &b);
1840 assert_eq!(a, [255, 255, 255, 255]);
1841 assert!(overflow);
1842
1843 let mut a: [u32; 2] = [0, 1];
1845 let b: [u32; 2] = [1, 0];
1846 let overflow = test_sub(&mut a, &b);
1847 assert_eq!(a, [0xFFFFFFFF, 0]);
1848 assert!(!overflow);
1849
1850 #[cfg(feature = "nightly")]
1851 {
1852 const SUB_RESULT: ([u8; 4], bool) = {
1853 let mut a = [3u8, 0, 0, 0];
1854 let b = [1u8, 0, 0, 0];
1855 let overflow = test_sub(&mut a, &b);
1856 (a, overflow)
1857 };
1858 assert_eq!(SUB_RESULT, ([2, 0, 0, 0], false));
1859 }
1860 }
1861
1862 #[test]
1863 fn test_const_mul_impl() {
1864 let a: [u8; 2] = [3, 0];
1866 let b: [u8; 2] = [4, 0];
1867 let (result, overflow) = test_mul(&a, &b, 8);
1868 assert_eq!(result, [12, 0]);
1869 assert!(!overflow);
1870
1871 let a: [u8; 2] = [200, 0];
1873 let b: [u8; 2] = [2, 0];
1874 let (result, overflow) = test_mul(&a, &b, 8);
1875 assert_eq!(result, [0x90, 0x01]);
1876 assert!(!overflow);
1877
1878 let a: [u8; 2] = [0, 1]; let b: [u8; 2] = [0, 1]; let (_result, overflow) = test_mul(&a, &b, 8);
1882 assert!(overflow);
1883
1884 let a: [u8; 3] = [0, 0, 1];
1888 let b: [u8; 3] = [0, 0, 1];
1889 let (_result, overflow) = test_mul(&a, &b, 8);
1890 assert!(overflow, "N=3 high-position overflow not detected");
1891
1892 let a: [u8; 3] = [0, 0, 2];
1896 let b: [u8; 3] = [0, 0, 2];
1897 let (_result, overflow) = test_mul(&a, &b, 8);
1898 assert!(
1899 overflow,
1900 "N=3 high-position overflow with larger values not detected"
1901 );
1902
1903 let a: [u8; 3] = [0, 1, 0];
1907 let b: [u8; 3] = [0, 1, 0];
1908 let (result, overflow) = test_mul(&a, &b, 8);
1909 assert_eq!(result, [0, 0, 1]);
1910 assert!(
1911 !overflow,
1912 "N=3 non-overflow incorrectly detected as overflow"
1913 );
1914
1915 let a: [u8; 3] = [255, 0, 0];
1919 let b: [u8; 3] = [255, 0, 0];
1920 let (result, overflow) = test_mul(&a, &b, 8);
1921 assert_eq!(result, [0x01, 0xFE, 0x00]);
1922 assert!(!overflow);
1923
1924 #[cfg(feature = "nightly")]
1925 {
1926 const MUL_RESULT: ([u8; 2], bool) = test_mul(&[3u8, 0], &[4u8, 0], 8);
1927 assert_eq!(MUL_RESULT, ([12, 0], false));
1928 }
1929 }
1930
1931 #[test]
1932 fn test_const_helpers() {
1933 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]));
1958 assert!(!arr_is_zero(&[1u8, 0, 0, 0]));
1959 assert!(!arr_is_zero(&[0u8, 0, 0, 1]));
1960 assert!(!arr_is_zero(&[0u8, 1, 0, 0]));
1961
1962 let mut arr: [u8; 4] = [0, 0, 0, 0];
1964 arr_set_bit(&mut arr, 0);
1965 assert_eq!(arr, [1, 0, 0, 0]);
1966
1967 let mut arr: [u8; 4] = [0, 0, 0, 0];
1968 arr_set_bit(&mut arr, 8);
1969 assert_eq!(arr, [0, 1, 0, 0]);
1970
1971 let mut arr: [u8; 4] = [0, 0, 0, 0];
1972 arr_set_bit(&mut arr, 31);
1973 assert_eq!(arr, [0, 0, 0, 0x80]);
1974
1975 let mut arr: [u8; 4] = [0, 0, 0, 0];
1977 arr_set_bit(&mut arr, 0);
1978 arr_set_bit(&mut arr, 3);
1979 arr_set_bit(&mut arr, 8);
1980 assert_eq!(arr, [0b00001001, 1, 0, 0]);
1981
1982 let mut arr: [u8; 4] = [0, 0, 0, 0];
1984 arr_set_bit(&mut arr, 32);
1985 assert_eq!(arr, [0, 0, 0, 0]);
1986
1987 assert_eq!(arr_leading_zeros(&[0u32, 0]), 64);
1989 assert_eq!(arr_leading_zeros(&[1u32, 0]), 63);
1990 assert_eq!(arr_leading_zeros(&[0u32, 1]), 31);
1991 assert_eq!(arr_trailing_zeros(&[0u32, 0]), 64);
1992 assert_eq!(arr_trailing_zeros(&[0u32, 1]), 32);
1993 assert_eq!(arr_bit_length(&[0u32, 0]), 0);
1994 assert_eq!(arr_bit_length(&[1u32, 0]), 1);
1995 assert_eq!(arr_bit_length(&[0u32, 1]), 33);
1996
1997 #[cfg(feature = "nightly")]
1998 {
1999 const LEADING: u32 = arr_leading_zeros(&[0u8, 0, 1, 0]);
2000 assert_eq!(LEADING, 15);
2001
2002 const TRAILING: u32 = arr_trailing_zeros(&[0u8, 0, 1, 0]);
2003 assert_eq!(TRAILING, 16);
2004
2005 const BIT_LEN: usize = arr_bit_length(&[0u8, 0, 1, 0]);
2006 assert_eq!(BIT_LEN, 17);
2007
2008 const IS_ZERO: bool = arr_is_zero(&[0u8, 0, 0, 0]);
2009 assert!(IS_ZERO);
2010
2011 const NOT_ZERO: bool = arr_is_zero(&[0u8, 1, 0, 0]);
2012 assert!(!NOT_ZERO);
2013
2014 const SET_BIT_RESULT: [u8; 4] = {
2015 let mut arr = [0u8, 0, 0, 0];
2016 arr_set_bit(&mut arr, 10);
2017 arr
2018 };
2019 assert_eq!(SET_BIT_RESULT, [0, 0b00000100, 0, 0]);
2020 }
2021 }
2022
2023 #[test]
2024 fn test_const_cmp() {
2025 use core::cmp::Ordering;
2026
2027 assert_eq!(arr_cmp(&[1u8, 2, 3, 4], &[1u8, 2, 3, 4]), Ordering::Equal);
2029 assert_eq!(arr_cmp(&[0u8, 0, 0, 0], &[0u8, 0, 0, 0]), Ordering::Equal);
2030
2031 assert_eq!(arr_cmp(&[0u8, 0, 0, 2], &[0u8, 0, 0, 1]), Ordering::Greater);
2033
2034 assert_eq!(arr_cmp(&[0u8, 0, 0, 1], &[0u8, 0, 0, 2]), Ordering::Less);
2036
2037 assert_eq!(arr_cmp(&[2u8, 0, 0, 0], &[1u8, 0, 0, 0]), Ordering::Greater);
2039
2040 assert_eq!(arr_cmp(&[1u8, 0, 0, 0], &[2u8, 0, 0, 0]), Ordering::Less);
2042
2043 assert_eq!(arr_cmp(&[0u32, 1], &[0u32, 1]), Ordering::Equal);
2045 assert_eq!(arr_cmp(&[0u32, 2], &[0u32, 1]), Ordering::Greater);
2046 assert_eq!(arr_cmp(&[0u32, 1], &[0u32, 2]), Ordering::Less);
2047
2048 #[cfg(feature = "nightly")]
2049 {
2050 const CMP_EQ: Ordering = arr_cmp(&[1u8, 2, 3, 4], &[1u8, 2, 3, 4]);
2051 const CMP_GT: Ordering = arr_cmp(&[0u8, 0, 0, 2], &[0u8, 0, 0, 1]);
2052 const CMP_LT: Ordering = arr_cmp(&[0u8, 0, 0, 1], &[0u8, 0, 0, 2]);
2053 assert_eq!(CMP_EQ, Ordering::Equal);
2054 assert_eq!(CMP_GT, Ordering::Greater);
2055 assert_eq!(CMP_LT, Ordering::Less);
2056 }
2057 }
2058
2059 #[test]
2060 fn test_const_cmp_shifted() {
2061 use core::cmp::Ordering;
2062
2063 assert_eq!(
2065 arr_cmp_shifted(&[1u8, 0, 0, 0], &[1u8, 0, 0, 0], 0),
2066 Ordering::Equal
2067 );
2068
2069 assert_eq!(
2071 arr_cmp_shifted(&[0u8, 1, 0, 0], &[1u8, 0, 0, 0], 8),
2072 Ordering::Equal
2073 );
2074
2075 assert_eq!(
2077 arr_cmp_shifted(&[0u8, 2, 0, 0], &[1u8, 0, 0, 0], 8),
2078 Ordering::Greater
2079 );
2080
2081 assert_eq!(
2083 arr_cmp_shifted(&[0u8, 0, 0, 0], &[1u8, 0, 0, 0], 8),
2084 Ordering::Less
2085 );
2086
2087 assert_eq!(
2090 arr_cmp_shifted(&[1u8, 0, 0, 0], &[1u8, 0, 0, 0], 32),
2091 Ordering::Greater
2092 );
2093
2094 assert_eq!(
2096 arr_cmp_shifted(&[0u8, 0, 0, 0], &[255u8, 255, 255, 255], 32),
2097 Ordering::Equal
2098 );
2099
2100 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 0, 1, 0), 0);
2104 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 1, 1, 0), 1);
2105 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 2, 1, 0), 2);
2106
2107 assert_eq!(arr_get_shifted_word(&[0x0Fu8, 0xF0, 0, 0], 0, 0, 4), 0xF0);
2111 assert_eq!(arr_get_shifted_word(&[0x0Fu8, 0xF0, 0, 0], 1, 0, 4), 0x00);
2113
2114 assert_eq!(arr_get_shifted_word(&[0xFFu8, 0x00, 0, 0], 0, 0, 4), 0xF0);
2117 assert_eq!(arr_get_shifted_word(&[0xFFu8, 0x00, 0, 0], 1, 0, 4), 0x0F);
2119
2120 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 0, 1, 4), 0);
2124 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 1, 1, 4), 0xB0);
2126 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 2, 1, 4), 0xDA);
2128
2129 #[cfg(feature = "nightly")]
2130 {
2131 const CMP_SHIFTED_EQ: Ordering = arr_cmp_shifted(&[0u8, 1, 0, 0], &[1u8, 0, 0, 0], 8);
2132 const CMP_SHIFTED_GT: Ordering = arr_cmp_shifted(&[0u8, 2, 0, 0], &[1u8, 0, 0, 0], 8);
2133 assert_eq!(CMP_SHIFTED_EQ, Ordering::Equal);
2134 assert_eq!(CMP_SHIFTED_GT, Ordering::Greater);
2135 }
2136 }
2137
2138 #[test]
2139 fn test_core_convert_u8() {
2140 let f = Bn::<u8, 1>::from(1u8);
2141 assert_eq!(f.array, [1]);
2142 let f = Bn::<u8, 2>::from(1u8);
2143 assert_eq!(f.array, [1, 0]);
2144
2145 let f = Bn::<u16, 1>::from(1u8);
2146 assert_eq!(f.array, [1]);
2147 let f = Bn::<u16, 2>::from(1u8);
2148 assert_eq!(f.array, [1, 0]);
2149
2150 #[cfg(feature = "nightly")]
2151 {
2152 const F1: Bn<u8, 2> = Bn::<u8, 2>::from(42u8);
2153 assert_eq!(F1.array, [42, 0]);
2154 }
2155 }
2156
2157 #[test]
2158 fn test_core_convert_u16() {
2159 let f = Bn::<u8, 1>::from(1u16);
2160 assert_eq!(f.array, [1]);
2161 let f = Bn::<u8, 2>::from(1u16);
2162 assert_eq!(f.array, [1, 0]);
2163
2164 let f = Bn::<u8, 1>::from(256u16);
2165 assert_eq!(f.array, [0]);
2166 let f = Bn::<u8, 2>::from(257u16);
2167 assert_eq!(f.array, [1, 1]);
2168 let f = Bn::<u8, 2>::from(65535u16);
2169 assert_eq!(f.array, [255, 255]);
2170
2171 let f = Bn::<u16, 1>::from(1u16);
2172 assert_eq!(f.array, [1]);
2173 let f = Bn::<u16, 2>::from(1u16);
2174 assert_eq!(f.array, [1, 0]);
2175
2176 let f = Bn::<u16, 1>::from(65535u16);
2177 assert_eq!(f.array, [65535]);
2178
2179 #[cfg(feature = "nightly")]
2180 {
2181 const F1: Bn<u8, 2> = Bn::<u8, 2>::from(0x0102u16);
2182 assert_eq!(F1.array, [0x02, 0x01]);
2183 }
2184 }
2185
2186 #[test]
2187 fn test_core_convert_u32() {
2188 let f = Bn::<u8, 1>::from(1u32);
2189 assert_eq!(f.array, [1]);
2190 let f = Bn::<u8, 1>::from(256u32);
2191 assert_eq!(f.array, [0]);
2192
2193 let f = Bn::<u8, 2>::from(1u32);
2194 assert_eq!(f.array, [1, 0]);
2195 let f = Bn::<u8, 2>::from(257u32);
2196 assert_eq!(f.array, [1, 1]);
2197 let f = Bn::<u8, 2>::from(65535u32);
2198 assert_eq!(f.array, [255, 255]);
2199
2200 let f = Bn::<u8, 4>::from(1u32);
2201 assert_eq!(f.array, [1, 0, 0, 0]);
2202 let f = Bn::<u8, 4>::from(257u32);
2203 assert_eq!(f.array, [1, 1, 0, 0]);
2204 let f = Bn::<u8, 4>::from(u32::MAX);
2205 assert_eq!(f.array, [255, 255, 255, 255]);
2206
2207 let f = Bn::<u8, 1>::from(1u32);
2208 assert_eq!(f.array, [1]);
2209 let f = Bn::<u8, 1>::from(256u32);
2210 assert_eq!(f.array, [0]);
2211
2212 let f = Bn::<u16, 2>::from(65537u32);
2213 assert_eq!(f.array, [1, 1]);
2214
2215 let f = Bn::<u32, 1>::from(1u32);
2216 assert_eq!(f.array, [1]);
2217 let f = Bn::<u32, 2>::from(1u32);
2218 assert_eq!(f.array, [1, 0]);
2219
2220 let f = Bn::<u32, 1>::from(65537u32);
2221 assert_eq!(f.array, [65537]);
2222
2223 let f = Bn::<u32, 1>::from(u32::MAX);
2224 assert_eq!(f.array, [4294967295]);
2225
2226 #[cfg(feature = "nightly")]
2227 {
2228 const F1: Bn<u8, 4> = Bn::<u8, 4>::from(0x01020304u32);
2229 assert_eq!(F1.array, [0x04, 0x03, 0x02, 0x01]);
2230 }
2231 }
2232
2233 #[test]
2234 fn test_core_convert_u64() {
2235 let f = Bn::<u8, 8>::from(0x0102030405060708u64);
2236 assert_eq!(f.array, [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
2237
2238 let f = Bn::<u16, 4>::from(0x0102030405060708u64);
2239 assert_eq!(f.array, [0x0708, 0x0506, 0x0304, 0x0102]);
2240
2241 let f = Bn::<u32, 2>::from(0x0102030405060708u64);
2242 assert_eq!(f.array, [0x05060708, 0x01020304]);
2243
2244 let f = Bn::<u64, 1>::from(0x0102030405060708u64);
2245 assert_eq!(f.array, [0x0102030405060708]);
2246
2247 #[cfg(feature = "nightly")]
2248 {
2249 const F1: Bn<u8, 8> = Bn::<u8, 8>::from(0x0102030405060708u64);
2250 assert_eq!(F1.array, [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
2251 }
2252 }
2253
2254 #[test]
2255 fn testsimple() {
2256 assert_eq!(Bn::<u8, 8>::new(), Bn::<u8, 8>::new());
2257
2258 assert_eq!(Bn::<u8, 8>::from_u8(3).unwrap().to_u32(), Some(3));
2259 assert_eq!(Bn::<u16, 4>::from_u8(3).unwrap().to_u32(), Some(3));
2260 assert_eq!(Bn::<u32, 2>::from_u8(3).unwrap().to_u32(), Some(3));
2261 assert_eq!(Bn::<u32, 2>::from_u64(3).unwrap().to_u32(), Some(3));
2262 assert_eq!(Bn::<u8, 8>::from_u64(255).unwrap().to_u32(), Some(255));
2263 assert_eq!(Bn::<u8, 8>::from_u64(256).unwrap().to_u32(), Some(256));
2264 assert_eq!(Bn::<u8, 8>::from_u64(65536).unwrap().to_u32(), Some(65536));
2265 }
2266 #[test]
2267 fn testfrom() {
2268 let mut n1 = Bn::<u8, 8>::new();
2269 n1.array[0] = 1;
2270 assert_eq!(Some(1), n1.to_u32());
2271 n1.array[1] = 1;
2272 assert_eq!(Some(257), n1.to_u32());
2273
2274 let mut n2 = Bn::<u16, 8>::new();
2275 n2.array[0] = 0xffff;
2276 assert_eq!(Some(65535), n2.to_u32());
2277 n2.array[0] = 0x0;
2278 n2.array[2] = 0x1;
2279 assert_eq!(None, n2.to_u32());
2281 assert_eq!(Some(0x100000000), n2.to_u64());
2282 }
2283
2284 #[test]
2285 fn test_from_str_bitlengths() {
2286 let test_s64 = "81906f5e4d3c2c01";
2287 let test_u64: u64 = 0x81906f5e4d3c2c01;
2288 let bb = Bn8::from_str_radix(test_s64, 16).unwrap();
2289 let cc = Bn8::from_u64(test_u64).unwrap();
2290 assert_eq!(cc.array, [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81]);
2291 assert_eq!(bb.array, [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81]);
2292 let dd = Bn16::from_u64(test_u64).unwrap();
2293 let ff = Bn16::from_str_radix(test_s64, 16).unwrap();
2294 assert_eq!(dd.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190]);
2295 assert_eq!(ff.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190]);
2296 let ee = Bn32::from_u64(test_u64).unwrap();
2297 let gg = Bn32::from_str_radix(test_s64, 16).unwrap();
2298 assert_eq!(ee.array, [0x4d3c2c01, 0x81906f5e]);
2299 assert_eq!(gg.array, [0x4d3c2c01, 0x81906f5e]);
2300 }
2301
2302 #[test]
2303 fn test_from_str_stringlengths() {
2304 let ab = Bn::<u8, 9>::from_str_radix("2281906f5e4d3c2c01", 16).unwrap();
2305 assert_eq!(
2306 ab.array,
2307 [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81, 0x22]
2308 );
2309 assert_eq!(
2310 [0x2c01, 0x4d3c, 0x6f5e, 0],
2311 Bn::<u16, 4>::from_str_radix("6f5e4d3c2c01", 16)
2312 .unwrap()
2313 .array
2314 );
2315 assert_eq!(
2316 [0x2c01, 0x4d3c, 0x6f5e, 0x190],
2317 Bn::<u16, 4>::from_str_radix("1906f5e4d3c2c01", 16)
2318 .unwrap()
2319 .array
2320 );
2321 assert_eq!(
2322 Err(make_overflow_err()),
2323 Bn::<u16, 4>::from_str_radix("f81906f5e4d3c2c01", 16)
2324 );
2325 assert_eq!(
2326 Err(make_overflow_err()),
2327 Bn::<u16, 4>::from_str_radix("af81906f5e4d3c2c01", 16)
2328 );
2329 assert_eq!(
2330 Err(make_overflow_err()),
2331 Bn::<u16, 4>::from_str_radix("baaf81906f5e4d3c2c01", 16)
2332 );
2333 let ac = Bn::<u16, 5>::from_str_radix("baaf81906f5e4d3c2c01", 16).unwrap();
2334 assert_eq!(ac.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190, 0xbaaf]);
2335 }
2336
2337 #[test]
2338 fn test_resize() {
2339 type TestInt1 = FixedUInt<u32, 1>;
2340 type TestInt2 = FixedUInt<u32, 2>;
2341
2342 let a = TestInt1::from(u32::MAX);
2343 let b: TestInt2 = a.resize();
2344 assert_eq!(b, TestInt2::from([u32::MAX, 0]));
2345
2346 let a = TestInt2::from([u32::MAX, u32::MAX]);
2347 let b: TestInt1 = a.resize();
2348 assert_eq!(b, TestInt1::from(u32::MAX));
2349 }
2350
2351 #[test]
2352 fn test_bit_length() {
2353 assert_eq!(0, Bn8::from_u8(0).unwrap().bit_length());
2354 assert_eq!(1, Bn8::from_u8(1).unwrap().bit_length());
2355 assert_eq!(2, Bn8::from_u8(2).unwrap().bit_length());
2356 assert_eq!(2, Bn8::from_u8(3).unwrap().bit_length());
2357 assert_eq!(7, Bn8::from_u8(0x70).unwrap().bit_length());
2358 assert_eq!(8, Bn8::from_u8(0xF0).unwrap().bit_length());
2359 assert_eq!(9, Bn8::from_u16(0x1F0).unwrap().bit_length());
2360
2361 assert_eq!(20, Bn8::from_u64(990223).unwrap().bit_length());
2362 assert_eq!(32, Bn8::from_u64(0xefffffff).unwrap().bit_length());
2363 assert_eq!(32, Bn8::from_u64(0x8fffffff).unwrap().bit_length());
2364 assert_eq!(31, Bn8::from_u64(0x7fffffff).unwrap().bit_length());
2365 assert_eq!(34, Bn8::from_u64(0x3ffffffff).unwrap().bit_length());
2366
2367 assert_eq!(0, Bn32::from_u8(0).unwrap().bit_length());
2368 assert_eq!(1, Bn32::from_u8(1).unwrap().bit_length());
2369 assert_eq!(2, Bn32::from_u8(2).unwrap().bit_length());
2370 assert_eq!(2, Bn32::from_u8(3).unwrap().bit_length());
2371 assert_eq!(7, Bn32::from_u8(0x70).unwrap().bit_length());
2372 assert_eq!(8, Bn32::from_u8(0xF0).unwrap().bit_length());
2373 assert_eq!(9, Bn32::from_u16(0x1F0).unwrap().bit_length());
2374
2375 assert_eq!(20, Bn32::from_u64(990223).unwrap().bit_length());
2376 assert_eq!(32, Bn32::from_u64(0xefffffff).unwrap().bit_length());
2377 assert_eq!(32, Bn32::from_u64(0x8fffffff).unwrap().bit_length());
2378 assert_eq!(31, Bn32::from_u64(0x7fffffff).unwrap().bit_length());
2379 assert_eq!(34, Bn32::from_u64(0x3ffffffff).unwrap().bit_length());
2380 }
2381
2382 #[test]
2383 fn test_bit_length_1000() {
2384 let value = Bn32::from_u16(1000).unwrap();
2386
2387 assert_eq!(value.to_u32().unwrap(), 1000);
2390 assert_eq!(value.bit_length(), 10);
2391
2392 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);
2399 assert_eq!(Bn16::from_u16(1000).unwrap().bit_length(), 10);
2400
2401 let value_from_str = Bn32::from_str_radix("1000", 10).unwrap();
2403 assert_eq!(value_from_str.bit_length(), 10);
2404
2405 let value_from_bytes = Bn32::from_le_bytes(&1000u16.to_le_bytes());
2407 assert_eq!(
2409 value_from_bytes.to_u32().unwrap_or(0),
2410 1000,
2411 "from_le_bytes didn't create the correct value"
2412 );
2413 assert_eq!(value_from_bytes.bit_length(), 10);
2414 }
2415 #[test]
2416 fn test_cmp() {
2417 let f0 = <Bn8 as Zero>::zero();
2418 let f1 = <Bn8 as Zero>::zero();
2419 let f2 = <Bn8 as One>::one();
2420 assert_eq!(f0, f1);
2421 assert!(f2 > f0);
2422 assert!(f0 < f2);
2423 let f3 = Bn32::from_u64(990223).unwrap();
2424 assert_eq!(f3, Bn32::from_u64(990223).unwrap());
2425 let f4 = Bn32::from_u64(990224).unwrap();
2426 assert!(f4 > Bn32::from_u64(990223).unwrap());
2427
2428 let f3 = Bn8::from_u64(990223).unwrap();
2429 assert_eq!(f3, Bn8::from_u64(990223).unwrap());
2430 let f4 = Bn8::from_u64(990224).unwrap();
2431 assert!(f4 > Bn8::from_u64(990223).unwrap());
2432
2433 #[cfg(feature = "nightly")]
2434 {
2435 use core::cmp::Ordering;
2436
2437 const A: FixedUInt<u8, 2> = FixedUInt::from_array([10, 0]);
2438 const B: FixedUInt<u8, 2> = FixedUInt::from_array([20, 0]);
2439 const C: FixedUInt<u8, 2> = FixedUInt::from_array([10, 0]);
2440
2441 const CMP_LT: Ordering = A.cmp(&B);
2442 const CMP_GT: Ordering = B.cmp(&A);
2443 const CMP_EQ: Ordering = A.cmp(&C);
2444 const EQ_TRUE: bool = A.eq(&C);
2445 const EQ_FALSE: bool = A.eq(&B);
2446
2447 assert_eq!(CMP_LT, Ordering::Less);
2448 assert_eq!(CMP_GT, Ordering::Greater);
2449 assert_eq!(CMP_EQ, Ordering::Equal);
2450 assert!(EQ_TRUE);
2451 assert!(!EQ_FALSE);
2452 }
2453 }
2454
2455 #[test]
2456 fn test_default() {
2457 let d: Bn8 = Default::default();
2458 assert!(<Bn8 as const_num_traits::Zero>::is_zero(&d));
2459
2460 #[cfg(feature = "nightly")]
2461 {
2462 const D: FixedUInt<u8, 2> = <FixedUInt<u8, 2> as Default>::default();
2463 assert!(<FixedUInt<u8, 2> as const_num_traits::Zero>::is_zero(&D));
2464 }
2465 }
2466
2467 #[test]
2468 fn test_clone() {
2469 let a: Bn8 = 42u8.into();
2470 let b = a;
2471 assert_eq!(a, b);
2472
2473 #[cfg(feature = "nightly")]
2474 {
2475 const A: FixedUInt<u8, 2> = FixedUInt::from_array([42, 0]);
2476 const B: FixedUInt<u8, 2> = A.clone();
2477 assert_eq!(A.array, B.array);
2478 }
2479 }
2480
2481 #[test]
2482 fn test_le_be_bytes() {
2483 let le_bytes = [1, 2, 3, 4];
2484 let be_bytes = [4, 3, 2, 1];
2485 let u8_ver = FixedUInt::<u8, 4>::from_le_bytes(&le_bytes);
2486 let u16_ver = FixedUInt::<u16, 2>::from_le_bytes(&le_bytes);
2487 let u32_ver = FixedUInt::<u32, 1>::from_le_bytes(&le_bytes);
2488 let u8_ver_be = FixedUInt::<u8, 4>::from_be_bytes(&be_bytes);
2489 let u16_ver_be = FixedUInt::<u16, 2>::from_be_bytes(&be_bytes);
2490 let u32_ver_be = FixedUInt::<u32, 1>::from_be_bytes(&be_bytes);
2491
2492 assert_eq!(u8_ver.array, [1, 2, 3, 4]);
2493 assert_eq!(u16_ver.array, [0x0201, 0x0403]);
2494 assert_eq!(u32_ver.array, [0x04030201]);
2495 assert_eq!(u8_ver_be.array, [1, 2, 3, 4]);
2496 assert_eq!(u16_ver_be.array, [0x0201, 0x0403]);
2497 assert_eq!(u32_ver_be.array, [0x04030201]);
2498
2499 let mut output_buffer = [0u8; 16];
2500 assert_eq!(u8_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
2501 assert_eq!(u8_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
2502 assert_eq!(u16_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
2503 assert_eq!(u16_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
2504 assert_eq!(u32_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
2505 assert_eq!(u32_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
2506 }
2507
2508 #[test]
2510 fn test_div_small() {
2511 type TestInt = FixedUInt<u8, 2>;
2512
2513 let test_cases = [
2515 (20u16, 3u16, 6u16), (100u16, 7u16, 14u16), (255u16, 5u16, 51u16), (65535u16, 256u16, 255u16), ];
2520
2521 for (dividend_val, divisor_val, expected) in test_cases {
2522 let dividend = TestInt::from(dividend_val);
2523 let divisor = TestInt::from(divisor_val);
2524 let expected_result = TestInt::from(expected);
2525
2526 assert_eq!(
2527 dividend / divisor,
2528 expected_result,
2529 "Division failed for {} / {} = {}",
2530 dividend_val,
2531 divisor_val,
2532 expected
2533 );
2534 }
2535 }
2536
2537 #[test]
2538 fn test_div_edge_cases() {
2539 type TestInt = FixedUInt<u16, 2>;
2540
2541 let dividend = TestInt::from(1000u16);
2543 let divisor = TestInt::from(1u16);
2544 assert_eq!(dividend / divisor, TestInt::from(1000u16));
2545
2546 let dividend = TestInt::from(42u16);
2548 let divisor = TestInt::from(42u16);
2549 assert_eq!(dividend / divisor, TestInt::from(1u16));
2550
2551 let dividend = TestInt::from(5u16);
2553 let divisor = TestInt::from(10u16);
2554 assert_eq!(dividend / divisor, TestInt::from(0u16));
2555
2556 let dividend = TestInt::from(1024u16);
2558 let divisor = TestInt::from(4u16);
2559 assert_eq!(dividend / divisor, TestInt::from(256u16));
2560 }
2561
2562 #[test]
2563 fn test_helper_methods() {
2564 type TestInt = FixedUInt<u8, 2>;
2565
2566 let mut val = <TestInt as Zero>::zero();
2568 const_set_bit(&mut val.array, 0);
2569 assert_eq!(val, TestInt::from(1u8));
2570
2571 const_set_bit(&mut val.array, 8);
2572 assert_eq!(val, TestInt::from(257u16)); let a = TestInt::from(8u8); let b = TestInt::from(1u8); assert_eq!(
2580 const_cmp_shifted(&a.array, &b.array, 3),
2581 core::cmp::Ordering::Equal
2582 );
2583
2584 assert_eq!(
2586 const_cmp_shifted(&a.array, &b.array, 2),
2587 core::cmp::Ordering::Greater
2588 );
2589
2590 assert_eq!(
2592 const_cmp_shifted(&a.array, &b.array, 4),
2593 core::cmp::Ordering::Less
2594 );
2595
2596 let mut val = TestInt::from(10u8);
2598 let one = TestInt::from(1u8);
2599 const_sub_shifted(&mut val.array, &one.array, 2); assert_eq!(val, TestInt::from(6u8)); }
2602
2603 #[test]
2604 fn test_shifted_operations_comprehensive() {
2605 type TestInt = FixedUInt<u32, 2>;
2606
2607 let a = TestInt::from(0x12345678u32);
2609 let b = TestInt::from(0x12345678u32);
2610
2611 assert_eq!(
2613 const_cmp_shifted(&a.array, &b.array, 0),
2614 core::cmp::Ordering::Equal
2615 );
2616
2617 let c = TestInt::from(0x123u32); let d = TestInt::from(0x48d159e2u32); assert_eq!(
2623 const_cmp_shifted(&d.array, &c.array, 16),
2624 core::cmp::Ordering::Greater
2625 );
2626
2627 let e = TestInt::from(1u32);
2629 let zero = TestInt::from(0u32);
2630 assert_eq!(
2631 const_cmp_shifted(&e.array, &zero.array, 100),
2632 core::cmp::Ordering::Greater
2633 );
2634 assert_eq!(
2636 const_cmp_shifted(&zero.array, &e.array, 100),
2637 core::cmp::Ordering::Equal
2638 );
2639
2640 let mut val = TestInt::from(0x10000u32); let one = TestInt::from(1u32);
2643 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)); }
2651
2652 #[test]
2653 fn test_shifted_operations_edge_cases() {
2654 type TestInt = FixedUInt<u32, 2>;
2655
2656 let a = TestInt::from(42u32);
2658 let a2 = TestInt::from(42u32);
2659 assert_eq!(
2660 const_cmp_shifted(&a.array, &a2.array, 0),
2661 core::cmp::Ordering::Equal
2662 );
2663
2664 let mut b = TestInt::from(42u32);
2665 let ten = TestInt::from(10u32);
2666 const_sub_shifted(&mut b.array, &ten.array, 0);
2667 assert_eq!(b, TestInt::from(32u32));
2668
2669 let c = TestInt::from(123u32);
2671 let large = TestInt::from(456u32);
2672 assert_eq!(
2673 const_cmp_shifted(&c.array, &large.array, 200),
2674 core::cmp::Ordering::Greater
2675 );
2676
2677 let mut d = TestInt::from(123u32);
2678 const_sub_shifted(&mut d.array, &large.array, 200); assert_eq!(d, TestInt::from(123u32));
2680
2681 let zero = TestInt::from(0u32);
2683 let one = TestInt::from(1u32);
2684 assert_eq!(
2685 const_cmp_shifted(&zero.array, &zero.array, 10),
2686 core::cmp::Ordering::Equal
2687 );
2688 assert_eq!(
2689 const_cmp_shifted(&one.array, &zero.array, 10),
2690 core::cmp::Ordering::Greater
2691 );
2692 }
2693
2694 #[test]
2695 fn test_shifted_operations_equivalence() {
2696 type TestInt = FixedUInt<u32, 2>;
2697
2698 let test_cases = [
2700 (0x12345u32, 0x678u32, 4),
2701 (0x1000u32, 0x10u32, 8),
2702 (0xABCDu32, 0x1u32, 16),
2703 (0x80000000u32, 0x1u32, 1),
2704 ];
2705
2706 for (a_val, b_val, shift) in test_cases {
2707 let a = TestInt::from(a_val);
2708 let b = TestInt::from(b_val);
2709
2710 let optimized_cmp = const_cmp_shifted(&a.array, &b.array, shift);
2712 let naive_cmp = a.cmp(&(b << shift));
2713 assert_eq!(
2714 optimized_cmp, naive_cmp,
2715 "cmp_shifted mismatch: {} vs ({} << {})",
2716 a_val, b_val, shift
2717 );
2718
2719 if a >= (b << shift) {
2721 let mut optimized_result = a;
2722 const_sub_shifted(&mut optimized_result.array, &b.array, shift);
2723
2724 let naive_result = a - (b << shift);
2725 assert_eq!(
2726 optimized_result, naive_result,
2727 "sub_shifted mismatch: {} - ({} << {})",
2728 a_val, b_val, shift
2729 );
2730 }
2731 }
2732 }
2733
2734 #[test]
2735 fn test_div_assign_in_place_optimization() {
2736 type TestInt = FixedUInt<u32, 2>;
2737
2738 let test_cases = [
2740 (100u32, 10u32, 10u32, 0u32), (123u32, 7u32, 17u32, 4u32), (1000u32, 13u32, 76u32, 12u32), (65535u32, 255u32, 257u32, 0u32), ];
2745
2746 for (dividend_val, divisor_val, expected_quotient, expected_remainder) in test_cases {
2747 let mut dividend = TestInt::from(dividend_val);
2749 let divisor = TestInt::from(divisor_val);
2750
2751 dividend /= divisor;
2752 assert_eq!(
2753 dividend,
2754 TestInt::from(expected_quotient),
2755 "div_assign: {} / {} should be {}",
2756 dividend_val,
2757 divisor_val,
2758 expected_quotient
2759 );
2760
2761 let dividend2 = TestInt::from(dividend_val);
2763 let (quotient, remainder) = dividend2.div_rem(&divisor);
2764 assert_eq!(
2765 quotient,
2766 TestInt::from(expected_quotient),
2767 "div_rem quotient: {} / {} should be {}",
2768 dividend_val,
2769 divisor_val,
2770 expected_quotient
2771 );
2772 assert_eq!(
2773 remainder,
2774 TestInt::from(expected_remainder),
2775 "div_rem remainder: {} % {} should be {}",
2776 dividend_val,
2777 divisor_val,
2778 expected_remainder
2779 );
2780
2781 assert_eq!(
2783 quotient * divisor + remainder,
2784 TestInt::from(dividend_val),
2785 "Property check failed for {}",
2786 dividend_val
2787 );
2788 }
2789 }
2790
2791 #[test]
2792 fn test_div_assign_stack_efficiency() {
2793 type TestInt = FixedUInt<u32, 4>; let mut dividend = TestInt::from(0x123456789ABCDEFu64);
2797 let divisor = TestInt::from(0x12345u32);
2798 let original_dividend = dividend;
2799
2800 dividend /= divisor;
2802
2803 let remainder = original_dividend % divisor;
2805 assert_eq!(dividend * divisor + remainder, original_dividend);
2806 }
2807
2808 #[test]
2809 fn test_rem_assign_optimization() {
2810 type TestInt = FixedUInt<u32, 2>;
2811
2812 let test_cases = [
2813 (100u32, 10u32, 0u32), (123u32, 7u32, 4u32), (1000u32, 13u32, 12u32), (65535u32, 255u32, 0u32), ];
2818
2819 for (dividend_val, divisor_val, expected_remainder) in test_cases {
2820 let mut dividend = TestInt::from(dividend_val);
2821 let divisor = TestInt::from(divisor_val);
2822
2823 dividend %= divisor;
2824 assert_eq!(
2825 dividend,
2826 TestInt::from(expected_remainder),
2827 "rem_assign: {} % {} should be {}",
2828 dividend_val,
2829 divisor_val,
2830 expected_remainder
2831 );
2832 }
2833 }
2834
2835 #[test]
2836 fn test_div_with_remainder_property() {
2837 type TestInt = FixedUInt<u32, 2>;
2838
2839 let test_cases = [
2841 (100u32, 10u32, 10u32), (123u32, 7u32, 17u32), (1000u32, 13u32, 76u32), (65535u32, 255u32, 257u32), ];
2846
2847 for (dividend_val, divisor_val, expected_quotient) in test_cases {
2848 let dividend = TestInt::from(dividend_val);
2849 let divisor = TestInt::from(divisor_val);
2850
2851 let quotient = dividend / divisor;
2853 assert_eq!(
2854 quotient,
2855 TestInt::from(expected_quotient),
2856 "Division: {} / {} should be {}",
2857 dividend_val,
2858 divisor_val,
2859 expected_quotient
2860 );
2861
2862 let remainder = dividend % divisor;
2864 assert_eq!(
2865 quotient * divisor + remainder,
2866 dividend,
2867 "Division property check failed for {}",
2868 dividend_val
2869 );
2870 }
2871 }
2872
2873 #[test]
2874 fn test_code_simplification_benefits() {
2875 type TestInt = FixedUInt<u32, 2>;
2876
2877 let dividend = TestInt::from(12345u32);
2879 let divisor = TestInt::from(67u32);
2880 let quotient = dividend / divisor;
2881 let remainder = dividend % divisor;
2882
2883 assert_eq!(quotient * divisor + remainder, dividend);
2885 }
2886
2887 #[test]
2888 fn test_rem_assign_correctness_after_fix() {
2889 type TestInt = FixedUInt<u32, 2>;
2890
2891 let mut a = TestInt::from(17u32);
2893 let b = TestInt::from(5u32);
2894
2895 a %= b;
2898 assert_eq!(a, TestInt::from(2u32), "17 % 5 should be 2");
2899
2900 let mut test_val = TestInt::from(100u32);
2902 test_val %= TestInt::from(7u32);
2903 assert_eq!(
2904 test_val,
2905 TestInt::from(2u32),
2906 "100 % 7 should be 2 (not 14, the quotient)"
2907 );
2908 }
2909
2910 #[test]
2911 fn test_div_property_based() {
2912 type TestInt = FixedUInt<u16, 2>;
2913
2914 let test_pairs = [
2916 (12345u16, 67u16),
2917 (1000u16, 13u16),
2918 (65535u16, 255u16),
2919 (5000u16, 7u16),
2920 ];
2921
2922 for (dividend_val, divisor_val) in test_pairs {
2923 let dividend = TestInt::from(dividend_val);
2924 let divisor = TestInt::from(divisor_val);
2925
2926 let quotient = dividend / divisor;
2927
2928 let remainder = dividend - (quotient * divisor);
2930 let reconstructed = quotient * divisor + remainder;
2931
2932 assert_eq!(
2933 reconstructed,
2934 dividend,
2935 "Property failed for {} / {}: {} * {} + {} != {}",
2936 dividend_val,
2937 divisor_val,
2938 quotient.to_u32().unwrap_or(0),
2939 divisor_val,
2940 remainder.to_u32().unwrap_or(0),
2941 dividend_val
2942 );
2943
2944 assert!(
2946 remainder < divisor,
2947 "Remainder {} >= divisor {} for {} / {}",
2948 remainder.to_u32().unwrap_or(0),
2949 divisor_val,
2950 dividend_val,
2951 divisor_val
2952 );
2953 }
2954 }
2955}