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