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 + subtle::ConditionallySelectable, 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);
192 let valid = subtle::Choice::from((!overflow) as u8);
193 subtle::CtOption::new(res, valid)
194 }
195
196 pub fn ct_checked_sub(&self, other: &Self) -> subtle::CtOption<Self> {
198 let (res, overflow) = <Self as OverflowingSub>::overflowing_sub(*self, *other);
199 let valid = subtle::Choice::from((!overflow) as u8);
200 subtle::CtOption::new(res, valid)
201 }
202
203 pub fn ct_checked_mul(&self, other: &Self) -> subtle::CtOption<Self> {
205 let (res, overflow) = <Self as OverflowingMul>::overflowing_mul(*self, *other);
206 let valid = subtle::Choice::from((!overflow) as u8);
207 subtle::CtOption::new(res, valid)
208 }
209
210 pub fn ct_checked_shl(&self, bits: u32) -> subtle::CtOption<Self> {
219 subtle::CtOption::new(
220 bit_ops_impl::const_unbounded_shl_u32::<T, N, Ct>(*self, bits),
221 ct_checked_shift_valid(bits, Self::BIT_SIZE),
222 )
223 }
224
225 pub fn ct_checked_shr(&self, bits: u32) -> subtle::CtOption<Self> {
230 subtle::CtOption::new(
231 bit_ops_impl::const_unbounded_shr_u32::<T, N, Ct>(*self, bits),
232 ct_checked_shift_valid(bits, Self::BIT_SIZE),
233 )
234 }
235
236 pub fn ct_checked_next_power_of_two(self) -> subtle::CtOption<Self>
238 where
239 T: subtle::ConstantTimeEq,
240 {
241 let one = <Self as One>::one();
242 let m_one = <Self as const_num_traits::WrappingSub>::wrapping_sub(self, one);
243 let leading = <Self as PrimBits>::leading_zeros(m_one);
244 let bits = Self::BIT_SIZE as u32 - leading;
245 let shifted = one << (bits as usize);
246 let is_zero_choice =
247 <Self as subtle::ConstantTimeEq>::ct_eq(&self, &<Self as Zero>::zero());
248 let result = <Self as subtle::ConditionallySelectable>::conditional_select(
250 &shifted,
251 &one,
252 is_zero_choice,
253 );
254 let overflow = (bits >= Self::BIT_SIZE as u32) as u8;
257 let valid_otherwise = subtle::Choice::from(1u8 ^ overflow);
258 let valid = <subtle::Choice as subtle::ConditionallySelectable>::conditional_select(
259 &valid_otherwise,
260 &subtle::Choice::from(1u8),
261 is_zero_choice,
262 );
263 subtle::CtOption::new(result, valid)
264 }
265
266 pub fn ct_checked_pow(self, exp: u32) -> subtle::CtOption<Self>
267 where
268 T: subtle::ConstantTimeEq + subtle::ConstantTimeGreater,
269 for<'a> &'a Self: core::ops::Mul<&'a Self, Output = Self>,
270 {
271 let mut result = <Self as One>::one();
272 let mut base = self;
273 let mut e = exp;
274 let mut any_overflow: u8 = 0;
275 for _ in 0..u32::BITS {
276 let bit = core::hint::black_box((e & 1) as u8);
280 let (candidate, mul_ov) = <Self as OverflowingMul>::overflowing_mul(result, base);
281 any_overflow |= (mul_ov as u8) & bit;
283 let bit_t = <T as core::convert::From<u8>>::from(bit);
285 let mask = core::hint::black_box(bit_t * <T as Bounded>::max_value());
286 for i in 0..N {
287 let diff = result.array[i] ^ candidate.array[i];
288 result.array[i] ^= mask & diff;
289 }
290 e >>= 1;
291 let (new_base, base_ov) = <Self as OverflowingMul>::overflowing_mul(base, base);
292 let any_remaining: u8 = core::hint::black_box((e != 0) as u8);
294 any_overflow |= (base_ov as u8) & any_remaining;
295 base = new_base;
296 }
297 let valid = subtle::Choice::from(1u8 ^ any_overflow);
298 subtle::CtOption::new(result, valid)
299 }
300}
301
302impl<T: MachineWord + subtle::ConstantTimeEq, const N: usize> subtle::ConstantTimeEq
307 for FixedUInt<T, N, Ct>
308{
309 fn ct_eq(&self, other: &Self) -> subtle::Choice {
310 <[T] as subtle::ConstantTimeEq>::ct_eq(self.array.as_slice(), other.array.as_slice())
311 }
312}
313
314impl<T: MachineWord + subtle::ConditionallySelectable, const N: usize>
315 subtle::ConditionallySelectable for FixedUInt<T, N, Ct>
316{
317 fn conditional_select(a: &Self, b: &Self, choice: subtle::Choice) -> Self {
318 let mut array = a.array;
319 let mut i = 0;
320 while i < N {
321 array[i] = T::conditional_select(&a.array[i], &b.array[i], choice);
322 i += 1;
323 }
324 FixedUInt::from_array(array)
325 }
326}
327
328impl<T: MachineWord + subtle::ConstantTimeEq + subtle::ConstantTimeGreater, const N: usize>
335 subtle::ConstantTimeGreater for FixedUInt<T, N, Ct>
336{
337 fn ct_gt(&self, other: &Self) -> subtle::Choice {
338 let mut gt = subtle::Choice::from(0u8);
339 let mut undecided = subtle::Choice::from(1u8);
340 let mut i = N;
341 while i > 0 {
342 i -= 1;
343 let gt_here = self.array[i].ct_gt(&other.array[i]);
344 let eq_here = self.array[i].ct_eq(&other.array[i]);
345 gt |= undecided & gt_here;
346 undecided &= eq_here;
347 }
348 gt
349 }
350}
351
352impl<T: MachineWord + subtle::ConstantTimeEq + subtle::ConstantTimeGreater, const N: usize>
353 subtle::ConstantTimeLess for FixedUInt<T, N, Ct>
354{
355}
356
357const LONGEST_WORD_IN_BITS: usize = 128;
358
359impl<T: MachineWord, const N: usize, P: Personality> FixedUInt<T, N, P> {
360 const WORD_SIZE: usize = core::mem::size_of::<T>();
361 const WORD_BITS: usize = Self::WORD_SIZE * 8;
362 const BYTE_SIZE: usize = Self::WORD_SIZE * N;
363 const BIT_SIZE: usize = Self::BYTE_SIZE * 8;
364
365 pub const BYTE_WIDTH: usize = Self::BYTE_SIZE;
372
373 pub fn new() -> FixedUInt<T, N, P> {
375 FixedUInt::from_array([T::zero(); N])
376 }
377
378 pub fn words(&self) -> &[T; N] {
380 &self.array
381 }
382
383 pub fn bit_length(&self) -> u32 {
385 Self::BIT_SIZE as u32 - PrimBits::leading_zeros(*self)
386 }
387}
388
389impl<T: MachineWord, const N: usize> FixedUInt<T, N, Nct> {
390 pub fn div_rem(&self, divisor: &Self) -> (Self, Self) {
392 let (quotient, remainder) = const_div_rem(&self.array, &divisor.array);
393 (Self::from_array(quotient), Self::from_array(remainder))
394 }
395
396 pub fn to_radix_str<'a>(
398 &self,
399 result: &'a mut [u8],
400 radix: u8,
401 ) -> Result<&'a str, core::fmt::Error> {
402 type Error = core::fmt::Error;
403
404 if !(2..=16).contains(&radix) {
405 return Err(Error {}); }
407 for byte in result.iter_mut() {
408 *byte = b'0';
409 }
410 if <Self as Zero>::is_zero(self) {
411 if !result.is_empty() {
412 result[0] = b'0';
413 return core::str::from_utf8(&result[0..1]).map_err(|_| Error {});
414 } else {
415 return Err(Error {});
416 }
417 }
418
419 let mut number = *self;
420 let mut idx = result.len();
421
422 let radix_t = Self::from(radix);
423
424 while !<Self as Zero>::is_zero(&number) {
425 if idx == 0 {
426 return Err(Error {}); }
428
429 idx -= 1;
430 let (quotient, remainder) = number.div_rem(&radix_t);
431
432 let digit =
436 <T as const_num_traits::ToPrimitive>::to_u8(&remainder.array[0]).unwrap_or(0);
437 result[idx] = match digit {
438 0..=9 => b'0' + digit, 10..=16 => b'a' + (digit - 10), _ => return Err(Error {}),
441 };
442
443 number = quotient;
444 }
445
446 let start = result[idx..].iter().position(|&c| c != b'0').unwrap_or(0);
447 let radix_str = core::str::from_utf8(&result[idx + start..]).map_err(|_| Error {})?;
448 Ok(radix_str)
449 }
450}
451
452c0nst::c0nst! {
454 pub(crate) c0nst fn impl_from_le_bytes_slice<T: [c0nst] ConstMachineWord, const N: usize>(
457 bytes: &[u8],
458 ) -> [T; N] {
459 let word_size = core::mem::size_of::<T>();
460 let mut ret: [T; N] = [T::zero(); N];
461 let capacity = N * word_size;
462 let total_bytes = if bytes.len() < capacity { bytes.len() } else { capacity };
463
464 let mut byte_index = 0;
465 while byte_index < total_bytes {
466 let word_index = byte_index / word_size;
467 let byte_in_word = byte_index % word_size;
468
469 let byte_value: T = <T as core::convert::From<u8>>::from(bytes[byte_index]);
470 let shifted_value = byte_value.shl(byte_in_word * 8);
471 ret[word_index] = ret[word_index].bitor(shifted_value);
472 byte_index += 1;
473 }
474 ret
475 }
476
477 pub(crate) c0nst fn impl_from_be_bytes_slice<T: [c0nst] ConstMachineWord, const N: usize>(
480 bytes: &[u8],
481 ) -> [T; N] {
482 let word_size = core::mem::size_of::<T>();
483 let mut ret: [T; N] = [T::zero(); N];
484 let capacity_bytes = N * word_size;
485 let total_bytes = if bytes.len() < capacity_bytes { bytes.len() } else { capacity_bytes };
486
487 let start_offset = if bytes.len() > capacity_bytes {
490 bytes.len() - capacity_bytes
491 } else {
492 0
493 };
494
495 let mut byte_index = 0;
496 while byte_index < total_bytes {
497 let be_byte_index = start_offset + total_bytes - 1 - byte_index;
499 let word_index = byte_index / word_size;
500 let byte_in_word = byte_index % word_size;
501
502 let byte_value: T = <T as core::convert::From<u8>>::from(bytes[be_byte_index]);
503 let shifted_value = byte_value.shl(byte_in_word * 8);
504 ret[word_index] = ret[word_index].bitor(shifted_value);
505 byte_index += 1;
506 }
507 ret
508 }
509}
510
511impl<T: MachineWord, const N: usize, P: Personality> FixedUInt<T, N, P> {
513 pub fn from_le_bytes(bytes: &[u8]) -> Self {
515 Self::from_array(impl_from_le_bytes_slice::<T, N>(bytes))
516 }
517
518 pub fn from_be_bytes(bytes: &[u8]) -> Self {
520 Self::from_array(impl_from_be_bytes_slice::<T, N>(bytes))
521 }
522}
523
524impl<T: MachineWord, const N: usize, P: Personality> FixedUInt<T, N, P> {
525 pub fn to_le_bytes<'a>(&self, output_buffer: &'a mut [u8]) -> Result<&'a [u8], bool> {
527 let total_bytes = N * Self::WORD_SIZE;
528 if output_buffer.len() < total_bytes {
529 return Err(false); }
531 for (i, word) in self.array.iter().enumerate() {
532 let start = i * Self::WORD_SIZE;
533 let end = start + Self::WORD_SIZE;
534 let word_bytes = word.to_le_bytes();
535 output_buffer[start..end].copy_from_slice(word_bytes.as_ref());
536 }
537 Ok(&output_buffer[..total_bytes])
538 }
539
540 pub fn to_be_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().rev().enumerate() {
547 let start = i * Self::WORD_SIZE;
548 let end = start + Self::WORD_SIZE;
549 let word_bytes = word.to_be_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_hex_str<'a>(&self, result: &'a mut [u8]) -> Result<&'a str, core::fmt::Error> {
557 type Error = core::fmt::Error;
558
559 let word_size = Self::WORD_SIZE;
560 let need_bits = self.bit_length() as usize;
562 let need_chars = if need_bits > 0 { need_bits / 4 } else { 0 };
564
565 if result.len() < need_chars {
566 return Err(Error {});
568 }
569 let offset = result.len() - need_chars;
570 for i in result.iter_mut() {
571 *i = b'0';
572 }
573
574 for iter_words in 0..self.array.len() {
575 let word = self.array[iter_words];
576 let mut encoded = [0u8; LONGEST_WORD_IN_BITS / 4];
577 let encode_slice = &mut encoded[0..word_size * 2];
578 let mut wordbytes = word.to_le_bytes();
579 wordbytes.as_mut().reverse();
580 let wordslice = wordbytes.as_ref();
581 to_slice_hex(wordslice, encode_slice).map_err(|_| Error {})?;
582 for iter_chars in 0..encode_slice.len() {
583 let copy_char_to = (iter_words * word_size * 2) + iter_chars;
584 if copy_char_to <= need_chars {
585 let reverse_index = offset + (need_chars - copy_char_to);
586 if reverse_index <= result.len() && reverse_index > 0 {
587 let current_char = encode_slice[(encode_slice.len() - 1) - iter_chars];
588 result[reverse_index - 1] = current_char;
589 }
590 }
591 }
592 }
593
594 let convert = core::str::from_utf8(result).map_err(|_| Error {})?;
595 let pos = convert.find(|c: char| c != '0');
596 match pos {
597 Some(x) => Ok(&convert[x..convert.len()]),
598 None => {
599 if convert.starts_with('0') {
600 Ok("0")
601 } else {
602 Ok(convert)
603 }
604 }
605 }
606 }
607
608 #[must_use]
613 pub fn resize<const N2: usize>(&self) -> FixedUInt<T, N2, P> {
614 let mut array = [T::zero(); N2];
615 let min_size = N.min(N2);
616 array[..min_size].copy_from_slice(&self.array[..min_size]);
617 FixedUInt::<T, N2, P>::from_array(array)
618 }
619
620 #[cfg(feature = "num-traits")]
621 fn hex_fmt(
622 &self,
623 formatter: &mut core::fmt::Formatter<'_>,
624 uppercase: bool,
625 ) -> Result<(), core::fmt::Error>
626 where
627 u8: core::convert::TryFrom<T>,
628 {
629 type Err = core::fmt::Error;
630
631 fn to_casedigit(byte: u8, uppercase: bool) -> Result<char, core::fmt::Error> {
632 let digit = core::char::from_digit(byte as u32, 16).ok_or(Err {})?;
633 if uppercase {
634 digit.to_uppercase().next().ok_or(Err {})
635 } else {
636 digit.to_lowercase().next().ok_or(Err {})
637 }
638 }
639
640 let mut leading_zero: bool = true;
641
642 let mut maybe_write = |nibble: char| -> Result<(), core::fmt::Error> {
643 leading_zero &= nibble == '0';
644 if !leading_zero {
645 formatter.write_char(nibble)?;
646 }
647 Ok(())
648 };
649
650 for index in (0..N).rev() {
651 let val = self.array[index];
652 let mask: T = 0xff.into();
653 for j in (0..Self::WORD_SIZE as u32).rev() {
654 let masked = val & mask.shl((j * 8) as usize);
655
656 let byte = u8::try_from(masked.shr((j * 8) as usize)).map_err(|_| Err {})?;
657
658 maybe_write(to_casedigit((byte & 0xf0) >> 4, uppercase)?)?;
659 maybe_write(to_casedigit(byte & 0x0f, uppercase)?)?;
660 }
661 }
662 Ok(())
663 }
664}
665
666c0nst::c0nst! {
667 pub(crate) c0nst fn add_with_carry<T: [c0nst] ConstMachineWord, const N: usize>(
672 a: &[T; N],
673 b: &[T; N],
674 carry_in: bool,
675 ) -> ([T; N], bool) {
676 let mut result = [T::zero(); N];
677 let mut carry = carry_in;
678 let mut i = 0usize;
679 while i < N {
680 let (sum, c) = CarryingAdd::carrying_add(a[i], b[i], carry);
681 result[i] = sum;
682 carry = c;
683 i += 1;
684 }
685 (result, carry)
686 }
687
688 pub(crate) c0nst fn sub_with_borrow<T: [c0nst] ConstMachineWord, const N: usize>(
690 a: &[T; N],
691 b: &[T; N],
692 borrow_in: bool,
693 ) -> ([T; N], bool) {
694 let mut result = [T::zero(); N];
695 let mut borrow = borrow_in;
696 let mut i = 0usize;
697 while i < N {
698 let (diff, br) = BorrowingSub::borrowing_sub(a[i], b[i], borrow);
699 result[i] = diff;
700 borrow = br;
701 i += 1;
702 }
703 (result, borrow)
704 }
705
706 pub(crate) c0nst fn add_impl<T: [c0nst] ConstMachineWord, const N: usize>(
711 target: &mut [T; N],
712 other: &[T; N]
713 ) -> bool {
714 let mut carry = false;
715 let mut i = 0usize;
716 while i < N {
717 let (sum, c) = CarryingAdd::carrying_add(target[i], other[i], carry);
718 target[i] = sum;
719 carry = c;
720 i += 1;
721 }
722 carry
723 }
724
725 pub(crate) c0nst fn sub_impl<T: [c0nst] ConstMachineWord, const N: usize>(
727 target: &mut [T; N],
728 other: &[T; N]
729 ) -> bool {
730 let mut borrow = false;
731 let mut i = 0usize;
732 while i < N {
733 let (diff, br) = BorrowingSub::borrowing_sub(target[i], other[i], borrow);
734 target[i] = diff;
735 borrow = br;
736 i += 1;
737 }
738 borrow
739 }
740}
741
742c0nst::c0nst! {
743 pub(crate) c0nst fn const_shl_impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
745 target: &mut FixedUInt<T, N, P>,
746 bits: usize,
747 ) {
748 if N == 0 {
749 return;
750 }
751 let word_bits = FixedUInt::<T, N>::WORD_BITS;
752 let nwords = bits / word_bits;
753 let nbits = bits - nwords * word_bits;
754
755 if nwords >= N {
757 let mut i = 0;
758 while i < N {
759 target.array[i] = T::zero();
760 i += 1;
761 }
762 return;
763 }
764
765 let mut i = N;
767 while i > nwords {
768 i -= 1;
769 target.array[i] = target.array[i - nwords];
770 }
771 let mut i = 0;
773 while i < nwords {
774 target.array[i] = T::zero();
775 i += 1;
776 }
777
778 if nbits != 0 {
779 let mut i = N;
781 while i > 1 {
782 i -= 1;
783 let right = target.array[i] << nbits;
784 let left = target.array[i - 1] >> (word_bits - nbits);
785 target.array[i] = right | left;
786 }
787 target.array[0] <<= nbits;
788 }
789 }
790
791 pub(crate) c0nst fn const_shr_impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
793 target: &mut FixedUInt<T, N, P>,
794 bits: usize,
795 ) {
796 if N == 0 {
797 return;
798 }
799 let word_bits = FixedUInt::<T, N>::WORD_BITS;
800 let nwords = bits / word_bits;
801 let nbits = bits - nwords * word_bits;
802
803 if nwords >= N {
805 let mut i = 0;
806 while i < N {
807 target.array[i] = T::zero();
808 i += 1;
809 }
810 return;
811 }
812
813 let last_index = N - 1;
814 let last_word = N - nwords;
815
816 let mut i = 0;
818 while i < last_word {
819 target.array[i] = target.array[i + nwords];
820 i += 1;
821 }
822
823 let mut i = last_word;
825 while i < N {
826 target.array[i] = T::zero();
827 i += 1;
828 }
829
830 if nbits != 0 {
831 let mut i = 0;
833 while i < last_index {
834 let left = target.array[i] >> nbits;
835 let right = target.array[i + 1] << (word_bits - nbits);
836 target.array[i] = left | right;
837 i += 1;
838 }
839 target.array[last_index] >>= nbits;
840 }
841 }
842
843 pub(crate) c0nst fn const_shl_ct<
852 T: [c0nst] ConstMachineWord + MachineWord,
853 const N: usize,
854 P: Personality,
855 >(
856 target: &mut FixedUInt<T, N, P>,
857 bits: usize,
858 ) {
859 if N == 0 {
860 return;
861 }
862 let layers = core::mem::size_of::<usize>() * 8;
865 let mut k = 0;
866 while k < layers {
867 let amount = 1usize << k;
868 let mut shifted = *target;
870 const_shl_impl(&mut shifted, amount);
871 let bit_k = core::hint::black_box(((bits >> k) & 1) as u8);
875 let bit_k_t = <T as core::convert::From<u8>>::from(bit_k);
876 let mask = <T as core::ops::Mul>::mul(bit_k_t, <T as Bounded>::max_value());
877 let mut i = 0;
879 while i < N {
880 let diff =
881 <T as core::ops::BitXor>::bitxor(target.array[i], shifted.array[i]);
882 let masked = <T as core::ops::BitAnd>::bitand(mask, diff);
883 target.array[i] = <T as core::ops::BitXor>::bitxor(target.array[i], masked);
884 i += 1;
885 }
886 k += 1;
887 }
888 }
889
890 pub(crate) c0nst fn const_shr_ct<
893 T: [c0nst] ConstMachineWord + MachineWord,
894 const N: usize,
895 P: Personality,
896 >(
897 target: &mut FixedUInt<T, N, P>,
898 bits: usize,
899 ) {
900 if N == 0 {
901 return;
902 }
903 let layers = core::mem::size_of::<usize>() * 8;
906 let mut k = 0;
907 while k < layers {
908 let amount = 1usize << k;
909 let mut shifted = *target;
910 const_shr_impl(&mut shifted, amount);
911 let bit_k = core::hint::black_box(((bits >> k) & 1) as u8);
913 let bit_k_t = <T as core::convert::From<u8>>::from(bit_k);
914 let mask = <T as core::ops::Mul>::mul(bit_k_t, <T as Bounded>::max_value());
915 let mut i = 0;
916 while i < N {
917 let diff =
918 <T as core::ops::BitXor>::bitxor(target.array[i], shifted.array[i]);
919 let masked = <T as core::ops::BitAnd>::bitand(mask, diff);
920 target.array[i] = <T as core::ops::BitXor>::bitxor(target.array[i], masked);
921 i += 1;
922 }
923 k += 1;
924 }
925 }
926
927 pub(crate) c0nst fn const_mul<T: [c0nst] ConstMachineWord, const N: usize, const CHECK_OVERFLOW: bool, P: Personality>(
937 op1: &[T; N],
938 op2: &[T; N],
939 word_bits: usize,
940 ) -> ([T; N], bool) {
941 let mut result: [T; N] = [<T as ConstZero>::ZERO; N];
942 let mut overflowed = false;
943 let t_max = <T as ConstMachineWord>::to_double(<T as Bounded>::max_value());
944 let dw_zero = <<T as ConstMachineWord>::ConstDoubleWord as ConstZero>::ZERO;
945
946 let mut i = 0;
947 while i < N {
948 let mut carry = dw_zero;
949 let mut j = 0;
950 while j < N {
951 let round = i + j;
952 let op1_dw = <T as ConstMachineWord>::to_double(op1[i]);
953 let op2_dw = <T as ConstMachineWord>::to_double(op2[j]);
954 let mul_res = op1_dw * op2_dw;
955 let mut accumulator = if round < N {
956 <T as ConstMachineWord>::to_double(result[round])
957 } else {
958 dw_zero
959 };
960 accumulator += mul_res + carry;
961
962 match P::TAG {
963 PersonalityTag::Nct => {
964 if accumulator > t_max {
965 carry = accumulator >> word_bits;
966 accumulator &= t_max;
967 } else {
968 carry = dw_zero;
969 }
970 }
971 PersonalityTag::Ct => {
972 carry = accumulator >> word_bits;
973 accumulator &= t_max;
974 }
975 }
976 if round < N {
977 result[round] = <T as ConstMachineWord>::from_double(accumulator);
978 } else if CHECK_OVERFLOW {
979 overflowed |= accumulator != dw_zero;
980 }
981 j += 1;
982 }
983 if CHECK_OVERFLOW {
984 overflowed |= carry != dw_zero;
985 }
986 i += 1;
987 }
988 (result, overflowed)
989 }
990
991 pub(crate) c0nst fn const_word_bits<T>() -> usize {
993 core::mem::size_of::<T>() * 8
994 }
995
996 pub(crate) c0nst fn const_cmp_words<T: [c0nst] ConstMachineWord>(a: T, b: T) -> Option<core::cmp::Ordering> {
998 if a > b {
999 Some(core::cmp::Ordering::Greater)
1000 } else if a < b {
1001 Some(core::cmp::Ordering::Less)
1002 } else {
1003 None
1004 }
1005 }
1006
1007 pub(crate) c0nst fn const_leading_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1009 array: &[T; N],
1010 ) -> u32 {
1011 let mut ret = 0u32;
1012 let mut index = N;
1013 while index > 0 {
1014 index -= 1;
1015 let v = array[index];
1016 ret += <T as PrimBits>::leading_zeros(v);
1017 if !<T as Zero>::is_zero(&v) {
1018 break;
1019 }
1020 }
1021 ret
1022 }
1023
1024 pub(crate) c0nst fn const_leading_zeros_ct<T: [c0nst] ConstMachineWord, const N: usize>(
1031 array: &[T; N],
1032 ) -> u32 {
1033 let mut total: u32 = 0;
1034 let mut decided: u32 = 0;
1036 let mut index = N;
1037 while index > 0 {
1038 index -= 1;
1039 let v = array[index];
1040 let v_lz = <T as PrimBits>::leading_zeros(v);
1041 let undecided = core::hint::black_box(!decided);
1045 total += undecided & v_lz;
1046 let v_nz_bit = (!<T as Zero>::is_zero(&v)) as u32;
1048 let v_nz_mask = core::hint::black_box(v_nz_bit.wrapping_neg());
1049 decided |= v_nz_mask;
1050 }
1051 total
1052 }
1053
1054 pub(crate) c0nst fn const_trailing_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1056 array: &[T; N],
1057 ) -> u32 {
1058 let mut ret = 0u32;
1059 let mut index = 0;
1060 while index < N {
1061 let v = array[index];
1062 ret += <T as PrimBits>::trailing_zeros(v);
1063 if !<T as Zero>::is_zero(&v) {
1064 break;
1065 }
1066 index += 1;
1067 }
1068 ret
1069 }
1070
1071 pub(crate) c0nst fn const_trailing_zeros_ct<T: [c0nst] ConstMachineWord, const N: usize>(
1076 array: &[T; N],
1077 ) -> u32 {
1078 let mut total: u32 = 0;
1079 let mut decided: u32 = 0;
1081 let mut index = 0;
1082 while index < N {
1083 let v = array[index];
1084 let v_tz = <T as PrimBits>::trailing_zeros(v);
1085 let undecided = core::hint::black_box(!decided);
1088 total += undecided & v_tz;
1089 let v_nz_bit = (!<T as Zero>::is_zero(&v)) as u32;
1090 let v_nz_mask = core::hint::black_box(v_nz_bit.wrapping_neg());
1091 decided |= v_nz_mask;
1092 index += 1;
1093 }
1094 total
1095 }
1096
1097 pub(crate) c0nst fn const_bit_length<T: [c0nst] ConstMachineWord, const N: usize>(
1099 array: &[T; N],
1100 ) -> usize {
1101 let word_bits = const_word_bits::<T>();
1102 let bit_size = N * word_bits;
1103 bit_size - const_leading_zeros::<T, N>(array) as usize
1104 }
1105
1106 pub(crate) c0nst fn const_is_zero<T: [c0nst] ConstMachineWord, const N: usize>(
1108 array: &[T; N],
1109 ) -> bool {
1110 let mut index = 0;
1111 while index < N {
1112 if !<T as Zero>::is_zero(&array[index]) {
1113 return false;
1114 }
1115 index += 1;
1116 }
1117 true
1118 }
1119
1120 pub(crate) c0nst fn const_is_zero_ct<T: [c0nst] ConstMachineWord, const N: usize>(
1125 array: &[T; N],
1126 ) -> bool {
1127 let mut acc = <T as ConstZero>::ZERO;
1128 let mut index = 0;
1129 while index < N {
1130 acc = <T as core::ops::BitOr>::bitor(acc, array[index]);
1131 index += 1;
1132 }
1133 <T as Zero>::is_zero(&acc)
1134 }
1135
1136 pub(crate) c0nst fn const_is_one<T: [c0nst] ConstMachineWord, const N: usize>(
1141 array: &[T; N],
1142 ) -> bool {
1143 if N == 0 || !array[0].is_one() {
1144 return false;
1145 }
1146 let mut i = 1;
1147 while i < N {
1148 if !<T as Zero>::is_zero(&array[i]) {
1149 return false;
1150 }
1151 i += 1;
1152 }
1153 true
1154 }
1155
1156 pub(crate) c0nst fn const_is_one_ct<T: [c0nst] ConstMachineWord, const N: usize>(
1161 array: &[T; N],
1162 ) -> bool {
1163 if N == 0 {
1164 return false;
1165 }
1166 let mut acc = <T as core::ops::BitXor>::bitxor(array[0], <T as ConstOne>::ONE);
1167 let mut index = 1;
1168 while index < N {
1169 acc = <T as core::ops::BitOr>::bitor(acc, array[index]);
1170 index += 1;
1171 }
1172 <T as Zero>::is_zero(&acc)
1173 }
1174
1175 pub(crate) c0nst fn const_set_bit<T: [c0nst] ConstMachineWord, const N: usize>(
1181 array: &mut [T; N],
1182 pos: usize,
1183 ) {
1184 let word_bits = const_word_bits::<T>();
1185 let word_idx = pos / word_bits;
1186 if word_idx >= N {
1187 return;
1188 }
1189 let bit_idx = pos % word_bits;
1190 array[word_idx] |= <T as ConstOne>::ONE << bit_idx;
1191 }
1192
1193 pub(crate) c0nst fn const_cmp<T: [c0nst] ConstMachineWord, const N: usize>(
1198 a: &[T; N],
1199 b: &[T; N],
1200 ) -> core::cmp::Ordering {
1201 let mut index = N;
1202 while index > 0 {
1203 index -= 1;
1204 if let Some(ord) = const_cmp_words(a[index], b[index]) {
1205 return ord;
1206 }
1207 }
1208 core::cmp::Ordering::Equal
1209 }
1210
1211 pub(crate) c0nst fn const_cmp_ct<T: [c0nst] ConstMachineWord, const N: usize>(
1216 a: &[T; N],
1217 b: &[T; N],
1218 ) -> core::cmp::Ordering {
1219 let mut result: u8 = 0;
1221 let mut decided: u8 = 0;
1223 let mut index = N;
1224 while index > 0 {
1225 index -= 1;
1226 let gt = (a[index] > b[index]) as u8;
1227 let lt = (a[index] < b[index]) as u8;
1228 let here = (gt << 1) | lt;
1230 let undecided_mask = core::hint::black_box(!decided);
1232 result |= undecided_mask & here;
1233 let here_nz_mask = core::hint::black_box(((here != 0) as u8).wrapping_neg());
1235 decided |= here_nz_mask;
1236 }
1237 match result {
1238 2 => core::cmp::Ordering::Greater,
1239 1 => core::cmp::Ordering::Less,
1240 _ => core::cmp::Ordering::Equal,
1241 }
1242 }
1243
1244 pub(crate) c0nst fn const_get_shifted_word<T: [c0nst] ConstMachineWord, const N: usize>(
1249 array: &[T; N],
1250 word_idx: usize,
1251 word_shift: usize,
1252 bit_shift: usize,
1253 ) -> T {
1254 let word_bits = const_word_bits::<T>();
1255
1256 if bit_shift >= word_bits {
1258 return <T as ConstZero>::ZERO;
1259 }
1260
1261 if word_idx < word_shift {
1262 return <T as ConstZero>::ZERO;
1263 }
1264
1265 let source_idx = word_idx - word_shift;
1266
1267 if bit_shift == 0 {
1268 if source_idx < N {
1269 array[source_idx]
1270 } else {
1271 <T as ConstZero>::ZERO
1272 }
1273 } else {
1274 let mut result = <T as ConstZero>::ZERO;
1275
1276 if source_idx < N {
1278 result |= array[source_idx] << bit_shift;
1279 }
1280
1281 if source_idx > 0 && source_idx - 1 < N {
1283 let high_bits = array[source_idx - 1] >> (word_bits - bit_shift);
1284 result |= high_bits;
1285 }
1286
1287 result
1288 }
1289 }
1290
1291 pub(crate) c0nst fn const_cmp_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
1296 array: &[T; N],
1297 other: &[T; N],
1298 shift_bits: usize,
1299 ) -> core::cmp::Ordering {
1300 let word_bits = const_word_bits::<T>();
1301
1302 if shift_bits == 0 {
1303 return const_cmp::<T, N>(array, other);
1304 }
1305
1306 let word_shift = shift_bits / word_bits;
1307 if word_shift >= N {
1308 if const_is_zero::<T, N>(array) {
1310 return core::cmp::Ordering::Equal;
1311 } else {
1312 return core::cmp::Ordering::Greater;
1313 }
1314 }
1315
1316 let bit_shift = shift_bits % word_bits;
1317
1318 let mut index = N;
1320 while index > 0 {
1321 index -= 1;
1322 let self_word = array[index];
1323 let other_shifted_word = const_get_shifted_word::<T, N>(
1324 other, index, word_shift, bit_shift
1325 );
1326
1327 if let Some(ord) = const_cmp_words(self_word, other_shifted_word) {
1328 return ord;
1329 }
1330 }
1331
1332 core::cmp::Ordering::Equal
1333 }
1334
1335 pub(crate) c0nst fn const_sub_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
1340 array: &mut [T; N],
1341 other: &[T; N],
1342 shift_bits: usize,
1343 ) {
1344 let word_bits = const_word_bits::<T>();
1345
1346 if shift_bits == 0 {
1347 sub_impl::<T, N>(array, other);
1348 return;
1349 }
1350
1351 let word_shift = shift_bits / word_bits;
1352 if word_shift >= N {
1353 return;
1354 }
1355
1356 let bit_shift = shift_bits % word_bits;
1357 let mut borrow = T::zero();
1358 let mut index = 0;
1359 while index < N {
1360 let other_word = const_get_shifted_word::<T, N>(other, index, word_shift, bit_shift);
1361 let (res, borrow1) = array[index].overflowing_sub(other_word);
1362 let (res, borrow2) = res.overflowing_sub(borrow);
1363 borrow = if borrow1 || borrow2 { T::one() } else { T::zero() };
1364 array[index] = res;
1365 index += 1;
1366 }
1367 }
1368
1369 pub(crate) c0nst fn const_div<T: [c0nst] ConstMachineWord, const N: usize>(
1373 dividend: &mut [T; N],
1374 divisor: &[T; N],
1375 ) -> [T; N] {
1376 use core::cmp::Ordering;
1377
1378 match const_cmp::<T, N>(dividend, divisor) {
1379 Ordering::Less => {
1381 let remainder = *dividend;
1382 let mut i = 0;
1383 while i < N {
1384 dividend[i] = <T as ConstZero>::ZERO;
1385 i += 1;
1386 }
1387 return remainder;
1388 }
1389 Ordering::Equal => {
1391 let mut i = 0;
1392 while i < N {
1393 dividend[i] = <T as ConstZero>::ZERO;
1394 i += 1;
1395 }
1396 if N > 0 {
1397 dividend[0] = <T as ConstOne>::ONE;
1398 }
1399 return [<T as ConstZero>::ZERO; N];
1400 }
1401 Ordering::Greater => {}
1402 }
1403
1404 let mut quotient = [<T as ConstZero>::ZERO; N];
1405
1406 let dividend_bits = const_bit_length::<T, N>(dividend);
1408 let divisor_bits = const_bit_length::<T, N>(divisor);
1409
1410 let mut bit_pos = if dividend_bits >= divisor_bits {
1411 dividend_bits - divisor_bits
1412 } else {
1413 0
1414 };
1415
1416 while bit_pos > 0 {
1418 let cmp = const_cmp_shifted::<T, N>(dividend, divisor, bit_pos);
1419 if !matches!(cmp, Ordering::Less) {
1420 break;
1421 }
1422 bit_pos -= 1;
1423 }
1424
1425 loop {
1427 let cmp = const_cmp_shifted::<T, N>(dividend, divisor, bit_pos);
1428 if !matches!(cmp, Ordering::Less) {
1429 const_sub_shifted::<T, N>(dividend, divisor, bit_pos);
1430 const_set_bit::<T, N>(&mut quotient, bit_pos);
1431 }
1432
1433 if bit_pos == 0 {
1434 break;
1435 }
1436 bit_pos -= 1;
1437 }
1438
1439 let remainder = *dividend;
1440 *dividend = quotient;
1441 remainder
1442 }
1443
1444 pub(crate) c0nst fn const_div_rem<T: [c0nst] ConstMachineWord, const N: usize>(
1448 dividend: &[T; N],
1449 divisor: &[T; N],
1450 ) -> ([T; N], [T; N]) {
1451 if const_is_zero(divisor) {
1452 maybe_panic(PanicReason::DivByZero)
1453 }
1454 let mut quotient = *dividend;
1455 let remainder = const_div(&mut quotient, divisor);
1456 (quotient, remainder)
1457 }
1458}
1459
1460c0nst::c0nst! {
1461 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> Default for FixedUInt<T, N, P> {
1462 fn default() -> Self {
1463 FixedUInt::from_array([<T as ConstZero>::ZERO; N])
1464 }
1465 }
1466
1467 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> Clone for FixedUInt<T, N, P> {
1468 fn clone(&self) -> Self {
1469 *self
1470 }
1471 }
1472}
1473
1474#[cfg(feature = "num-traits")]
1477impl<T: MachineWord, const N: usize> num_traits::Unsigned for FixedUInt<T, N, Nct> {}
1478
1479c0nst::c0nst! {
1482 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::cmp::PartialEq for FixedUInt<T, N, P> {
1483 fn eq(&self, other: &Self) -> bool {
1490 match P::TAG {
1491 PersonalityTag::Nct => self.array == other.array,
1492 PersonalityTag::Ct => {
1493 let mut diff = <T as ConstZero>::ZERO;
1494 let mut i = 0;
1495 while i < N {
1496 let x = <T as core::ops::BitXor>::bitxor(self.array[i], other.array[i]);
1497 diff = <T as core::ops::BitOr>::bitor(diff, x);
1498 i += 1;
1499 }
1500 <T as Zero>::is_zero(&diff)
1501 }
1502 }
1503 }
1504 }
1505
1506 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::cmp::Eq for FixedUInt<T, N, P> {}
1507
1508 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::cmp::Ord for FixedUInt<T, N, P> {
1509 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
1510 match P::TAG {
1511 PersonalityTag::Nct => const_cmp(&self.array, &other.array),
1512 PersonalityTag::Ct => const_cmp_ct(&self.array, &other.array),
1513 }
1514 }
1515 }
1516
1517 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::cmp::PartialOrd for FixedUInt<T, N, P> {
1518 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
1519 Some(self.cmp(other))
1520 }
1521 }
1522}
1523
1524c0nst::c0nst! {
1529 c0nst fn const_from_le_bytes<T: [c0nst] ConstMachineWord, const N: usize, const B: usize>(
1532 bytes: [u8; B],
1533 ) -> [T; N] {
1534 impl_from_le_bytes_slice::<T, N>(&bytes)
1535 }
1536
1537 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::convert::From<u8> for FixedUInt<T, N, P> {
1538 fn from(x: u8) -> Self {
1539 Self::from_array(const_from_le_bytes(x.to_le_bytes()))
1540 }
1541 }
1542
1543 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::convert::From<u16> for FixedUInt<T, N, P> {
1544 fn from(x: u16) -> Self {
1545 Self::from_array(const_from_le_bytes(x.to_le_bytes()))
1546 }
1547 }
1548
1549 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::convert::From<u32> for FixedUInt<T, N, P> {
1550 fn from(x: u32) -> Self {
1551 Self::from_array(const_from_le_bytes(x.to_le_bytes()))
1552 }
1553 }
1554
1555 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::convert::From<u64> for FixedUInt<T, N, P> {
1556 fn from(x: u64) -> Self {
1557 Self::from_array(const_from_le_bytes(x.to_le_bytes()))
1558 }
1559 }
1560}
1561
1562fn make_parse_int_err() -> core::num::ParseIntError {
1569 <u8>::from_str_radix("-", 2).err().unwrap()
1570}
1571#[cfg(feature = "num-traits")]
1572fn make_overflow_err() -> core::num::ParseIntError {
1573 <u8>::from_str_radix("101", 16).err().unwrap()
1574}
1575#[cfg(feature = "num-traits")]
1576fn make_empty_error() -> core::num::ParseIntError {
1577 <u8>::from_str_radix("", 8).err().unwrap()
1578}
1579
1580fn to_slice_hex<T: AsRef<[u8]>>(
1581 input: T,
1582 output: &mut [u8],
1583) -> Result<(), core::num::ParseIntError> {
1584 fn from_digit(byte: u8) -> Option<char> {
1585 core::char::from_digit(byte as u32, 16)
1586 }
1587 let r = input.as_ref();
1588 if r.len() * 2 != output.len() {
1589 return Err(make_parse_int_err());
1590 }
1591 for i in 0..r.len() {
1592 let byte = r[i];
1593 output[i * 2] = from_digit((byte & 0xf0) >> 4).ok_or_else(make_parse_int_err)? as u8;
1594 output[i * 2 + 1] = from_digit(byte & 0x0f).ok_or_else(make_parse_int_err)? as u8;
1595 }
1596
1597 Ok(())
1598}
1599
1600pub(super) enum PanicReason {
1601 Add,
1602 Sub,
1603 Mul,
1604 DivByZero,
1605}
1606
1607c0nst::c0nst! {
1608 pub(super) c0nst fn maybe_panic(r: PanicReason) {
1609 match r {
1610 PanicReason::Add => panic!("attempt to add with overflow"),
1611 PanicReason::Sub => panic!("attempt to subtract with overflow"),
1612 PanicReason::Mul => panic!("attempt to multiply with overflow"),
1613 PanicReason::DivByZero => panic!("attempt to divide by zero"),
1614 }
1615 }
1616
1617 pub(crate) c0nst fn const_ct_select<
1630 T: [c0nst] ConstMachineWord + MachineWord,
1631 const N: usize,
1632 P: Personality,
1633 >(
1634 if_zero: FixedUInt<T, N, P>,
1635 if_one: FixedUInt<T, N, P>,
1636 choice: u8,
1637 ) -> FixedUInt<T, N, P> {
1638 let choice = core::hint::black_box(choice);
1639 let bit_t = <T as core::convert::From<u8>>::from(choice);
1640 let mask = <T as core::ops::Mul>::mul(bit_t, <T as Bounded>::max_value());
1641 let mut result = if_zero;
1642 let mut i = 0;
1643 while i < N {
1644 let diff = <T as core::ops::BitXor>::bitxor(if_zero.array[i], if_one.array[i]);
1645 let masked = <T as core::ops::BitAnd>::bitand(mask, diff);
1646 result.array[i] = <T as core::ops::BitXor>::bitxor(if_zero.array[i], masked);
1647 i += 1;
1648 }
1649 result
1650 }
1651
1652 pub(super) c0nst fn maybe_panic_if<P: Personality>(
1653 overflow: bool,
1654 reason: PanicReason,
1655 ) {
1656 match P::TAG {
1657 PersonalityTag::Nct => {
1658 if overflow {
1659 maybe_panic(reason);
1660 }
1661 }
1662 PersonalityTag::Ct => {
1663 let _ = overflow;
1664 let _ = reason;
1665 }
1666 }
1667 }
1668}
1669
1670#[cfg(test)]
1673#[cfg(feature = "num-traits")]
1674mod tests {
1675 use super::FixedUInt as Bn;
1676 use super::*;
1677 use const_num_traits::{One, Zero};
1678 use num_traits::{FromPrimitive, Num, ToPrimitive};
1679
1680 type Bn8 = Bn<u8, 8>;
1681 type Bn16 = Bn<u16, 4>;
1682 type Bn32 = Bn<u32, 2>;
1683
1684 c0nst::c0nst! {
1685 pub c0nst fn test_add<T: [c0nst] ConstMachineWord, const N: usize>(
1686 a: &mut [T; N],
1687 b: &[T; N]
1688 ) -> bool {
1689 add_impl(a, b)
1690 }
1691
1692 pub c0nst fn test_sub<T: [c0nst] ConstMachineWord, const N: usize>(
1693 a: &mut [T; N],
1694 b: &[T; N]
1695 ) -> bool {
1696 sub_impl(a, b)
1697 }
1698
1699 pub c0nst fn test_mul<T: [c0nst] ConstMachineWord, const N: usize>(
1700 a: &[T; N],
1701 b: &[T; N],
1702 word_bits: usize,
1703 ) -> ([T; N], bool) {
1704 const_mul::<T, N, true, const_num_traits::Nct>(a, b, word_bits)
1705 }
1706
1707 pub c0nst fn arr_leading_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1708 a: &[T; N],
1709 ) -> u32 {
1710 const_leading_zeros::<T, N>(a)
1711 }
1712
1713 pub c0nst fn arr_trailing_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1714 a: &[T; N],
1715 ) -> u32 {
1716 const_trailing_zeros::<T, N>(a)
1717 }
1718
1719 pub c0nst fn arr_bit_length<T: [c0nst] ConstMachineWord, const N: usize>(
1720 a: &[T; N],
1721 ) -> usize {
1722 const_bit_length::<T, N>(a)
1723 }
1724
1725 pub c0nst fn arr_is_zero<T: [c0nst] ConstMachineWord, const N: usize>(
1726 a: &[T; N],
1727 ) -> bool {
1728 const_is_zero::<T, N>(a)
1729 }
1730
1731 pub c0nst fn arr_set_bit<T: [c0nst] ConstMachineWord, const N: usize>(
1732 a: &mut [T; N],
1733 pos: usize,
1734 ) {
1735 const_set_bit::<T, N>(a, pos)
1736 }
1737
1738 pub c0nst fn arr_cmp<T: [c0nst] ConstMachineWord, const N: usize>(
1739 a: &[T; N],
1740 b: &[T; N],
1741 ) -> core::cmp::Ordering {
1742 const_cmp::<T, N>(a, b)
1743 }
1744
1745 pub c0nst fn arr_cmp_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
1746 a: &[T; N],
1747 b: &[T; N],
1748 shift_bits: usize,
1749 ) -> core::cmp::Ordering {
1750 const_cmp_shifted::<T, N>(a, b, shift_bits)
1751 }
1752
1753 pub c0nst fn arr_get_shifted_word<T: [c0nst] ConstMachineWord, const N: usize>(
1754 a: &[T; N],
1755 word_idx: usize,
1756 word_shift: usize,
1757 bit_shift: usize,
1758 ) -> T {
1759 const_get_shifted_word::<T, N>(a, word_idx, word_shift, bit_shift)
1760 }
1761 }
1762
1763 #[test]
1764 fn test_const_add_impl() {
1765 let mut a: [u8; 4] = [1, 0, 0, 0];
1767 let b: [u8; 4] = [2, 0, 0, 0];
1768 let overflow = test_add(&mut a, &b);
1769 assert_eq!(a, [3, 0, 0, 0]);
1770 assert!(!overflow);
1771
1772 let mut a: [u8; 4] = [255, 0, 0, 0];
1774 let b: [u8; 4] = [1, 0, 0, 0];
1775 let overflow = test_add(&mut a, &b);
1776 assert_eq!(a, [0, 1, 0, 0]);
1777 assert!(!overflow);
1778
1779 let mut a: [u8; 4] = [255, 255, 255, 255];
1781 let b: [u8; 4] = [1, 0, 0, 0];
1782 let overflow = test_add(&mut a, &b);
1783 assert_eq!(a, [0, 0, 0, 0]);
1784 assert!(overflow);
1785
1786 let mut a: [u32; 2] = [0xFFFFFFFF, 0];
1788 let b: [u32; 2] = [1, 0];
1789 let overflow = test_add(&mut a, &b);
1790 assert_eq!(a, [0, 1]);
1791 assert!(!overflow);
1792
1793 #[cfg(feature = "nightly")]
1794 {
1795 const ADD_RESULT: ([u8; 4], bool) = {
1796 let mut a = [1u8, 0, 0, 0];
1797 let b = [2u8, 0, 0, 0];
1798 let overflow = test_add(&mut a, &b);
1799 (a, overflow)
1800 };
1801 assert_eq!(ADD_RESULT, ([3, 0, 0, 0], false));
1802 }
1803 }
1804
1805 #[test]
1806 fn test_const_sub_impl() {
1807 let mut a: [u8; 4] = [3, 0, 0, 0];
1809 let b: [u8; 4] = [1, 0, 0, 0];
1810 let overflow = test_sub(&mut a, &b);
1811 assert_eq!(a, [2, 0, 0, 0]);
1812 assert!(!overflow);
1813
1814 let mut a: [u8; 4] = [0, 1, 0, 0];
1816 let b: [u8; 4] = [1, 0, 0, 0];
1817 let overflow = test_sub(&mut a, &b);
1818 assert_eq!(a, [255, 0, 0, 0]);
1819 assert!(!overflow);
1820
1821 let mut a: [u8; 4] = [0, 0, 0, 0];
1823 let b: [u8; 4] = [1, 0, 0, 0];
1824 let overflow = test_sub(&mut a, &b);
1825 assert_eq!(a, [255, 255, 255, 255]);
1826 assert!(overflow);
1827
1828 let mut a: [u32; 2] = [0, 1];
1830 let b: [u32; 2] = [1, 0];
1831 let overflow = test_sub(&mut a, &b);
1832 assert_eq!(a, [0xFFFFFFFF, 0]);
1833 assert!(!overflow);
1834
1835 #[cfg(feature = "nightly")]
1836 {
1837 const SUB_RESULT: ([u8; 4], bool) = {
1838 let mut a = [3u8, 0, 0, 0];
1839 let b = [1u8, 0, 0, 0];
1840 let overflow = test_sub(&mut a, &b);
1841 (a, overflow)
1842 };
1843 assert_eq!(SUB_RESULT, ([2, 0, 0, 0], false));
1844 }
1845 }
1846
1847 #[test]
1848 fn test_const_mul_impl() {
1849 let a: [u8; 2] = [3, 0];
1851 let b: [u8; 2] = [4, 0];
1852 let (result, overflow) = test_mul(&a, &b, 8);
1853 assert_eq!(result, [12, 0]);
1854 assert!(!overflow);
1855
1856 let a: [u8; 2] = [200, 0];
1858 let b: [u8; 2] = [2, 0];
1859 let (result, overflow) = test_mul(&a, &b, 8);
1860 assert_eq!(result, [0x90, 0x01]);
1861 assert!(!overflow);
1862
1863 let a: [u8; 2] = [0, 1]; let b: [u8; 2] = [0, 1]; let (_result, overflow) = test_mul(&a, &b, 8);
1867 assert!(overflow);
1868
1869 let a: [u8; 3] = [0, 0, 1];
1873 let b: [u8; 3] = [0, 0, 1];
1874 let (_result, overflow) = test_mul(&a, &b, 8);
1875 assert!(overflow, "N=3 high-position overflow not detected");
1876
1877 let a: [u8; 3] = [0, 0, 2];
1881 let b: [u8; 3] = [0, 0, 2];
1882 let (_result, overflow) = test_mul(&a, &b, 8);
1883 assert!(
1884 overflow,
1885 "N=3 high-position overflow with larger values not detected"
1886 );
1887
1888 let a: [u8; 3] = [0, 1, 0];
1892 let b: [u8; 3] = [0, 1, 0];
1893 let (result, overflow) = test_mul(&a, &b, 8);
1894 assert_eq!(result, [0, 0, 1]);
1895 assert!(
1896 !overflow,
1897 "N=3 non-overflow incorrectly detected as overflow"
1898 );
1899
1900 let a: [u8; 3] = [255, 0, 0];
1904 let b: [u8; 3] = [255, 0, 0];
1905 let (result, overflow) = test_mul(&a, &b, 8);
1906 assert_eq!(result, [0x01, 0xFE, 0x00]);
1907 assert!(!overflow);
1908
1909 #[cfg(feature = "nightly")]
1910 {
1911 const MUL_RESULT: ([u8; 2], bool) = test_mul(&[3u8, 0], &[4u8, 0], 8);
1912 assert_eq!(MUL_RESULT, ([12, 0], false));
1913 }
1914 }
1915
1916 #[test]
1917 fn test_const_helpers() {
1918 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]));
1943 assert!(!arr_is_zero(&[1u8, 0, 0, 0]));
1944 assert!(!arr_is_zero(&[0u8, 0, 0, 1]));
1945 assert!(!arr_is_zero(&[0u8, 1, 0, 0]));
1946
1947 let mut arr: [u8; 4] = [0, 0, 0, 0];
1949 arr_set_bit(&mut arr, 0);
1950 assert_eq!(arr, [1, 0, 0, 0]);
1951
1952 let mut arr: [u8; 4] = [0, 0, 0, 0];
1953 arr_set_bit(&mut arr, 8);
1954 assert_eq!(arr, [0, 1, 0, 0]);
1955
1956 let mut arr: [u8; 4] = [0, 0, 0, 0];
1957 arr_set_bit(&mut arr, 31);
1958 assert_eq!(arr, [0, 0, 0, 0x80]);
1959
1960 let mut arr: [u8; 4] = [0, 0, 0, 0];
1962 arr_set_bit(&mut arr, 0);
1963 arr_set_bit(&mut arr, 3);
1964 arr_set_bit(&mut arr, 8);
1965 assert_eq!(arr, [0b00001001, 1, 0, 0]);
1966
1967 let mut arr: [u8; 4] = [0, 0, 0, 0];
1969 arr_set_bit(&mut arr, 32);
1970 assert_eq!(arr, [0, 0, 0, 0]);
1971
1972 assert_eq!(arr_leading_zeros(&[0u32, 0]), 64);
1974 assert_eq!(arr_leading_zeros(&[1u32, 0]), 63);
1975 assert_eq!(arr_leading_zeros(&[0u32, 1]), 31);
1976 assert_eq!(arr_trailing_zeros(&[0u32, 0]), 64);
1977 assert_eq!(arr_trailing_zeros(&[0u32, 1]), 32);
1978 assert_eq!(arr_bit_length(&[0u32, 0]), 0);
1979 assert_eq!(arr_bit_length(&[1u32, 0]), 1);
1980 assert_eq!(arr_bit_length(&[0u32, 1]), 33);
1981
1982 #[cfg(feature = "nightly")]
1983 {
1984 const LEADING: u32 = arr_leading_zeros(&[0u8, 0, 1, 0]);
1985 assert_eq!(LEADING, 15);
1986
1987 const TRAILING: u32 = arr_trailing_zeros(&[0u8, 0, 1, 0]);
1988 assert_eq!(TRAILING, 16);
1989
1990 const BIT_LEN: usize = arr_bit_length(&[0u8, 0, 1, 0]);
1991 assert_eq!(BIT_LEN, 17);
1992
1993 const IS_ZERO: bool = arr_is_zero(&[0u8, 0, 0, 0]);
1994 assert!(IS_ZERO);
1995
1996 const NOT_ZERO: bool = arr_is_zero(&[0u8, 1, 0, 0]);
1997 assert!(!NOT_ZERO);
1998
1999 const SET_BIT_RESULT: [u8; 4] = {
2000 let mut arr = [0u8, 0, 0, 0];
2001 arr_set_bit(&mut arr, 10);
2002 arr
2003 };
2004 assert_eq!(SET_BIT_RESULT, [0, 0b00000100, 0, 0]);
2005 }
2006 }
2007
2008 #[test]
2009 fn test_const_cmp() {
2010 use core::cmp::Ordering;
2011
2012 assert_eq!(arr_cmp(&[1u8, 2, 3, 4], &[1u8, 2, 3, 4]), Ordering::Equal);
2014 assert_eq!(arr_cmp(&[0u8, 0, 0, 0], &[0u8, 0, 0, 0]), Ordering::Equal);
2015
2016 assert_eq!(arr_cmp(&[0u8, 0, 0, 2], &[0u8, 0, 0, 1]), Ordering::Greater);
2018
2019 assert_eq!(arr_cmp(&[0u8, 0, 0, 1], &[0u8, 0, 0, 2]), Ordering::Less);
2021
2022 assert_eq!(arr_cmp(&[2u8, 0, 0, 0], &[1u8, 0, 0, 0]), Ordering::Greater);
2024
2025 assert_eq!(arr_cmp(&[1u8, 0, 0, 0], &[2u8, 0, 0, 0]), Ordering::Less);
2027
2028 assert_eq!(arr_cmp(&[0u32, 1], &[0u32, 1]), Ordering::Equal);
2030 assert_eq!(arr_cmp(&[0u32, 2], &[0u32, 1]), Ordering::Greater);
2031 assert_eq!(arr_cmp(&[0u32, 1], &[0u32, 2]), Ordering::Less);
2032
2033 #[cfg(feature = "nightly")]
2034 {
2035 const CMP_EQ: Ordering = arr_cmp(&[1u8, 2, 3, 4], &[1u8, 2, 3, 4]);
2036 const CMP_GT: Ordering = arr_cmp(&[0u8, 0, 0, 2], &[0u8, 0, 0, 1]);
2037 const CMP_LT: Ordering = arr_cmp(&[0u8, 0, 0, 1], &[0u8, 0, 0, 2]);
2038 assert_eq!(CMP_EQ, Ordering::Equal);
2039 assert_eq!(CMP_GT, Ordering::Greater);
2040 assert_eq!(CMP_LT, Ordering::Less);
2041 }
2042 }
2043
2044 #[test]
2045 fn test_const_cmp_shifted() {
2046 use core::cmp::Ordering;
2047
2048 assert_eq!(
2050 arr_cmp_shifted(&[1u8, 0, 0, 0], &[1u8, 0, 0, 0], 0),
2051 Ordering::Equal
2052 );
2053
2054 assert_eq!(
2056 arr_cmp_shifted(&[0u8, 1, 0, 0], &[1u8, 0, 0, 0], 8),
2057 Ordering::Equal
2058 );
2059
2060 assert_eq!(
2062 arr_cmp_shifted(&[0u8, 2, 0, 0], &[1u8, 0, 0, 0], 8),
2063 Ordering::Greater
2064 );
2065
2066 assert_eq!(
2068 arr_cmp_shifted(&[0u8, 0, 0, 0], &[1u8, 0, 0, 0], 8),
2069 Ordering::Less
2070 );
2071
2072 assert_eq!(
2075 arr_cmp_shifted(&[1u8, 0, 0, 0], &[1u8, 0, 0, 0], 32),
2076 Ordering::Greater
2077 );
2078
2079 assert_eq!(
2081 arr_cmp_shifted(&[0u8, 0, 0, 0], &[255u8, 255, 255, 255], 32),
2082 Ordering::Equal
2083 );
2084
2085 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 0, 1, 0), 0);
2089 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 1, 1, 0), 1);
2090 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 2, 1, 0), 2);
2091
2092 assert_eq!(arr_get_shifted_word(&[0x0Fu8, 0xF0, 0, 0], 0, 0, 4), 0xF0);
2096 assert_eq!(arr_get_shifted_word(&[0x0Fu8, 0xF0, 0, 0], 1, 0, 4), 0x00);
2098
2099 assert_eq!(arr_get_shifted_word(&[0xFFu8, 0x00, 0, 0], 0, 0, 4), 0xF0);
2102 assert_eq!(arr_get_shifted_word(&[0xFFu8, 0x00, 0, 0], 1, 0, 4), 0x0F);
2104
2105 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 0, 1, 4), 0);
2109 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 1, 1, 4), 0xB0);
2111 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 2, 1, 4), 0xDA);
2113
2114 #[cfg(feature = "nightly")]
2115 {
2116 const CMP_SHIFTED_EQ: Ordering = arr_cmp_shifted(&[0u8, 1, 0, 0], &[1u8, 0, 0, 0], 8);
2117 const CMP_SHIFTED_GT: Ordering = arr_cmp_shifted(&[0u8, 2, 0, 0], &[1u8, 0, 0, 0], 8);
2118 assert_eq!(CMP_SHIFTED_EQ, Ordering::Equal);
2119 assert_eq!(CMP_SHIFTED_GT, Ordering::Greater);
2120 }
2121 }
2122
2123 #[test]
2124 fn test_core_convert_u8() {
2125 let f = Bn::<u8, 1>::from(1u8);
2126 assert_eq!(f.array, [1]);
2127 let f = Bn::<u8, 2>::from(1u8);
2128 assert_eq!(f.array, [1, 0]);
2129
2130 let f = Bn::<u16, 1>::from(1u8);
2131 assert_eq!(f.array, [1]);
2132 let f = Bn::<u16, 2>::from(1u8);
2133 assert_eq!(f.array, [1, 0]);
2134
2135 #[cfg(feature = "nightly")]
2136 {
2137 const F1: Bn<u8, 2> = Bn::<u8, 2>::from(42u8);
2138 assert_eq!(F1.array, [42, 0]);
2139 }
2140 }
2141
2142 #[test]
2143 fn test_core_convert_u16() {
2144 let f = Bn::<u8, 1>::from(1u16);
2145 assert_eq!(f.array, [1]);
2146 let f = Bn::<u8, 2>::from(1u16);
2147 assert_eq!(f.array, [1, 0]);
2148
2149 let f = Bn::<u8, 1>::from(256u16);
2150 assert_eq!(f.array, [0]);
2151 let f = Bn::<u8, 2>::from(257u16);
2152 assert_eq!(f.array, [1, 1]);
2153 let f = Bn::<u8, 2>::from(65535u16);
2154 assert_eq!(f.array, [255, 255]);
2155
2156 let f = Bn::<u16, 1>::from(1u16);
2157 assert_eq!(f.array, [1]);
2158 let f = Bn::<u16, 2>::from(1u16);
2159 assert_eq!(f.array, [1, 0]);
2160
2161 let f = Bn::<u16, 1>::from(65535u16);
2162 assert_eq!(f.array, [65535]);
2163
2164 #[cfg(feature = "nightly")]
2165 {
2166 const F1: Bn<u8, 2> = Bn::<u8, 2>::from(0x0102u16);
2167 assert_eq!(F1.array, [0x02, 0x01]);
2168 }
2169 }
2170
2171 #[test]
2172 fn test_core_convert_u32() {
2173 let f = Bn::<u8, 1>::from(1u32);
2174 assert_eq!(f.array, [1]);
2175 let f = Bn::<u8, 1>::from(256u32);
2176 assert_eq!(f.array, [0]);
2177
2178 let f = Bn::<u8, 2>::from(1u32);
2179 assert_eq!(f.array, [1, 0]);
2180 let f = Bn::<u8, 2>::from(257u32);
2181 assert_eq!(f.array, [1, 1]);
2182 let f = Bn::<u8, 2>::from(65535u32);
2183 assert_eq!(f.array, [255, 255]);
2184
2185 let f = Bn::<u8, 4>::from(1u32);
2186 assert_eq!(f.array, [1, 0, 0, 0]);
2187 let f = Bn::<u8, 4>::from(257u32);
2188 assert_eq!(f.array, [1, 1, 0, 0]);
2189 let f = Bn::<u8, 4>::from(u32::MAX);
2190 assert_eq!(f.array, [255, 255, 255, 255]);
2191
2192 let f = Bn::<u8, 1>::from(1u32);
2193 assert_eq!(f.array, [1]);
2194 let f = Bn::<u8, 1>::from(256u32);
2195 assert_eq!(f.array, [0]);
2196
2197 let f = Bn::<u16, 2>::from(65537u32);
2198 assert_eq!(f.array, [1, 1]);
2199
2200 let f = Bn::<u32, 1>::from(1u32);
2201 assert_eq!(f.array, [1]);
2202 let f = Bn::<u32, 2>::from(1u32);
2203 assert_eq!(f.array, [1, 0]);
2204
2205 let f = Bn::<u32, 1>::from(65537u32);
2206 assert_eq!(f.array, [65537]);
2207
2208 let f = Bn::<u32, 1>::from(u32::MAX);
2209 assert_eq!(f.array, [4294967295]);
2210
2211 #[cfg(feature = "nightly")]
2212 {
2213 const F1: Bn<u8, 4> = Bn::<u8, 4>::from(0x01020304u32);
2214 assert_eq!(F1.array, [0x04, 0x03, 0x02, 0x01]);
2215 }
2216 }
2217
2218 #[test]
2219 fn test_core_convert_u64() {
2220 let f = Bn::<u8, 8>::from(0x0102030405060708u64);
2221 assert_eq!(f.array, [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
2222
2223 let f = Bn::<u16, 4>::from(0x0102030405060708u64);
2224 assert_eq!(f.array, [0x0708, 0x0506, 0x0304, 0x0102]);
2225
2226 let f = Bn::<u32, 2>::from(0x0102030405060708u64);
2227 assert_eq!(f.array, [0x05060708, 0x01020304]);
2228
2229 let f = Bn::<u64, 1>::from(0x0102030405060708u64);
2230 assert_eq!(f.array, [0x0102030405060708]);
2231
2232 #[cfg(feature = "nightly")]
2233 {
2234 const F1: Bn<u8, 8> = Bn::<u8, 8>::from(0x0102030405060708u64);
2235 assert_eq!(F1.array, [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
2236 }
2237 }
2238
2239 #[test]
2240 fn testsimple() {
2241 assert_eq!(Bn::<u8, 8>::new(), Bn::<u8, 8>::new());
2242
2243 assert_eq!(Bn::<u8, 8>::from_u8(3).unwrap().to_u32(), Some(3));
2244 assert_eq!(Bn::<u16, 4>::from_u8(3).unwrap().to_u32(), Some(3));
2245 assert_eq!(Bn::<u32, 2>::from_u8(3).unwrap().to_u32(), Some(3));
2246 assert_eq!(Bn::<u32, 2>::from_u64(3).unwrap().to_u32(), Some(3));
2247 assert_eq!(Bn::<u8, 8>::from_u64(255).unwrap().to_u32(), Some(255));
2248 assert_eq!(Bn::<u8, 8>::from_u64(256).unwrap().to_u32(), Some(256));
2249 assert_eq!(Bn::<u8, 8>::from_u64(65536).unwrap().to_u32(), Some(65536));
2250 }
2251 #[test]
2252 fn testfrom() {
2253 let mut n1 = Bn::<u8, 8>::new();
2254 n1.array[0] = 1;
2255 assert_eq!(Some(1), n1.to_u32());
2256 n1.array[1] = 1;
2257 assert_eq!(Some(257), n1.to_u32());
2258
2259 let mut n2 = Bn::<u16, 8>::new();
2260 n2.array[0] = 0xffff;
2261 assert_eq!(Some(65535), n2.to_u32());
2262 n2.array[0] = 0x0;
2263 n2.array[2] = 0x1;
2264 assert_eq!(None, n2.to_u32());
2266 assert_eq!(Some(0x100000000), n2.to_u64());
2267 }
2268
2269 #[test]
2270 fn test_from_str_bitlengths() {
2271 let test_s64 = "81906f5e4d3c2c01";
2272 let test_u64: u64 = 0x81906f5e4d3c2c01;
2273 let bb = Bn8::from_str_radix(test_s64, 16).unwrap();
2274 let cc = Bn8::from_u64(test_u64).unwrap();
2275 assert_eq!(cc.array, [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81]);
2276 assert_eq!(bb.array, [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81]);
2277 let dd = Bn16::from_u64(test_u64).unwrap();
2278 let ff = Bn16::from_str_radix(test_s64, 16).unwrap();
2279 assert_eq!(dd.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190]);
2280 assert_eq!(ff.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190]);
2281 let ee = Bn32::from_u64(test_u64).unwrap();
2282 let gg = Bn32::from_str_radix(test_s64, 16).unwrap();
2283 assert_eq!(ee.array, [0x4d3c2c01, 0x81906f5e]);
2284 assert_eq!(gg.array, [0x4d3c2c01, 0x81906f5e]);
2285 }
2286
2287 #[test]
2288 fn test_from_str_stringlengths() {
2289 let ab = Bn::<u8, 9>::from_str_radix("2281906f5e4d3c2c01", 16).unwrap();
2290 assert_eq!(
2291 ab.array,
2292 [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81, 0x22]
2293 );
2294 assert_eq!(
2295 [0x2c01, 0x4d3c, 0x6f5e, 0],
2296 Bn::<u16, 4>::from_str_radix("6f5e4d3c2c01", 16)
2297 .unwrap()
2298 .array
2299 );
2300 assert_eq!(
2301 [0x2c01, 0x4d3c, 0x6f5e, 0x190],
2302 Bn::<u16, 4>::from_str_radix("1906f5e4d3c2c01", 16)
2303 .unwrap()
2304 .array
2305 );
2306 assert_eq!(
2307 Err(make_overflow_err()),
2308 Bn::<u16, 4>::from_str_radix("f81906f5e4d3c2c01", 16)
2309 );
2310 assert_eq!(
2311 Err(make_overflow_err()),
2312 Bn::<u16, 4>::from_str_radix("af81906f5e4d3c2c01", 16)
2313 );
2314 assert_eq!(
2315 Err(make_overflow_err()),
2316 Bn::<u16, 4>::from_str_radix("baaf81906f5e4d3c2c01", 16)
2317 );
2318 let ac = Bn::<u16, 5>::from_str_radix("baaf81906f5e4d3c2c01", 16).unwrap();
2319 assert_eq!(ac.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190, 0xbaaf]);
2320 }
2321
2322 #[test]
2323 fn test_resize() {
2324 type TestInt1 = FixedUInt<u32, 1>;
2325 type TestInt2 = FixedUInt<u32, 2>;
2326
2327 let a = TestInt1::from(u32::MAX);
2328 let b: TestInt2 = a.resize();
2329 assert_eq!(b, TestInt2::from([u32::MAX, 0]));
2330
2331 let a = TestInt2::from([u32::MAX, u32::MAX]);
2332 let b: TestInt1 = a.resize();
2333 assert_eq!(b, TestInt1::from(u32::MAX));
2334 }
2335
2336 #[test]
2337 fn test_bit_length() {
2338 assert_eq!(0, Bn8::from_u8(0).unwrap().bit_length());
2339 assert_eq!(1, Bn8::from_u8(1).unwrap().bit_length());
2340 assert_eq!(2, Bn8::from_u8(2).unwrap().bit_length());
2341 assert_eq!(2, Bn8::from_u8(3).unwrap().bit_length());
2342 assert_eq!(7, Bn8::from_u8(0x70).unwrap().bit_length());
2343 assert_eq!(8, Bn8::from_u8(0xF0).unwrap().bit_length());
2344 assert_eq!(9, Bn8::from_u16(0x1F0).unwrap().bit_length());
2345
2346 assert_eq!(20, Bn8::from_u64(990223).unwrap().bit_length());
2347 assert_eq!(32, Bn8::from_u64(0xefffffff).unwrap().bit_length());
2348 assert_eq!(32, Bn8::from_u64(0x8fffffff).unwrap().bit_length());
2349 assert_eq!(31, Bn8::from_u64(0x7fffffff).unwrap().bit_length());
2350 assert_eq!(34, Bn8::from_u64(0x3ffffffff).unwrap().bit_length());
2351
2352 assert_eq!(0, Bn32::from_u8(0).unwrap().bit_length());
2353 assert_eq!(1, Bn32::from_u8(1).unwrap().bit_length());
2354 assert_eq!(2, Bn32::from_u8(2).unwrap().bit_length());
2355 assert_eq!(2, Bn32::from_u8(3).unwrap().bit_length());
2356 assert_eq!(7, Bn32::from_u8(0x70).unwrap().bit_length());
2357 assert_eq!(8, Bn32::from_u8(0xF0).unwrap().bit_length());
2358 assert_eq!(9, Bn32::from_u16(0x1F0).unwrap().bit_length());
2359
2360 assert_eq!(20, Bn32::from_u64(990223).unwrap().bit_length());
2361 assert_eq!(32, Bn32::from_u64(0xefffffff).unwrap().bit_length());
2362 assert_eq!(32, Bn32::from_u64(0x8fffffff).unwrap().bit_length());
2363 assert_eq!(31, Bn32::from_u64(0x7fffffff).unwrap().bit_length());
2364 assert_eq!(34, Bn32::from_u64(0x3ffffffff).unwrap().bit_length());
2365 }
2366
2367 #[test]
2368 fn test_bit_length_1000() {
2369 let value = Bn32::from_u16(1000).unwrap();
2371
2372 assert_eq!(value.to_u32().unwrap(), 1000);
2375 assert_eq!(value.bit_length(), 10);
2376
2377 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);
2384 assert_eq!(Bn16::from_u16(1000).unwrap().bit_length(), 10);
2385
2386 let value_from_str = Bn32::from_str_radix("1000", 10).unwrap();
2388 assert_eq!(value_from_str.bit_length(), 10);
2389
2390 let value_from_bytes = Bn32::from_le_bytes(&1000u16.to_le_bytes());
2392 assert_eq!(
2394 value_from_bytes.to_u32().unwrap_or(0),
2395 1000,
2396 "from_le_bytes didn't create the correct value"
2397 );
2398 assert_eq!(value_from_bytes.bit_length(), 10);
2399 }
2400 #[test]
2401 fn test_cmp() {
2402 let f0 = <Bn8 as Zero>::zero();
2403 let f1 = <Bn8 as Zero>::zero();
2404 let f2 = <Bn8 as One>::one();
2405 assert_eq!(f0, f1);
2406 assert!(f2 > f0);
2407 assert!(f0 < f2);
2408 let f3 = Bn32::from_u64(990223).unwrap();
2409 assert_eq!(f3, Bn32::from_u64(990223).unwrap());
2410 let f4 = Bn32::from_u64(990224).unwrap();
2411 assert!(f4 > Bn32::from_u64(990223).unwrap());
2412
2413 let f3 = Bn8::from_u64(990223).unwrap();
2414 assert_eq!(f3, Bn8::from_u64(990223).unwrap());
2415 let f4 = Bn8::from_u64(990224).unwrap();
2416 assert!(f4 > Bn8::from_u64(990223).unwrap());
2417
2418 #[cfg(feature = "nightly")]
2419 {
2420 use core::cmp::Ordering;
2421
2422 const A: FixedUInt<u8, 2> = FixedUInt::from_array([10, 0]);
2423 const B: FixedUInt<u8, 2> = FixedUInt::from_array([20, 0]);
2424 const C: FixedUInt<u8, 2> = FixedUInt::from_array([10, 0]);
2425
2426 const CMP_LT: Ordering = A.cmp(&B);
2427 const CMP_GT: Ordering = B.cmp(&A);
2428 const CMP_EQ: Ordering = A.cmp(&C);
2429 const EQ_TRUE: bool = A.eq(&C);
2430 const EQ_FALSE: bool = A.eq(&B);
2431
2432 assert_eq!(CMP_LT, Ordering::Less);
2433 assert_eq!(CMP_GT, Ordering::Greater);
2434 assert_eq!(CMP_EQ, Ordering::Equal);
2435 assert!(EQ_TRUE);
2436 assert!(!EQ_FALSE);
2437 }
2438 }
2439
2440 #[test]
2441 fn test_default() {
2442 let d: Bn8 = Default::default();
2443 assert!(<Bn8 as const_num_traits::Zero>::is_zero(&d));
2444
2445 #[cfg(feature = "nightly")]
2446 {
2447 const D: FixedUInt<u8, 2> = <FixedUInt<u8, 2> as Default>::default();
2448 assert!(<FixedUInt<u8, 2> as const_num_traits::Zero>::is_zero(&D));
2449 }
2450 }
2451
2452 #[test]
2453 fn test_clone() {
2454 let a: Bn8 = 42u8.into();
2455 let b = a;
2456 assert_eq!(a, b);
2457
2458 #[cfg(feature = "nightly")]
2459 {
2460 const A: FixedUInt<u8, 2> = FixedUInt::from_array([42, 0]);
2461 const B: FixedUInt<u8, 2> = A.clone();
2462 assert_eq!(A.array, B.array);
2463 }
2464 }
2465
2466 #[test]
2467 fn test_le_be_bytes() {
2468 let le_bytes = [1, 2, 3, 4];
2469 let be_bytes = [4, 3, 2, 1];
2470 let u8_ver = FixedUInt::<u8, 4>::from_le_bytes(&le_bytes);
2471 let u16_ver = FixedUInt::<u16, 2>::from_le_bytes(&le_bytes);
2472 let u32_ver = FixedUInt::<u32, 1>::from_le_bytes(&le_bytes);
2473 let u8_ver_be = FixedUInt::<u8, 4>::from_be_bytes(&be_bytes);
2474 let u16_ver_be = FixedUInt::<u16, 2>::from_be_bytes(&be_bytes);
2475 let u32_ver_be = FixedUInt::<u32, 1>::from_be_bytes(&be_bytes);
2476
2477 assert_eq!(u8_ver.array, [1, 2, 3, 4]);
2478 assert_eq!(u16_ver.array, [0x0201, 0x0403]);
2479 assert_eq!(u32_ver.array, [0x04030201]);
2480 assert_eq!(u8_ver_be.array, [1, 2, 3, 4]);
2481 assert_eq!(u16_ver_be.array, [0x0201, 0x0403]);
2482 assert_eq!(u32_ver_be.array, [0x04030201]);
2483
2484 let mut output_buffer = [0u8; 16];
2485 assert_eq!(u8_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
2486 assert_eq!(u8_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
2487 assert_eq!(u16_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
2488 assert_eq!(u16_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
2489 assert_eq!(u32_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
2490 assert_eq!(u32_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
2491 }
2492
2493 #[test]
2495 fn test_div_small() {
2496 type TestInt = FixedUInt<u8, 2>;
2497
2498 let test_cases = [
2500 (20u16, 3u16, 6u16), (100u16, 7u16, 14u16), (255u16, 5u16, 51u16), (65535u16, 256u16, 255u16), ];
2505
2506 for (dividend_val, divisor_val, expected) in test_cases {
2507 let dividend = TestInt::from(dividend_val);
2508 let divisor = TestInt::from(divisor_val);
2509 let expected_result = TestInt::from(expected);
2510
2511 assert_eq!(
2512 dividend / divisor,
2513 expected_result,
2514 "Division failed for {} / {} = {}",
2515 dividend_val,
2516 divisor_val,
2517 expected
2518 );
2519 }
2520 }
2521
2522 #[test]
2523 fn test_div_edge_cases() {
2524 type TestInt = FixedUInt<u16, 2>;
2525
2526 let dividend = TestInt::from(1000u16);
2528 let divisor = TestInt::from(1u16);
2529 assert_eq!(dividend / divisor, TestInt::from(1000u16));
2530
2531 let dividend = TestInt::from(42u16);
2533 let divisor = TestInt::from(42u16);
2534 assert_eq!(dividend / divisor, TestInt::from(1u16));
2535
2536 let dividend = TestInt::from(5u16);
2538 let divisor = TestInt::from(10u16);
2539 assert_eq!(dividend / divisor, TestInt::from(0u16));
2540
2541 let dividend = TestInt::from(1024u16);
2543 let divisor = TestInt::from(4u16);
2544 assert_eq!(dividend / divisor, TestInt::from(256u16));
2545 }
2546
2547 #[test]
2548 fn test_helper_methods() {
2549 type TestInt = FixedUInt<u8, 2>;
2550
2551 let mut val = <TestInt as Zero>::zero();
2553 const_set_bit(&mut val.array, 0);
2554 assert_eq!(val, TestInt::from(1u8));
2555
2556 const_set_bit(&mut val.array, 8);
2557 assert_eq!(val, TestInt::from(257u16)); let a = TestInt::from(8u8); let b = TestInt::from(1u8); assert_eq!(
2565 const_cmp_shifted(&a.array, &b.array, 3),
2566 core::cmp::Ordering::Equal
2567 );
2568
2569 assert_eq!(
2571 const_cmp_shifted(&a.array, &b.array, 2),
2572 core::cmp::Ordering::Greater
2573 );
2574
2575 assert_eq!(
2577 const_cmp_shifted(&a.array, &b.array, 4),
2578 core::cmp::Ordering::Less
2579 );
2580
2581 let mut val = TestInt::from(10u8);
2583 let one = TestInt::from(1u8);
2584 const_sub_shifted(&mut val.array, &one.array, 2); assert_eq!(val, TestInt::from(6u8)); }
2587
2588 #[test]
2589 fn test_shifted_operations_comprehensive() {
2590 type TestInt = FixedUInt<u32, 2>;
2591
2592 let a = TestInt::from(0x12345678u32);
2594 let b = TestInt::from(0x12345678u32);
2595
2596 assert_eq!(
2598 const_cmp_shifted(&a.array, &b.array, 0),
2599 core::cmp::Ordering::Equal
2600 );
2601
2602 let c = TestInt::from(0x123u32); let d = TestInt::from(0x48d159e2u32); assert_eq!(
2608 const_cmp_shifted(&d.array, &c.array, 16),
2609 core::cmp::Ordering::Greater
2610 );
2611
2612 let e = TestInt::from(1u32);
2614 let zero = TestInt::from(0u32);
2615 assert_eq!(
2616 const_cmp_shifted(&e.array, &zero.array, 100),
2617 core::cmp::Ordering::Greater
2618 );
2619 assert_eq!(
2621 const_cmp_shifted(&zero.array, &e.array, 100),
2622 core::cmp::Ordering::Equal
2623 );
2624
2625 let mut val = TestInt::from(0x10000u32); let one = TestInt::from(1u32);
2628 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)); }
2636
2637 #[test]
2638 fn test_shifted_operations_edge_cases() {
2639 type TestInt = FixedUInt<u32, 2>;
2640
2641 let a = TestInt::from(42u32);
2643 let a2 = TestInt::from(42u32);
2644 assert_eq!(
2645 const_cmp_shifted(&a.array, &a2.array, 0),
2646 core::cmp::Ordering::Equal
2647 );
2648
2649 let mut b = TestInt::from(42u32);
2650 let ten = TestInt::from(10u32);
2651 const_sub_shifted(&mut b.array, &ten.array, 0);
2652 assert_eq!(b, TestInt::from(32u32));
2653
2654 let c = TestInt::from(123u32);
2656 let large = TestInt::from(456u32);
2657 assert_eq!(
2658 const_cmp_shifted(&c.array, &large.array, 200),
2659 core::cmp::Ordering::Greater
2660 );
2661
2662 let mut d = TestInt::from(123u32);
2663 const_sub_shifted(&mut d.array, &large.array, 200); assert_eq!(d, TestInt::from(123u32));
2665
2666 let zero = TestInt::from(0u32);
2668 let one = TestInt::from(1u32);
2669 assert_eq!(
2670 const_cmp_shifted(&zero.array, &zero.array, 10),
2671 core::cmp::Ordering::Equal
2672 );
2673 assert_eq!(
2674 const_cmp_shifted(&one.array, &zero.array, 10),
2675 core::cmp::Ordering::Greater
2676 );
2677 }
2678
2679 #[test]
2680 fn test_shifted_operations_equivalence() {
2681 type TestInt = FixedUInt<u32, 2>;
2682
2683 let test_cases = [
2685 (0x12345u32, 0x678u32, 4),
2686 (0x1000u32, 0x10u32, 8),
2687 (0xABCDu32, 0x1u32, 16),
2688 (0x80000000u32, 0x1u32, 1),
2689 ];
2690
2691 for (a_val, b_val, shift) in test_cases {
2692 let a = TestInt::from(a_val);
2693 let b = TestInt::from(b_val);
2694
2695 let optimized_cmp = const_cmp_shifted(&a.array, &b.array, shift);
2697 let naive_cmp = a.cmp(&(b << shift));
2698 assert_eq!(
2699 optimized_cmp, naive_cmp,
2700 "cmp_shifted mismatch: {} vs ({} << {})",
2701 a_val, b_val, shift
2702 );
2703
2704 if a >= (b << shift) {
2706 let mut optimized_result = a;
2707 const_sub_shifted(&mut optimized_result.array, &b.array, shift);
2708
2709 let naive_result = a - (b << shift);
2710 assert_eq!(
2711 optimized_result, naive_result,
2712 "sub_shifted mismatch: {} - ({} << {})",
2713 a_val, b_val, shift
2714 );
2715 }
2716 }
2717 }
2718
2719 #[test]
2720 fn test_div_assign_in_place_optimization() {
2721 type TestInt = FixedUInt<u32, 2>;
2722
2723 let test_cases = [
2725 (100u32, 10u32, 10u32, 0u32), (123u32, 7u32, 17u32, 4u32), (1000u32, 13u32, 76u32, 12u32), (65535u32, 255u32, 257u32, 0u32), ];
2730
2731 for (dividend_val, divisor_val, expected_quotient, expected_remainder) in test_cases {
2732 let mut dividend = TestInt::from(dividend_val);
2734 let divisor = TestInt::from(divisor_val);
2735
2736 dividend /= divisor;
2737 assert_eq!(
2738 dividend,
2739 TestInt::from(expected_quotient),
2740 "div_assign: {} / {} should be {}",
2741 dividend_val,
2742 divisor_val,
2743 expected_quotient
2744 );
2745
2746 let dividend2 = TestInt::from(dividend_val);
2748 let (quotient, remainder) = dividend2.div_rem(&divisor);
2749 assert_eq!(
2750 quotient,
2751 TestInt::from(expected_quotient),
2752 "div_rem quotient: {} / {} should be {}",
2753 dividend_val,
2754 divisor_val,
2755 expected_quotient
2756 );
2757 assert_eq!(
2758 remainder,
2759 TestInt::from(expected_remainder),
2760 "div_rem remainder: {} % {} should be {}",
2761 dividend_val,
2762 divisor_val,
2763 expected_remainder
2764 );
2765
2766 assert_eq!(
2768 quotient * divisor + remainder,
2769 TestInt::from(dividend_val),
2770 "Property check failed for {}",
2771 dividend_val
2772 );
2773 }
2774 }
2775
2776 #[test]
2777 fn test_div_assign_stack_efficiency() {
2778 type TestInt = FixedUInt<u32, 4>; let mut dividend = TestInt::from(0x123456789ABCDEFu64);
2782 let divisor = TestInt::from(0x12345u32);
2783 let original_dividend = dividend;
2784
2785 dividend /= divisor;
2787
2788 let remainder = original_dividend % divisor;
2790 assert_eq!(dividend * divisor + remainder, original_dividend);
2791 }
2792
2793 #[test]
2794 fn test_rem_assign_optimization() {
2795 type TestInt = FixedUInt<u32, 2>;
2796
2797 let test_cases = [
2798 (100u32, 10u32, 0u32), (123u32, 7u32, 4u32), (1000u32, 13u32, 12u32), (65535u32, 255u32, 0u32), ];
2803
2804 for (dividend_val, divisor_val, expected_remainder) in test_cases {
2805 let mut dividend = TestInt::from(dividend_val);
2806 let divisor = TestInt::from(divisor_val);
2807
2808 dividend %= divisor;
2809 assert_eq!(
2810 dividend,
2811 TestInt::from(expected_remainder),
2812 "rem_assign: {} % {} should be {}",
2813 dividend_val,
2814 divisor_val,
2815 expected_remainder
2816 );
2817 }
2818 }
2819
2820 #[test]
2821 fn test_div_with_remainder_property() {
2822 type TestInt = FixedUInt<u32, 2>;
2823
2824 let test_cases = [
2826 (100u32, 10u32, 10u32), (123u32, 7u32, 17u32), (1000u32, 13u32, 76u32), (65535u32, 255u32, 257u32), ];
2831
2832 for (dividend_val, divisor_val, expected_quotient) in test_cases {
2833 let dividend = TestInt::from(dividend_val);
2834 let divisor = TestInt::from(divisor_val);
2835
2836 let quotient = dividend / divisor;
2838 assert_eq!(
2839 quotient,
2840 TestInt::from(expected_quotient),
2841 "Division: {} / {} should be {}",
2842 dividend_val,
2843 divisor_val,
2844 expected_quotient
2845 );
2846
2847 let remainder = dividend % divisor;
2849 assert_eq!(
2850 quotient * divisor + remainder,
2851 dividend,
2852 "Division property check failed for {}",
2853 dividend_val
2854 );
2855 }
2856 }
2857
2858 #[test]
2859 fn test_code_simplification_benefits() {
2860 type TestInt = FixedUInt<u32, 2>;
2861
2862 let dividend = TestInt::from(12345u32);
2864 let divisor = TestInt::from(67u32);
2865 let quotient = dividend / divisor;
2866 let remainder = dividend % divisor;
2867
2868 assert_eq!(quotient * divisor + remainder, dividend);
2870 }
2871
2872 #[test]
2873 fn test_rem_assign_correctness_after_fix() {
2874 type TestInt = FixedUInt<u32, 2>;
2875
2876 let mut a = TestInt::from(17u32);
2878 let b = TestInt::from(5u32);
2879
2880 a %= b;
2883 assert_eq!(a, TestInt::from(2u32), "17 % 5 should be 2");
2884
2885 let mut test_val = TestInt::from(100u32);
2887 test_val %= TestInt::from(7u32);
2888 assert_eq!(
2889 test_val,
2890 TestInt::from(2u32),
2891 "100 % 7 should be 2 (not 14, the quotient)"
2892 );
2893 }
2894
2895 #[test]
2896 fn test_div_property_based() {
2897 type TestInt = FixedUInt<u16, 2>;
2898
2899 let test_pairs = [
2901 (12345u16, 67u16),
2902 (1000u16, 13u16),
2903 (65535u16, 255u16),
2904 (5000u16, 7u16),
2905 ];
2906
2907 for (dividend_val, divisor_val) in test_pairs {
2908 let dividend = TestInt::from(dividend_val);
2909 let divisor = TestInt::from(divisor_val);
2910
2911 let quotient = dividend / divisor;
2912
2913 let remainder = dividend - (quotient * divisor);
2915 let reconstructed = quotient * divisor + remainder;
2916
2917 assert_eq!(
2918 reconstructed,
2919 dividend,
2920 "Property failed for {} / {}: {} * {} + {} != {}",
2921 dividend_val,
2922 divisor_val,
2923 quotient.to_u32().unwrap_or(0),
2924 divisor_val,
2925 remainder.to_u32().unwrap_or(0),
2926 dividend_val
2927 );
2928
2929 assert!(
2931 remainder < divisor,
2932 "Remainder {} >= divisor {} for {} / {}",
2933 remainder.to_u32().unwrap_or(0),
2934 divisor_val,
2935 dividend_val,
2936 divisor_val
2937 );
2938 }
2939 }
2940}