1use num_traits::{ToPrimitive, Zero};
16
17use core::convert::TryFrom;
18use core::fmt::Write;
19
20pub use crate::const_numtraits::{
21 ConstAbsDiff, ConstBitPrimInt, ConstBorrowingSub, ConstBounded, ConstCarryingAdd,
22 ConstCarryingMul, ConstCheckedPow, ConstDivCeil, ConstIlog, ConstIsqrt, ConstMultiple,
23 ConstOne, ConstPowerOfTwo, ConstPrimInt, ConstWideningMul, ConstZero,
24};
25use crate::machineword::{ConstMachineWord, MachineWord};
26
27#[allow(unused_imports)]
28use num_traits::{FromPrimitive, Num};
29
30mod abs_diff_impl;
31mod add_sub_impl;
32mod bit_ops_impl;
33mod checked_pow_impl;
34mod div_ceil_impl;
35mod euclid;
36mod extended_precision_impl;
37mod ilog_impl;
38mod isqrt_impl;
39mod iter_impl;
40mod midpoint_impl;
41mod mul_acc_ops_impl;
42mod mul_div_impl;
43mod multiple_impl;
44mod num_integer_impl;
45mod num_traits_casts;
46mod num_traits_identity;
47mod power_of_two_impl;
48mod prim_int_impl;
49mod roots_impl;
50mod string_conversion;
51#[cfg(feature = "nightly")]
53mod const_to_from_bytes;
54#[cfg(any(feature = "nightly", feature = "use-unsafe"))]
56mod to_from_bytes;
57
58use crate::personality::{Ct, Nct, Personality, PersonalityMarker, PersonalityTag};
59#[cfg(feature = "zeroize")]
60use zeroize::DefaultIsZeroes;
61
62#[derive(Copy)]
72pub struct FixedUInt<T, const N: usize, P: Personality = Nct>
73where
74 T: MachineWord,
75{
76 pub(super) array: [T; N],
78 pub(super) _p: PersonalityMarker<P>,
80}
81
82impl<T: MachineWord + core::fmt::Debug, const N: usize> core::fmt::Debug for FixedUInt<T, N, Nct> {
87 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
88 f.debug_struct("FixedUInt")
89 .field("array", &self.array)
90 .finish()
91 }
92}
93
94impl<T: MachineWord, const N: usize> core::fmt::Debug for FixedUInt<T, N, Ct> {
95 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
96 f.write_str("FixedUInt<…>")
97 }
98}
99
100#[cfg(feature = "zeroize")]
101impl<T: MachineWord, const N: usize, P: Personality> DefaultIsZeroes for FixedUInt<T, N, P> {}
102
103impl<T, const N: usize, P: Personality> From<[T; N]> for FixedUInt<T, N, P>
104where
105 T: MachineWord,
106{
107 fn from(array: [T; N]) -> Self {
108 Self {
109 array,
110 _p: core::marker::PhantomData,
111 }
112 }
113}
114
115impl<T: MachineWord, const N: usize, P: Personality> FixedUInt<T, N, P> {
118 pub(crate) const fn from_array(array: [T; N]) -> Self {
119 Self {
120 array,
121 _p: core::marker::PhantomData,
122 }
123 }
124}
125
126impl<T: MachineWord, const N: usize> From<FixedUInt<T, N, Nct>> for FixedUInt<T, N, Ct> {
134 fn from(v: FixedUInt<T, N, Nct>) -> Self {
135 FixedUInt::from_array(v.array)
136 }
137}
138
139impl<T: MachineWord, const N: usize> FixedUInt<T, N, Ct> {
140 pub const fn forget_ct(self) -> FixedUInt<T, N, Nct> {
147 FixedUInt::from_array(self.array)
148 }
149}
150
151impl<T: MachineWord + subtle::ConditionallySelectable, const N: usize> FixedUInt<T, N, Ct> {
152 pub fn ct_checked_add(&self, other: &Self) -> subtle::CtOption<Self> {
158 let (res, overflow) =
159 <Self as crate::const_numtraits::ConstOverflowingAdd>::overflowing_add(self, other);
160 let valid = subtle::Choice::from((!overflow) as u8);
161 subtle::CtOption::new(res, valid)
162 }
163
164 pub fn ct_checked_sub(&self, other: &Self) -> subtle::CtOption<Self> {
166 let (res, overflow) =
167 <Self as crate::const_numtraits::ConstOverflowingSub>::overflowing_sub(self, other);
168 let valid = subtle::Choice::from((!overflow) as u8);
169 subtle::CtOption::new(res, valid)
170 }
171
172 pub fn ct_checked_mul(&self, other: &Self) -> subtle::CtOption<Self> {
174 let (res, overflow) =
175 <Self as crate::const_numtraits::ConstOverflowingMul>::overflowing_mul(self, other);
176 let valid = subtle::Choice::from((!overflow) as u8);
177 subtle::CtOption::new(res, valid)
178 }
179
180 pub fn ct_checked_shl(&self, bits: u32) -> subtle::CtOption<Self> {
182 let (res, overflow) =
183 <Self as crate::const_numtraits::ConstOverflowingShl>::overflowing_shl(self, bits);
184 let valid = subtle::Choice::from((!overflow) as u8);
185 subtle::CtOption::new(res, valid)
186 }
187
188 pub fn ct_checked_shr(&self, bits: u32) -> subtle::CtOption<Self> {
190 let (res, overflow) =
191 <Self as crate::const_numtraits::ConstOverflowingShr>::overflowing_shr(self, bits);
192 let valid = subtle::Choice::from((!overflow) as u8);
193 subtle::CtOption::new(res, valid)
194 }
195
196 pub fn ct_checked_next_power_of_two(self) -> subtle::CtOption<Self>
198 where
199 T: subtle::ConstantTimeEq,
200 {
201 let one = <Self as num_traits::One>::one();
202 let m_one = <Self as crate::const_numtraits::ConstWrappingSub>::wrapping_sub(&self, &one);
203 let leading = <Self as crate::const_numtraits::ConstBitPrimInt>::leading_zeros(m_one);
204 let bits = Self::BIT_SIZE as u32 - leading;
205 let shifted = one << (bits as usize);
206 let is_zero_choice =
207 <Self as subtle::ConstantTimeEq>::ct_eq(&self, &<Self as num_traits::Zero>::zero());
208 let result = <Self as subtle::ConditionallySelectable>::conditional_select(
210 &shifted,
211 &one,
212 is_zero_choice,
213 );
214 let overflow = (bits >= Self::BIT_SIZE as u32) as u8;
217 let valid_otherwise = subtle::Choice::from(1u8 ^ overflow);
218 let valid = <subtle::Choice as subtle::ConditionallySelectable>::conditional_select(
219 &valid_otherwise,
220 &subtle::Choice::from(1u8),
221 is_zero_choice,
222 );
223 subtle::CtOption::new(result, valid)
224 }
225
226 pub fn ct_checked_pow(self, exp: u32) -> subtle::CtOption<Self>
227 where
228 T: subtle::ConstantTimeEq + subtle::ConstantTimeGreater,
229 for<'a> &'a Self: core::ops::Mul<&'a Self, Output = Self>,
230 {
231 use num_traits::ops::overflowing::OverflowingMul;
232 let mut result = <Self as num_traits::One>::one();
233 let mut base = self;
234 let mut e = exp;
235 let mut any_overflow: u8 = 0;
236 for _ in 0..u32::BITS {
237 let bit = (e & 1) as u8;
238 let (candidate, mul_ov) = OverflowingMul::overflowing_mul(&result, &base);
239 any_overflow |= (mul_ov as u8) & bit;
241 let bit_t = <T as core::convert::From<u8>>::from(bit);
243 let mask = bit_t * <T as crate::const_numtraits::ConstBounded>::max_value();
244 for i in 0..N {
245 let diff = result.array[i] ^ candidate.array[i];
246 result.array[i] ^= mask & diff;
247 }
248 e >>= 1;
249 let (new_base, base_ov) = OverflowingMul::overflowing_mul(&base, &base);
250 let any_remaining: u8 = (e != 0) as u8;
252 any_overflow |= (base_ov as u8) & any_remaining;
253 base = new_base;
254 }
255 let valid = subtle::Choice::from(1u8 ^ any_overflow);
256 subtle::CtOption::new(result, valid)
257 }
258}
259
260impl<T: MachineWord + subtle::ConstantTimeEq, const N: usize> subtle::ConstantTimeEq
265 for FixedUInt<T, N, Ct>
266{
267 fn ct_eq(&self, other: &Self) -> subtle::Choice {
268 <[T] as subtle::ConstantTimeEq>::ct_eq(self.array.as_slice(), other.array.as_slice())
269 }
270}
271
272impl<T: MachineWord + subtle::ConditionallySelectable, const N: usize>
273 subtle::ConditionallySelectable for FixedUInt<T, N, Ct>
274{
275 fn conditional_select(a: &Self, b: &Self, choice: subtle::Choice) -> Self {
276 let mut array = a.array;
277 let mut i = 0;
278 while i < N {
279 array[i] = T::conditional_select(&a.array[i], &b.array[i], choice);
280 i += 1;
281 }
282 FixedUInt::from_array(array)
283 }
284}
285
286impl<T: MachineWord + subtle::ConstantTimeEq + subtle::ConstantTimeGreater, const N: usize>
293 subtle::ConstantTimeGreater for FixedUInt<T, N, Ct>
294{
295 fn ct_gt(&self, other: &Self) -> subtle::Choice {
296 let mut gt = subtle::Choice::from(0u8);
297 let mut undecided = subtle::Choice::from(1u8);
298 let mut i = N;
299 while i > 0 {
300 i -= 1;
301 let gt_here = self.array[i].ct_gt(&other.array[i]);
302 let eq_here = self.array[i].ct_eq(&other.array[i]);
303 gt |= undecided & gt_here;
304 undecided &= eq_here;
305 }
306 gt
307 }
308}
309
310impl<T: MachineWord + subtle::ConstantTimeEq + subtle::ConstantTimeGreater, const N: usize>
311 subtle::ConstantTimeLess for FixedUInt<T, N, Ct>
312{
313}
314
315const LONGEST_WORD_IN_BITS: usize = 128;
316
317impl<T: MachineWord, const N: usize, P: Personality> FixedUInt<T, N, P> {
318 const WORD_SIZE: usize = core::mem::size_of::<T>();
319 const WORD_BITS: usize = Self::WORD_SIZE * 8;
320 const BYTE_SIZE: usize = Self::WORD_SIZE * N;
321 const BIT_SIZE: usize = Self::BYTE_SIZE * 8;
322
323 pub fn new() -> FixedUInt<T, N, P> {
325 FixedUInt::from_array([T::zero(); N])
326 }
327
328 pub fn words(&self) -> &[T; N] {
330 &self.array
331 }
332
333 pub fn bit_length(&self) -> u32 {
335 Self::BIT_SIZE as u32 - ConstBitPrimInt::leading_zeros(*self)
336 }
337}
338
339impl<T: MachineWord, const N: usize> FixedUInt<T, N, Nct> {
340 pub fn div_rem(&self, divisor: &Self) -> (Self, Self) {
342 let (quotient, remainder) = const_div_rem(&self.array, &divisor.array);
343 (Self::from_array(quotient), Self::from_array(remainder))
344 }
345
346 pub fn to_radix_str<'a>(
348 &self,
349 result: &'a mut [u8],
350 radix: u8,
351 ) -> Result<&'a str, core::fmt::Error> {
352 type Error = core::fmt::Error;
353
354 if !(2..=16).contains(&radix) {
355 return Err(Error {}); }
357 for byte in result.iter_mut() {
358 *byte = b'0';
359 }
360 if Zero::is_zero(self) {
361 if !result.is_empty() {
362 result[0] = b'0';
363 return core::str::from_utf8(&result[0..1]).map_err(|_| Error {});
364 } else {
365 return Err(Error {});
366 }
367 }
368
369 let mut number = *self;
370 let mut idx = result.len();
371
372 let radix_t = Self::from(radix);
373
374 while !Zero::is_zero(&number) {
375 if idx == 0 {
376 return Err(Error {}); }
378
379 idx -= 1;
380 let (quotient, remainder) = number.div_rem(&radix_t);
381
382 let digit = remainder.to_u8().unwrap();
383 result[idx] = match digit {
384 0..=9 => b'0' + digit, 10..=16 => b'a' + (digit - 10), _ => return Err(Error {}),
387 };
388
389 number = quotient;
390 }
391
392 let start = result[idx..].iter().position(|&c| c != b'0').unwrap_or(0);
393 let radix_str = core::str::from_utf8(&result[idx + start..]).map_err(|_| Error {})?;
394 Ok(radix_str)
395 }
396}
397
398c0nst::c0nst! {
400 pub(crate) c0nst fn impl_from_le_bytes_slice<T: [c0nst] ConstMachineWord, const N: usize>(
403 bytes: &[u8],
404 ) -> [T; N] {
405 let word_size = core::mem::size_of::<T>();
406 let mut ret: [T; N] = [T::zero(); N];
407 let capacity = N * word_size;
408 let total_bytes = if bytes.len() < capacity { bytes.len() } else { capacity };
409
410 let mut byte_index = 0;
411 while byte_index < total_bytes {
412 let word_index = byte_index / word_size;
413 let byte_in_word = byte_index % word_size;
414
415 let byte_value: T = T::from(bytes[byte_index]);
416 let shifted_value = byte_value.shl(byte_in_word * 8);
417 ret[word_index] = ret[word_index].bitor(shifted_value);
418 byte_index += 1;
419 }
420 ret
421 }
422
423 pub(crate) c0nst fn impl_from_be_bytes_slice<T: [c0nst] ConstMachineWord, const N: usize>(
426 bytes: &[u8],
427 ) -> [T; N] {
428 let word_size = core::mem::size_of::<T>();
429 let mut ret: [T; N] = [T::zero(); N];
430 let capacity_bytes = N * word_size;
431 let total_bytes = if bytes.len() < capacity_bytes { bytes.len() } else { capacity_bytes };
432
433 let start_offset = if bytes.len() > capacity_bytes {
436 bytes.len() - capacity_bytes
437 } else {
438 0
439 };
440
441 let mut byte_index = 0;
442 while byte_index < total_bytes {
443 let be_byte_index = start_offset + total_bytes - 1 - byte_index;
445 let word_index = byte_index / word_size;
446 let byte_in_word = byte_index % word_size;
447
448 let byte_value: T = T::from(bytes[be_byte_index]);
449 let shifted_value = byte_value.shl(byte_in_word * 8);
450 ret[word_index] = ret[word_index].bitor(shifted_value);
451 byte_index += 1;
452 }
453 ret
454 }
455}
456
457impl<T: MachineWord, const N: usize, P: Personality> FixedUInt<T, N, P> {
459 pub fn from_le_bytes(bytes: &[u8]) -> Self {
461 Self::from_array(impl_from_le_bytes_slice::<T, N>(bytes))
462 }
463
464 pub fn from_be_bytes(bytes: &[u8]) -> Self {
466 Self::from_array(impl_from_be_bytes_slice::<T, N>(bytes))
467 }
468}
469
470impl<T: MachineWord, const N: usize, P: Personality> FixedUInt<T, N, P> {
471 pub fn to_le_bytes<'a>(&self, output_buffer: &'a mut [u8]) -> Result<&'a [u8], bool> {
473 let total_bytes = N * Self::WORD_SIZE;
474 if output_buffer.len() < total_bytes {
475 return Err(false); }
477 for (i, word) in self.array.iter().enumerate() {
478 let start = i * Self::WORD_SIZE;
479 let end = start + Self::WORD_SIZE;
480 let word_bytes = word.to_le_bytes();
481 output_buffer[start..end].copy_from_slice(word_bytes.as_ref());
482 }
483 Ok(&output_buffer[..total_bytes])
484 }
485
486 pub fn to_be_bytes<'a>(&self, output_buffer: &'a mut [u8]) -> Result<&'a [u8], bool> {
488 let total_bytes = N * Self::WORD_SIZE;
489 if output_buffer.len() < total_bytes {
490 return Err(false); }
492 for (i, word) in self.array.iter().rev().enumerate() {
493 let start = i * Self::WORD_SIZE;
494 let end = start + Self::WORD_SIZE;
495 let word_bytes = word.to_be_bytes();
496 output_buffer[start..end].copy_from_slice(word_bytes.as_ref());
497 }
498 Ok(&output_buffer[..total_bytes])
499 }
500
501 pub fn to_hex_str<'a>(&self, result: &'a mut [u8]) -> Result<&'a str, core::fmt::Error> {
503 type Error = core::fmt::Error;
504
505 let word_size = Self::WORD_SIZE;
506 let need_bits = self.bit_length() as usize;
508 let need_chars = if need_bits > 0 { need_bits / 4 } else { 0 };
510
511 if result.len() < need_chars {
512 return Err(Error {});
514 }
515 let offset = result.len() - need_chars;
516 for i in result.iter_mut() {
517 *i = b'0';
518 }
519
520 for iter_words in 0..self.array.len() {
521 let word = self.array[iter_words];
522 let mut encoded = [0u8; LONGEST_WORD_IN_BITS / 4];
523 let encode_slice = &mut encoded[0..word_size * 2];
524 let mut wordbytes = word.to_le_bytes();
525 wordbytes.as_mut().reverse();
526 let wordslice = wordbytes.as_ref();
527 to_slice_hex(wordslice, encode_slice).map_err(|_| Error {})?;
528 for iter_chars in 0..encode_slice.len() {
529 let copy_char_to = (iter_words * word_size * 2) + iter_chars;
530 if copy_char_to <= need_chars {
531 let reverse_index = offset + (need_chars - copy_char_to);
532 if reverse_index <= result.len() && reverse_index > 0 {
533 let current_char = encode_slice[(encode_slice.len() - 1) - iter_chars];
534 result[reverse_index - 1] = current_char;
535 }
536 }
537 }
538 }
539
540 let convert = core::str::from_utf8(result).map_err(|_| Error {})?;
541 let pos = convert.find(|c: char| c != '0');
542 match pos {
543 Some(x) => Ok(&convert[x..convert.len()]),
544 None => {
545 if convert.starts_with('0') {
546 Ok("0")
547 } else {
548 Ok(convert)
549 }
550 }
551 }
552 }
553
554 #[must_use]
559 pub fn resize<const N2: usize>(&self) -> FixedUInt<T, N2, P> {
560 let mut array = [T::zero(); N2];
561 let min_size = N.min(N2);
562 array[..min_size].copy_from_slice(&self.array[..min_size]);
563 FixedUInt::<T, N2, P>::from_array(array)
564 }
565
566 fn hex_fmt(
567 &self,
568 formatter: &mut core::fmt::Formatter<'_>,
569 uppercase: bool,
570 ) -> Result<(), core::fmt::Error>
571 where
572 u8: core::convert::TryFrom<T>,
573 {
574 type Err = core::fmt::Error;
575
576 fn to_casedigit(byte: u8, uppercase: bool) -> Result<char, core::fmt::Error> {
577 let digit = core::char::from_digit(byte as u32, 16).ok_or(Err {})?;
578 if uppercase {
579 digit.to_uppercase().next().ok_or(Err {})
580 } else {
581 digit.to_lowercase().next().ok_or(Err {})
582 }
583 }
584
585 let mut leading_zero: bool = true;
586
587 let mut maybe_write = |nibble: char| -> Result<(), core::fmt::Error> {
588 leading_zero &= nibble == '0';
589 if !leading_zero {
590 formatter.write_char(nibble)?;
591 }
592 Ok(())
593 };
594
595 for index in (0..N).rev() {
596 let val = self.array[index];
597 let mask: T = 0xff.into();
598 for j in (0..Self::WORD_SIZE as u32).rev() {
599 let masked = val & mask.shl((j * 8) as usize);
600
601 let byte = u8::try_from(masked.shr((j * 8) as usize)).map_err(|_| Err {})?;
602
603 maybe_write(to_casedigit((byte & 0xf0) >> 4, uppercase)?)?;
604 maybe_write(to_casedigit(byte & 0x0f, uppercase)?)?;
605 }
606 }
607 Ok(())
608 }
609}
610
611c0nst::c0nst! {
612 pub(crate) c0nst fn add_with_carry<T: [c0nst] ConstMachineWord, const N: usize>(
617 a: &[T; N],
618 b: &[T; N],
619 carry_in: bool,
620 ) -> ([T; N], bool) {
621 let mut result = [T::zero(); N];
622 let mut carry = carry_in;
623 let mut i = 0usize;
624 while i < N {
625 let (sum, c) = ConstCarryingAdd::carrying_add(a[i], b[i], carry);
626 result[i] = sum;
627 carry = c;
628 i += 1;
629 }
630 (result, carry)
631 }
632
633 pub(crate) c0nst fn sub_with_borrow<T: [c0nst] ConstMachineWord, const N: usize>(
635 a: &[T; N],
636 b: &[T; N],
637 borrow_in: bool,
638 ) -> ([T; N], bool) {
639 let mut result = [T::zero(); N];
640 let mut borrow = borrow_in;
641 let mut i = 0usize;
642 while i < N {
643 let (diff, br) = ConstBorrowingSub::borrowing_sub(a[i], b[i], borrow);
644 result[i] = diff;
645 borrow = br;
646 i += 1;
647 }
648 (result, borrow)
649 }
650
651 pub(crate) c0nst fn add_impl<T: [c0nst] ConstMachineWord, const N: usize>(
656 target: &mut [T; N],
657 other: &[T; N]
658 ) -> bool {
659 let mut carry = false;
660 let mut i = 0usize;
661 while i < N {
662 let (sum, c) = ConstCarryingAdd::carrying_add(target[i], other[i], carry);
663 target[i] = sum;
664 carry = c;
665 i += 1;
666 }
667 carry
668 }
669
670 pub(crate) c0nst fn sub_impl<T: [c0nst] ConstMachineWord, const N: usize>(
672 target: &mut [T; N],
673 other: &[T; N]
674 ) -> bool {
675 let mut borrow = false;
676 let mut i = 0usize;
677 while i < N {
678 let (diff, br) = ConstBorrowingSub::borrowing_sub(target[i], other[i], borrow);
679 target[i] = diff;
680 borrow = br;
681 i += 1;
682 }
683 borrow
684 }
685}
686
687c0nst::c0nst! {
688 pub(crate) c0nst fn const_shl_impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
690 target: &mut FixedUInt<T, N, P>,
691 bits: usize,
692 ) {
693 if N == 0 {
694 return;
695 }
696 let word_bits = FixedUInt::<T, N>::WORD_BITS;
697 let nwords = bits / word_bits;
698 let nbits = bits - nwords * word_bits;
699
700 if nwords >= N {
702 let mut i = 0;
703 while i < N {
704 target.array[i] = T::zero();
705 i += 1;
706 }
707 return;
708 }
709
710 let mut i = N;
712 while i > nwords {
713 i -= 1;
714 target.array[i] = target.array[i - nwords];
715 }
716 let mut i = 0;
718 while i < nwords {
719 target.array[i] = T::zero();
720 i += 1;
721 }
722
723 if nbits != 0 {
724 let mut i = N;
726 while i > 1 {
727 i -= 1;
728 let right = target.array[i] << nbits;
729 let left = target.array[i - 1] >> (word_bits - nbits);
730 target.array[i] = right | left;
731 }
732 target.array[0] <<= nbits;
733 }
734 }
735
736 pub(crate) c0nst fn const_shr_impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
738 target: &mut FixedUInt<T, N, P>,
739 bits: usize,
740 ) {
741 if N == 0 {
742 return;
743 }
744 let word_bits = FixedUInt::<T, N>::WORD_BITS;
745 let nwords = bits / word_bits;
746 let nbits = bits - nwords * word_bits;
747
748 if nwords >= N {
750 let mut i = 0;
751 while i < N {
752 target.array[i] = T::zero();
753 i += 1;
754 }
755 return;
756 }
757
758 let last_index = N - 1;
759 let last_word = N - nwords;
760
761 let mut i = 0;
763 while i < last_word {
764 target.array[i] = target.array[i + nwords];
765 i += 1;
766 }
767
768 let mut i = last_word;
770 while i < N {
771 target.array[i] = T::zero();
772 i += 1;
773 }
774
775 if nbits != 0 {
776 let mut i = 0;
778 while i < last_index {
779 let left = target.array[i] >> nbits;
780 let right = target.array[i + 1] << (word_bits - nbits);
781 target.array[i] = left | right;
782 i += 1;
783 }
784 target.array[last_index] >>= nbits;
785 }
786 }
787
788 pub(crate) c0nst fn const_shl_ct<
797 T: [c0nst] ConstMachineWord + MachineWord,
798 const N: usize,
799 P: Personality,
800 >(
801 target: &mut FixedUInt<T, N, P>,
802 bits: usize,
803 ) {
804 if N == 0 {
805 return;
806 }
807 let layers = core::mem::size_of::<usize>() * 8;
810 let mut k = 0;
811 while k < layers {
812 let amount = 1usize << k;
813 let mut shifted = *target;
815 const_shl_impl(&mut shifted, amount);
816 let bit_k = ((bits >> k) & 1) as u8;
818 let bit_k_t = <T as core::convert::From<u8>>::from(bit_k);
819 let mask = <T as core::ops::Mul>::mul(bit_k_t, <T as ConstBounded>::max_value());
820 let mut i = 0;
822 while i < N {
823 let diff =
824 <T as core::ops::BitXor>::bitxor(target.array[i], shifted.array[i]);
825 let masked = <T as core::ops::BitAnd>::bitand(mask, diff);
826 target.array[i] = <T as core::ops::BitXor>::bitxor(target.array[i], masked);
827 i += 1;
828 }
829 k += 1;
830 }
831 }
832
833 pub(crate) c0nst fn const_shr_ct<
836 T: [c0nst] ConstMachineWord + MachineWord,
837 const N: usize,
838 P: Personality,
839 >(
840 target: &mut FixedUInt<T, N, P>,
841 bits: usize,
842 ) {
843 if N == 0 {
844 return;
845 }
846 let layers = core::mem::size_of::<usize>() * 8;
849 let mut k = 0;
850 while k < layers {
851 let amount = 1usize << k;
852 let mut shifted = *target;
853 const_shr_impl(&mut shifted, amount);
854 let bit_k = ((bits >> k) & 1) as u8;
855 let bit_k_t = <T as core::convert::From<u8>>::from(bit_k);
856 let mask = <T as core::ops::Mul>::mul(bit_k_t, <T as ConstBounded>::max_value());
857 let mut i = 0;
858 while i < N {
859 let diff =
860 <T as core::ops::BitXor>::bitxor(target.array[i], shifted.array[i]);
861 let masked = <T as core::ops::BitAnd>::bitand(mask, diff);
862 target.array[i] = <T as core::ops::BitXor>::bitxor(target.array[i], masked);
863 i += 1;
864 }
865 k += 1;
866 }
867 }
868
869 pub(crate) c0nst fn const_mul<T: [c0nst] ConstMachineWord, const N: usize, const CHECK_OVERFLOW: bool, P: Personality>(
879 op1: &[T; N],
880 op2: &[T; N],
881 word_bits: usize,
882 ) -> ([T; N], bool) {
883 let mut result: [T; N] = [<T as ConstZero>::zero(); N];
884 let mut overflowed = false;
885 let t_max = <T as ConstMachineWord>::to_double(<T as ConstBounded>::max_value());
886 let dw_zero = <<T as ConstMachineWord>::ConstDoubleWord as ConstZero>::zero();
887
888 let mut i = 0;
889 while i < N {
890 let mut carry = dw_zero;
891 let mut j = 0;
892 while j < N {
893 let round = i + j;
894 let op1_dw = <T as ConstMachineWord>::to_double(op1[i]);
895 let op2_dw = <T as ConstMachineWord>::to_double(op2[j]);
896 let mul_res = op1_dw * op2_dw;
897 let mut accumulator = if round < N {
898 <T as ConstMachineWord>::to_double(result[round])
899 } else {
900 dw_zero
901 };
902 accumulator += mul_res + carry;
903
904 match P::TAG {
905 PersonalityTag::Nct => {
906 if accumulator > t_max {
907 carry = accumulator >> word_bits;
908 accumulator &= t_max;
909 } else {
910 carry = dw_zero;
911 }
912 }
913 PersonalityTag::Ct => {
914 carry = accumulator >> word_bits;
915 accumulator &= t_max;
916 }
917 }
918 if round < N {
919 result[round] = <T as ConstMachineWord>::from_double(accumulator);
920 } else if CHECK_OVERFLOW {
921 overflowed |= accumulator != dw_zero;
922 }
923 j += 1;
924 }
925 if CHECK_OVERFLOW {
926 overflowed |= carry != dw_zero;
927 }
928 i += 1;
929 }
930 (result, overflowed)
931 }
932
933 pub(crate) c0nst fn const_word_bits<T>() -> usize {
935 core::mem::size_of::<T>() * 8
936 }
937
938 pub(crate) c0nst fn const_cmp_words<T: [c0nst] ConstMachineWord>(a: T, b: T) -> Option<core::cmp::Ordering> {
940 if a > b {
941 Some(core::cmp::Ordering::Greater)
942 } else if a < b {
943 Some(core::cmp::Ordering::Less)
944 } else {
945 None
946 }
947 }
948
949 pub(crate) c0nst fn const_leading_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
951 array: &[T; N],
952 ) -> u32 {
953 let mut ret = 0u32;
954 let mut index = N;
955 while index > 0 {
956 index -= 1;
957 let v = array[index];
958 ret += <T as ConstBitPrimInt>::leading_zeros(v);
959 if !<T as ConstZero>::is_zero(&v) {
960 break;
961 }
962 }
963 ret
964 }
965
966 pub(crate) c0nst fn const_leading_zeros_ct<T: [c0nst] ConstMachineWord, const N: usize>(
973 array: &[T; N],
974 ) -> u32 {
975 let mut total: u32 = 0;
976 let mut decided: u32 = 0;
978 let mut index = N;
979 while index > 0 {
980 index -= 1;
981 let v = array[index];
982 let v_lz = <T as ConstBitPrimInt>::leading_zeros(v);
983 total += (!decided) & v_lz;
985 let v_nz_bit = (!<T as ConstZero>::is_zero(&v)) as u32;
987 let v_nz_mask = v_nz_bit.wrapping_neg();
988 decided |= v_nz_mask;
989 }
990 total
991 }
992
993 pub(crate) c0nst fn const_trailing_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
995 array: &[T; N],
996 ) -> u32 {
997 let mut ret = 0u32;
998 let mut index = 0;
999 while index < N {
1000 let v = array[index];
1001 ret += <T as ConstBitPrimInt>::trailing_zeros(v);
1002 if !<T as ConstZero>::is_zero(&v) {
1003 break;
1004 }
1005 index += 1;
1006 }
1007 ret
1008 }
1009
1010 pub(crate) c0nst fn const_trailing_zeros_ct<T: [c0nst] ConstMachineWord, const N: usize>(
1015 array: &[T; N],
1016 ) -> u32 {
1017 let mut total: u32 = 0;
1018 let mut decided: u32 = 0;
1020 let mut index = 0;
1021 while index < N {
1022 let v = array[index];
1023 let v_tz = <T as ConstBitPrimInt>::trailing_zeros(v);
1024 total += (!decided) & v_tz;
1025 let v_nz_bit = (!<T as ConstZero>::is_zero(&v)) as u32;
1026 let v_nz_mask = v_nz_bit.wrapping_neg();
1027 decided |= v_nz_mask;
1028 index += 1;
1029 }
1030 total
1031 }
1032
1033 pub(crate) c0nst fn const_bit_length<T: [c0nst] ConstMachineWord, const N: usize>(
1035 array: &[T; N],
1036 ) -> usize {
1037 let word_bits = const_word_bits::<T>();
1038 let bit_size = N * word_bits;
1039 bit_size - const_leading_zeros::<T, N>(array) as usize
1040 }
1041
1042 pub(crate) c0nst fn const_is_zero<T: [c0nst] ConstMachineWord, const N: usize>(
1044 array: &[T; N],
1045 ) -> bool {
1046 let mut index = 0;
1047 while index < N {
1048 if !<T as ConstZero>::is_zero(&array[index]) {
1049 return false;
1050 }
1051 index += 1;
1052 }
1053 true
1054 }
1055
1056 pub(crate) c0nst fn const_is_zero_ct<T: [c0nst] ConstMachineWord, const N: usize>(
1061 array: &[T; N],
1062 ) -> bool {
1063 let mut acc = <T as ConstZero>::zero();
1064 let mut index = 0;
1065 while index < N {
1066 acc = <T as core::ops::BitOr>::bitor(acc, array[index]);
1067 index += 1;
1068 }
1069 <T as ConstZero>::is_zero(&acc)
1070 }
1071
1072 pub(crate) c0nst fn const_is_one<T: [c0nst] ConstMachineWord, const N: usize>(
1077 array: &[T; N],
1078 ) -> bool {
1079 if N == 0 || !array[0].is_one() {
1080 return false;
1081 }
1082 let mut i = 1;
1083 while i < N {
1084 if !<T as ConstZero>::is_zero(&array[i]) {
1085 return false;
1086 }
1087 i += 1;
1088 }
1089 true
1090 }
1091
1092 pub(crate) c0nst fn const_is_one_ct<T: [c0nst] ConstMachineWord, const N: usize>(
1097 array: &[T; N],
1098 ) -> bool {
1099 if N == 0 {
1100 return false;
1101 }
1102 let mut acc = <T as core::ops::BitXor>::bitxor(array[0], <T as ConstOne>::one());
1103 let mut index = 1;
1104 while index < N {
1105 acc = <T as core::ops::BitOr>::bitor(acc, array[index]);
1106 index += 1;
1107 }
1108 <T as ConstZero>::is_zero(&acc)
1109 }
1110
1111 pub(crate) c0nst fn const_set_bit<T: [c0nst] ConstMachineWord, const N: usize>(
1117 array: &mut [T; N],
1118 pos: usize,
1119 ) {
1120 let word_bits = const_word_bits::<T>();
1121 let word_idx = pos / word_bits;
1122 if word_idx >= N {
1123 return;
1124 }
1125 let bit_idx = pos % word_bits;
1126 array[word_idx] |= <T as ConstOne>::one() << bit_idx;
1127 }
1128
1129 pub(crate) c0nst fn const_cmp<T: [c0nst] ConstMachineWord, const N: usize>(
1134 a: &[T; N],
1135 b: &[T; N],
1136 ) -> core::cmp::Ordering {
1137 let mut index = N;
1138 while index > 0 {
1139 index -= 1;
1140 if let Some(ord) = const_cmp_words(a[index], b[index]) {
1141 return ord;
1142 }
1143 }
1144 core::cmp::Ordering::Equal
1145 }
1146
1147 pub(crate) c0nst fn const_cmp_ct<T: [c0nst] ConstMachineWord, const N: usize>(
1152 a: &[T; N],
1153 b: &[T; N],
1154 ) -> core::cmp::Ordering {
1155 let mut result: u8 = 0;
1157 let mut decided: u8 = 0;
1159 let mut index = N;
1160 while index > 0 {
1161 index -= 1;
1162 let gt = (a[index] > b[index]) as u8;
1163 let lt = (a[index] < b[index]) as u8;
1164 let here = (gt << 1) | lt;
1166 let undecided_mask = !decided;
1167 result |= undecided_mask & here;
1168 let here_nz_mask = ((here != 0) as u8).wrapping_neg();
1170 decided |= here_nz_mask;
1171 }
1172 match result {
1173 2 => core::cmp::Ordering::Greater,
1174 1 => core::cmp::Ordering::Less,
1175 _ => core::cmp::Ordering::Equal,
1176 }
1177 }
1178
1179 pub(crate) c0nst fn const_get_shifted_word<T: [c0nst] ConstMachineWord, const N: usize>(
1184 array: &[T; N],
1185 word_idx: usize,
1186 word_shift: usize,
1187 bit_shift: usize,
1188 ) -> T {
1189 let word_bits = const_word_bits::<T>();
1190
1191 if bit_shift >= word_bits {
1193 return <T as ConstZero>::zero();
1194 }
1195
1196 if word_idx < word_shift {
1197 return <T as ConstZero>::zero();
1198 }
1199
1200 let source_idx = word_idx - word_shift;
1201
1202 if bit_shift == 0 {
1203 if source_idx < N {
1204 array[source_idx]
1205 } else {
1206 <T as ConstZero>::zero()
1207 }
1208 } else {
1209 let mut result = <T as ConstZero>::zero();
1210
1211 if source_idx < N {
1213 result |= array[source_idx] << bit_shift;
1214 }
1215
1216 if source_idx > 0 && source_idx - 1 < N {
1218 let high_bits = array[source_idx - 1] >> (word_bits - bit_shift);
1219 result |= high_bits;
1220 }
1221
1222 result
1223 }
1224 }
1225
1226 pub(crate) c0nst fn const_cmp_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
1231 array: &[T; N],
1232 other: &[T; N],
1233 shift_bits: usize,
1234 ) -> core::cmp::Ordering {
1235 let word_bits = const_word_bits::<T>();
1236
1237 if shift_bits == 0 {
1238 return const_cmp::<T, N>(array, other);
1239 }
1240
1241 let word_shift = shift_bits / word_bits;
1242 if word_shift >= N {
1243 if const_is_zero::<T, N>(array) {
1245 return core::cmp::Ordering::Equal;
1246 } else {
1247 return core::cmp::Ordering::Greater;
1248 }
1249 }
1250
1251 let bit_shift = shift_bits % word_bits;
1252
1253 let mut index = N;
1255 while index > 0 {
1256 index -= 1;
1257 let self_word = array[index];
1258 let other_shifted_word = const_get_shifted_word::<T, N>(
1259 other, index, word_shift, bit_shift
1260 );
1261
1262 if let Some(ord) = const_cmp_words(self_word, other_shifted_word) {
1263 return ord;
1264 }
1265 }
1266
1267 core::cmp::Ordering::Equal
1268 }
1269
1270 pub(crate) c0nst fn const_sub_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
1275 array: &mut [T; N],
1276 other: &[T; N],
1277 shift_bits: usize,
1278 ) {
1279 let word_bits = const_word_bits::<T>();
1280
1281 if shift_bits == 0 {
1282 sub_impl::<T, N>(array, other);
1283 return;
1284 }
1285
1286 let word_shift = shift_bits / word_bits;
1287 if word_shift >= N {
1288 return;
1289 }
1290
1291 let bit_shift = shift_bits % word_bits;
1292 let mut borrow = T::zero();
1293 let mut index = 0;
1294 while index < N {
1295 let other_word = const_get_shifted_word::<T, N>(other, index, word_shift, bit_shift);
1296 let (res, borrow1) = array[index].overflowing_sub(&other_word);
1297 let (res, borrow2) = res.overflowing_sub(&borrow);
1298 borrow = if borrow1 || borrow2 { T::one() } else { T::zero() };
1299 array[index] = res;
1300 index += 1;
1301 }
1302 }
1303
1304 pub(crate) c0nst fn const_div<T: [c0nst] ConstMachineWord, const N: usize>(
1308 dividend: &mut [T; N],
1309 divisor: &[T; N],
1310 ) -> [T; N] {
1311 use core::cmp::Ordering;
1312
1313 match const_cmp::<T, N>(dividend, divisor) {
1314 Ordering::Less => {
1316 let remainder = *dividend;
1317 let mut i = 0;
1318 while i < N {
1319 dividend[i] = <T as ConstZero>::zero();
1320 i += 1;
1321 }
1322 return remainder;
1323 }
1324 Ordering::Equal => {
1326 let mut i = 0;
1327 while i < N {
1328 dividend[i] = <T as ConstZero>::zero();
1329 i += 1;
1330 }
1331 if N > 0 {
1332 dividend[0] = <T as ConstOne>::one();
1333 }
1334 return [<T as ConstZero>::zero(); N];
1335 }
1336 Ordering::Greater => {}
1337 }
1338
1339 let mut quotient = [<T as ConstZero>::zero(); N];
1340
1341 let dividend_bits = const_bit_length::<T, N>(dividend);
1343 let divisor_bits = const_bit_length::<T, N>(divisor);
1344
1345 let mut bit_pos = if dividend_bits >= divisor_bits {
1346 dividend_bits - divisor_bits
1347 } else {
1348 0
1349 };
1350
1351 while bit_pos > 0 {
1353 let cmp = const_cmp_shifted::<T, N>(dividend, divisor, bit_pos);
1354 if !matches!(cmp, Ordering::Less) {
1355 break;
1356 }
1357 bit_pos -= 1;
1358 }
1359
1360 loop {
1362 let cmp = const_cmp_shifted::<T, N>(dividend, divisor, bit_pos);
1363 if !matches!(cmp, Ordering::Less) {
1364 const_sub_shifted::<T, N>(dividend, divisor, bit_pos);
1365 const_set_bit::<T, N>(&mut quotient, bit_pos);
1366 }
1367
1368 if bit_pos == 0 {
1369 break;
1370 }
1371 bit_pos -= 1;
1372 }
1373
1374 let remainder = *dividend;
1375 *dividend = quotient;
1376 remainder
1377 }
1378
1379 pub(crate) c0nst fn const_div_rem<T: [c0nst] ConstMachineWord, const N: usize>(
1383 dividend: &[T; N],
1384 divisor: &[T; N],
1385 ) -> ([T; N], [T; N]) {
1386 if const_is_zero(divisor) {
1387 maybe_panic(PanicReason::DivByZero)
1388 }
1389 let mut quotient = *dividend;
1390 let remainder = const_div(&mut quotient, divisor);
1391 (quotient, remainder)
1392 }
1393}
1394
1395c0nst::c0nst! {
1396 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> c0nst Default for FixedUInt<T, N, P> {
1397 fn default() -> Self {
1398 FixedUInt::from_array([<T as ConstZero>::zero(); N])
1399 }
1400 }
1401
1402 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> c0nst Clone for FixedUInt<T, N, P> {
1403 fn clone(&self) -> Self {
1404 *self
1405 }
1406 }
1407}
1408
1409impl<T: MachineWord, const N: usize> num_traits::Unsigned for FixedUInt<T, N, Nct> {}
1412
1413c0nst::c0nst! {
1416 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> c0nst core::cmp::PartialEq for FixedUInt<T, N, P> {
1417 fn eq(&self, other: &Self) -> bool {
1418 match P::TAG {
1419 PersonalityTag::Nct => self.array == other.array,
1420 PersonalityTag::Ct => {
1421 let mut diff = <T as crate::const_numtraits::ConstZero>::zero();
1422 let mut i = 0;
1423 while i < N {
1424 let x = <T as core::ops::BitXor>::bitxor(self.array[i], other.array[i]);
1425 diff = <T as core::ops::BitOr>::bitor(diff, x);
1426 i += 1;
1427 }
1428 <T as crate::const_numtraits::ConstZero>::is_zero(&diff)
1429 }
1430 }
1431 }
1432 }
1433
1434 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> c0nst core::cmp::Eq for FixedUInt<T, N, P> {}
1435
1436 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> c0nst core::cmp::Ord for FixedUInt<T, N, P> {
1437 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
1438 match P::TAG {
1439 PersonalityTag::Nct => const_cmp(&self.array, &other.array),
1440 PersonalityTag::Ct => const_cmp_ct(&self.array, &other.array),
1441 }
1442 }
1443 }
1444
1445 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> c0nst core::cmp::PartialOrd for FixedUInt<T, N, P> {
1446 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
1447 Some(self.cmp(other))
1448 }
1449 }
1450}
1451
1452c0nst::c0nst! {
1457 c0nst fn const_from_le_bytes<T: [c0nst] ConstMachineWord, const N: usize, const B: usize>(
1460 bytes: [u8; B],
1461 ) -> [T; N] {
1462 impl_from_le_bytes_slice::<T, N>(&bytes)
1463 }
1464
1465 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> c0nst core::convert::From<u8> for FixedUInt<T, N, P> {
1466 fn from(x: u8) -> Self {
1467 Self::from_array(const_from_le_bytes(x.to_le_bytes()))
1468 }
1469 }
1470
1471 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> c0nst core::convert::From<u16> for FixedUInt<T, N, P> {
1472 fn from(x: u16) -> Self {
1473 Self::from_array(const_from_le_bytes(x.to_le_bytes()))
1474 }
1475 }
1476
1477 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> c0nst core::convert::From<u32> for FixedUInt<T, N, P> {
1478 fn from(x: u32) -> Self {
1479 Self::from_array(const_from_le_bytes(x.to_le_bytes()))
1480 }
1481 }
1482
1483 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> c0nst core::convert::From<u64> for FixedUInt<T, N, P> {
1484 fn from(x: u64) -> Self {
1485 Self::from_array(const_from_le_bytes(x.to_le_bytes()))
1486 }
1487 }
1488}
1489
1490fn make_parse_int_err() -> core::num::ParseIntError {
1497 <u8>::from_str_radix("-", 2).err().unwrap()
1498}
1499fn make_overflow_err() -> core::num::ParseIntError {
1500 <u8>::from_str_radix("101", 16).err().unwrap()
1501}
1502fn make_empty_error() -> core::num::ParseIntError {
1503 <u8>::from_str_radix("", 8).err().unwrap()
1504}
1505
1506fn to_slice_hex<T: AsRef<[u8]>>(
1507 input: T,
1508 output: &mut [u8],
1509) -> Result<(), core::num::ParseIntError> {
1510 fn from_digit(byte: u8) -> Option<char> {
1511 core::char::from_digit(byte as u32, 16)
1512 }
1513 let r = input.as_ref();
1514 if r.len() * 2 != output.len() {
1515 return Err(make_parse_int_err());
1516 }
1517 for i in 0..r.len() {
1518 let byte = r[i];
1519 output[i * 2] = from_digit((byte & 0xf0) >> 4).ok_or_else(make_parse_int_err)? as u8;
1520 output[i * 2 + 1] = from_digit(byte & 0x0f).ok_or_else(make_parse_int_err)? as u8;
1521 }
1522
1523 Ok(())
1524}
1525
1526pub(super) enum PanicReason {
1527 Add,
1528 Sub,
1529 Mul,
1530 DivByZero,
1531}
1532
1533c0nst::c0nst! {
1534 pub(super) c0nst fn maybe_panic(r: PanicReason) {
1535 match r {
1536 PanicReason::Add => panic!("attempt to add with overflow"),
1537 PanicReason::Sub => panic!("attempt to subtract with overflow"),
1538 PanicReason::Mul => panic!("attempt to multiply with overflow"),
1539 PanicReason::DivByZero => panic!("attempt to divide by zero"),
1540 }
1541 }
1542
1543 pub(crate) c0nst fn const_ct_select<
1546 T: [c0nst] ConstMachineWord + MachineWord,
1547 const N: usize,
1548 P: Personality,
1549 >(
1550 if_zero: FixedUInt<T, N, P>,
1551 if_one: FixedUInt<T, N, P>,
1552 choice: u8,
1553 ) -> FixedUInt<T, N, P> {
1554 let bit_t = <T as core::convert::From<u8>>::from(choice);
1555 let mask = <T as core::ops::Mul>::mul(bit_t, <T as ConstBounded>::max_value());
1556 let mut result = if_zero;
1557 let mut i = 0;
1558 while i < N {
1559 let diff = <T as core::ops::BitXor>::bitxor(if_zero.array[i], if_one.array[i]);
1560 let masked = <T as core::ops::BitAnd>::bitand(mask, diff);
1561 result.array[i] = <T as core::ops::BitXor>::bitxor(if_zero.array[i], masked);
1562 i += 1;
1563 }
1564 result
1565 }
1566
1567 pub(super) c0nst fn maybe_panic_if<P: Personality>(
1568 overflow: bool,
1569 reason: PanicReason,
1570 ) {
1571 match P::TAG {
1572 PersonalityTag::Nct => {
1573 if overflow {
1574 maybe_panic(reason);
1575 }
1576 }
1577 PersonalityTag::Ct => {
1578 let _ = overflow;
1579 let _ = reason;
1580 }
1581 }
1582 }
1583}
1584
1585#[cfg(test)]
1588mod tests {
1589 use super::FixedUInt as Bn;
1590 use super::*;
1591 use num_traits::One;
1592
1593 type Bn8 = Bn<u8, 8>;
1594 type Bn16 = Bn<u16, 4>;
1595 type Bn32 = Bn<u32, 2>;
1596
1597 c0nst::c0nst! {
1598 pub c0nst fn test_add<T: [c0nst] ConstMachineWord, const N: usize>(
1599 a: &mut [T; N],
1600 b: &[T; N]
1601 ) -> bool {
1602 add_impl(a, b)
1603 }
1604
1605 pub c0nst fn test_sub<T: [c0nst] ConstMachineWord, const N: usize>(
1606 a: &mut [T; N],
1607 b: &[T; N]
1608 ) -> bool {
1609 sub_impl(a, b)
1610 }
1611
1612 pub c0nst fn test_mul<T: [c0nst] ConstMachineWord, const N: usize>(
1613 a: &[T; N],
1614 b: &[T; N],
1615 word_bits: usize,
1616 ) -> ([T; N], bool) {
1617 const_mul::<T, N, true, crate::personality::Nct>(a, b, word_bits)
1618 }
1619
1620 pub c0nst fn arr_leading_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1621 a: &[T; N],
1622 ) -> u32 {
1623 const_leading_zeros::<T, N>(a)
1624 }
1625
1626 pub c0nst fn arr_trailing_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1627 a: &[T; N],
1628 ) -> u32 {
1629 const_trailing_zeros::<T, N>(a)
1630 }
1631
1632 pub c0nst fn arr_bit_length<T: [c0nst] ConstMachineWord, const N: usize>(
1633 a: &[T; N],
1634 ) -> usize {
1635 const_bit_length::<T, N>(a)
1636 }
1637
1638 pub c0nst fn arr_is_zero<T: [c0nst] ConstMachineWord, const N: usize>(
1639 a: &[T; N],
1640 ) -> bool {
1641 const_is_zero::<T, N>(a)
1642 }
1643
1644 pub c0nst fn arr_set_bit<T: [c0nst] ConstMachineWord, const N: usize>(
1645 a: &mut [T; N],
1646 pos: usize,
1647 ) {
1648 const_set_bit::<T, N>(a, pos)
1649 }
1650
1651 pub c0nst fn arr_cmp<T: [c0nst] ConstMachineWord, const N: usize>(
1652 a: &[T; N],
1653 b: &[T; N],
1654 ) -> core::cmp::Ordering {
1655 const_cmp::<T, N>(a, b)
1656 }
1657
1658 pub c0nst fn arr_cmp_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
1659 a: &[T; N],
1660 b: &[T; N],
1661 shift_bits: usize,
1662 ) -> core::cmp::Ordering {
1663 const_cmp_shifted::<T, N>(a, b, shift_bits)
1664 }
1665
1666 pub c0nst fn arr_get_shifted_word<T: [c0nst] ConstMachineWord, const N: usize>(
1667 a: &[T; N],
1668 word_idx: usize,
1669 word_shift: usize,
1670 bit_shift: usize,
1671 ) -> T {
1672 const_get_shifted_word::<T, N>(a, word_idx, word_shift, bit_shift)
1673 }
1674 }
1675
1676 #[test]
1677 fn test_const_add_impl() {
1678 let mut a: [u8; 4] = [1, 0, 0, 0];
1680 let b: [u8; 4] = [2, 0, 0, 0];
1681 let overflow = test_add(&mut a, &b);
1682 assert_eq!(a, [3, 0, 0, 0]);
1683 assert!(!overflow);
1684
1685 let mut a: [u8; 4] = [255, 0, 0, 0];
1687 let b: [u8; 4] = [1, 0, 0, 0];
1688 let overflow = test_add(&mut a, &b);
1689 assert_eq!(a, [0, 1, 0, 0]);
1690 assert!(!overflow);
1691
1692 let mut a: [u8; 4] = [255, 255, 255, 255];
1694 let b: [u8; 4] = [1, 0, 0, 0];
1695 let overflow = test_add(&mut a, &b);
1696 assert_eq!(a, [0, 0, 0, 0]);
1697 assert!(overflow);
1698
1699 let mut a: [u32; 2] = [0xFFFFFFFF, 0];
1701 let b: [u32; 2] = [1, 0];
1702 let overflow = test_add(&mut a, &b);
1703 assert_eq!(a, [0, 1]);
1704 assert!(!overflow);
1705
1706 #[cfg(feature = "nightly")]
1707 {
1708 const ADD_RESULT: ([u8; 4], bool) = {
1709 let mut a = [1u8, 0, 0, 0];
1710 let b = [2u8, 0, 0, 0];
1711 let overflow = test_add(&mut a, &b);
1712 (a, overflow)
1713 };
1714 assert_eq!(ADD_RESULT, ([3, 0, 0, 0], false));
1715 }
1716 }
1717
1718 #[test]
1719 fn test_const_sub_impl() {
1720 let mut a: [u8; 4] = [3, 0, 0, 0];
1722 let b: [u8; 4] = [1, 0, 0, 0];
1723 let overflow = test_sub(&mut a, &b);
1724 assert_eq!(a, [2, 0, 0, 0]);
1725 assert!(!overflow);
1726
1727 let mut a: [u8; 4] = [0, 1, 0, 0];
1729 let b: [u8; 4] = [1, 0, 0, 0];
1730 let overflow = test_sub(&mut a, &b);
1731 assert_eq!(a, [255, 0, 0, 0]);
1732 assert!(!overflow);
1733
1734 let mut a: [u8; 4] = [0, 0, 0, 0];
1736 let b: [u8; 4] = [1, 0, 0, 0];
1737 let overflow = test_sub(&mut a, &b);
1738 assert_eq!(a, [255, 255, 255, 255]);
1739 assert!(overflow);
1740
1741 let mut a: [u32; 2] = [0, 1];
1743 let b: [u32; 2] = [1, 0];
1744 let overflow = test_sub(&mut a, &b);
1745 assert_eq!(a, [0xFFFFFFFF, 0]);
1746 assert!(!overflow);
1747
1748 #[cfg(feature = "nightly")]
1749 {
1750 const SUB_RESULT: ([u8; 4], bool) = {
1751 let mut a = [3u8, 0, 0, 0];
1752 let b = [1u8, 0, 0, 0];
1753 let overflow = test_sub(&mut a, &b);
1754 (a, overflow)
1755 };
1756 assert_eq!(SUB_RESULT, ([2, 0, 0, 0], false));
1757 }
1758 }
1759
1760 #[test]
1761 fn test_const_mul_impl() {
1762 let a: [u8; 2] = [3, 0];
1764 let b: [u8; 2] = [4, 0];
1765 let (result, overflow) = test_mul(&a, &b, 8);
1766 assert_eq!(result, [12, 0]);
1767 assert!(!overflow);
1768
1769 let a: [u8; 2] = [200, 0];
1771 let b: [u8; 2] = [2, 0];
1772 let (result, overflow) = test_mul(&a, &b, 8);
1773 assert_eq!(result, [0x90, 0x01]);
1774 assert!(!overflow);
1775
1776 let a: [u8; 2] = [0, 1]; let b: [u8; 2] = [0, 1]; let (_result, overflow) = test_mul(&a, &b, 8);
1780 assert!(overflow);
1781
1782 let a: [u8; 3] = [0, 0, 1];
1786 let b: [u8; 3] = [0, 0, 1];
1787 let (_result, overflow) = test_mul(&a, &b, 8);
1788 assert!(overflow, "N=3 high-position overflow not detected");
1789
1790 let a: [u8; 3] = [0, 0, 2];
1794 let b: [u8; 3] = [0, 0, 2];
1795 let (_result, overflow) = test_mul(&a, &b, 8);
1796 assert!(
1797 overflow,
1798 "N=3 high-position overflow with larger values not detected"
1799 );
1800
1801 let a: [u8; 3] = [0, 1, 0];
1805 let b: [u8; 3] = [0, 1, 0];
1806 let (result, overflow) = test_mul(&a, &b, 8);
1807 assert_eq!(result, [0, 0, 1]);
1808 assert!(
1809 !overflow,
1810 "N=3 non-overflow incorrectly detected as overflow"
1811 );
1812
1813 let a: [u8; 3] = [255, 0, 0];
1817 let b: [u8; 3] = [255, 0, 0];
1818 let (result, overflow) = test_mul(&a, &b, 8);
1819 assert_eq!(result, [0x01, 0xFE, 0x00]);
1820 assert!(!overflow);
1821
1822 #[cfg(feature = "nightly")]
1823 {
1824 const MUL_RESULT: ([u8; 2], bool) = test_mul(&[3u8, 0], &[4u8, 0], 8);
1825 assert_eq!(MUL_RESULT, ([12, 0], false));
1826 }
1827 }
1828
1829 #[test]
1830 fn test_const_helpers() {
1831 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]));
1856 assert!(!arr_is_zero(&[1u8, 0, 0, 0]));
1857 assert!(!arr_is_zero(&[0u8, 0, 0, 1]));
1858 assert!(!arr_is_zero(&[0u8, 1, 0, 0]));
1859
1860 let mut arr: [u8; 4] = [0, 0, 0, 0];
1862 arr_set_bit(&mut arr, 0);
1863 assert_eq!(arr, [1, 0, 0, 0]);
1864
1865 let mut arr: [u8; 4] = [0, 0, 0, 0];
1866 arr_set_bit(&mut arr, 8);
1867 assert_eq!(arr, [0, 1, 0, 0]);
1868
1869 let mut arr: [u8; 4] = [0, 0, 0, 0];
1870 arr_set_bit(&mut arr, 31);
1871 assert_eq!(arr, [0, 0, 0, 0x80]);
1872
1873 let mut arr: [u8; 4] = [0, 0, 0, 0];
1875 arr_set_bit(&mut arr, 0);
1876 arr_set_bit(&mut arr, 3);
1877 arr_set_bit(&mut arr, 8);
1878 assert_eq!(arr, [0b00001001, 1, 0, 0]);
1879
1880 let mut arr: [u8; 4] = [0, 0, 0, 0];
1882 arr_set_bit(&mut arr, 32);
1883 assert_eq!(arr, [0, 0, 0, 0]);
1884
1885 assert_eq!(arr_leading_zeros(&[0u32, 0]), 64);
1887 assert_eq!(arr_leading_zeros(&[1u32, 0]), 63);
1888 assert_eq!(arr_leading_zeros(&[0u32, 1]), 31);
1889 assert_eq!(arr_trailing_zeros(&[0u32, 0]), 64);
1890 assert_eq!(arr_trailing_zeros(&[0u32, 1]), 32);
1891 assert_eq!(arr_bit_length(&[0u32, 0]), 0);
1892 assert_eq!(arr_bit_length(&[1u32, 0]), 1);
1893 assert_eq!(arr_bit_length(&[0u32, 1]), 33);
1894
1895 #[cfg(feature = "nightly")]
1896 {
1897 const LEADING: u32 = arr_leading_zeros(&[0u8, 0, 1, 0]);
1898 assert_eq!(LEADING, 15);
1899
1900 const TRAILING: u32 = arr_trailing_zeros(&[0u8, 0, 1, 0]);
1901 assert_eq!(TRAILING, 16);
1902
1903 const BIT_LEN: usize = arr_bit_length(&[0u8, 0, 1, 0]);
1904 assert_eq!(BIT_LEN, 17);
1905
1906 const IS_ZERO: bool = arr_is_zero(&[0u8, 0, 0, 0]);
1907 assert!(IS_ZERO);
1908
1909 const NOT_ZERO: bool = arr_is_zero(&[0u8, 1, 0, 0]);
1910 assert!(!NOT_ZERO);
1911
1912 const SET_BIT_RESULT: [u8; 4] = {
1913 let mut arr = [0u8, 0, 0, 0];
1914 arr_set_bit(&mut arr, 10);
1915 arr
1916 };
1917 assert_eq!(SET_BIT_RESULT, [0, 0b00000100, 0, 0]);
1918 }
1919 }
1920
1921 #[test]
1922 fn test_const_cmp() {
1923 use core::cmp::Ordering;
1924
1925 assert_eq!(arr_cmp(&[1u8, 2, 3, 4], &[1u8, 2, 3, 4]), Ordering::Equal);
1927 assert_eq!(arr_cmp(&[0u8, 0, 0, 0], &[0u8, 0, 0, 0]), Ordering::Equal);
1928
1929 assert_eq!(arr_cmp(&[0u8, 0, 0, 2], &[0u8, 0, 0, 1]), Ordering::Greater);
1931
1932 assert_eq!(arr_cmp(&[0u8, 0, 0, 1], &[0u8, 0, 0, 2]), Ordering::Less);
1934
1935 assert_eq!(arr_cmp(&[2u8, 0, 0, 0], &[1u8, 0, 0, 0]), Ordering::Greater);
1937
1938 assert_eq!(arr_cmp(&[1u8, 0, 0, 0], &[2u8, 0, 0, 0]), Ordering::Less);
1940
1941 assert_eq!(arr_cmp(&[0u32, 1], &[0u32, 1]), Ordering::Equal);
1943 assert_eq!(arr_cmp(&[0u32, 2], &[0u32, 1]), Ordering::Greater);
1944 assert_eq!(arr_cmp(&[0u32, 1], &[0u32, 2]), Ordering::Less);
1945
1946 #[cfg(feature = "nightly")]
1947 {
1948 const CMP_EQ: Ordering = arr_cmp(&[1u8, 2, 3, 4], &[1u8, 2, 3, 4]);
1949 const CMP_GT: Ordering = arr_cmp(&[0u8, 0, 0, 2], &[0u8, 0, 0, 1]);
1950 const CMP_LT: Ordering = arr_cmp(&[0u8, 0, 0, 1], &[0u8, 0, 0, 2]);
1951 assert_eq!(CMP_EQ, Ordering::Equal);
1952 assert_eq!(CMP_GT, Ordering::Greater);
1953 assert_eq!(CMP_LT, Ordering::Less);
1954 }
1955 }
1956
1957 #[test]
1958 fn test_const_cmp_shifted() {
1959 use core::cmp::Ordering;
1960
1961 assert_eq!(
1963 arr_cmp_shifted(&[1u8, 0, 0, 0], &[1u8, 0, 0, 0], 0),
1964 Ordering::Equal
1965 );
1966
1967 assert_eq!(
1969 arr_cmp_shifted(&[0u8, 1, 0, 0], &[1u8, 0, 0, 0], 8),
1970 Ordering::Equal
1971 );
1972
1973 assert_eq!(
1975 arr_cmp_shifted(&[0u8, 2, 0, 0], &[1u8, 0, 0, 0], 8),
1976 Ordering::Greater
1977 );
1978
1979 assert_eq!(
1981 arr_cmp_shifted(&[0u8, 0, 0, 0], &[1u8, 0, 0, 0], 8),
1982 Ordering::Less
1983 );
1984
1985 assert_eq!(
1988 arr_cmp_shifted(&[1u8, 0, 0, 0], &[1u8, 0, 0, 0], 32),
1989 Ordering::Greater
1990 );
1991
1992 assert_eq!(
1994 arr_cmp_shifted(&[0u8, 0, 0, 0], &[255u8, 255, 255, 255], 32),
1995 Ordering::Equal
1996 );
1997
1998 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 0, 1, 0), 0);
2002 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 1, 1, 0), 1);
2003 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 2, 1, 0), 2);
2004
2005 assert_eq!(arr_get_shifted_word(&[0x0Fu8, 0xF0, 0, 0], 0, 0, 4), 0xF0);
2009 assert_eq!(arr_get_shifted_word(&[0x0Fu8, 0xF0, 0, 0], 1, 0, 4), 0x00);
2011
2012 assert_eq!(arr_get_shifted_word(&[0xFFu8, 0x00, 0, 0], 0, 0, 4), 0xF0);
2015 assert_eq!(arr_get_shifted_word(&[0xFFu8, 0x00, 0, 0], 1, 0, 4), 0x0F);
2017
2018 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 0, 1, 4), 0);
2022 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 1, 1, 4), 0xB0);
2024 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 2, 1, 4), 0xDA);
2026
2027 #[cfg(feature = "nightly")]
2028 {
2029 const CMP_SHIFTED_EQ: Ordering = arr_cmp_shifted(&[0u8, 1, 0, 0], &[1u8, 0, 0, 0], 8);
2030 const CMP_SHIFTED_GT: Ordering = arr_cmp_shifted(&[0u8, 2, 0, 0], &[1u8, 0, 0, 0], 8);
2031 assert_eq!(CMP_SHIFTED_EQ, Ordering::Equal);
2032 assert_eq!(CMP_SHIFTED_GT, Ordering::Greater);
2033 }
2034 }
2035
2036 #[test]
2037 fn test_core_convert_u8() {
2038 let f = Bn::<u8, 1>::from(1u8);
2039 assert_eq!(f.array, [1]);
2040 let f = Bn::<u8, 2>::from(1u8);
2041 assert_eq!(f.array, [1, 0]);
2042
2043 let f = Bn::<u16, 1>::from(1u8);
2044 assert_eq!(f.array, [1]);
2045 let f = Bn::<u16, 2>::from(1u8);
2046 assert_eq!(f.array, [1, 0]);
2047
2048 #[cfg(feature = "nightly")]
2049 {
2050 const F1: Bn<u8, 2> = Bn::<u8, 2>::from(42u8);
2051 assert_eq!(F1.array, [42, 0]);
2052 }
2053 }
2054
2055 #[test]
2056 fn test_core_convert_u16() {
2057 let f = Bn::<u8, 1>::from(1u16);
2058 assert_eq!(f.array, [1]);
2059 let f = Bn::<u8, 2>::from(1u16);
2060 assert_eq!(f.array, [1, 0]);
2061
2062 let f = Bn::<u8, 1>::from(256u16);
2063 assert_eq!(f.array, [0]);
2064 let f = Bn::<u8, 2>::from(257u16);
2065 assert_eq!(f.array, [1, 1]);
2066 let f = Bn::<u8, 2>::from(65535u16);
2067 assert_eq!(f.array, [255, 255]);
2068
2069 let f = Bn::<u16, 1>::from(1u16);
2070 assert_eq!(f.array, [1]);
2071 let f = Bn::<u16, 2>::from(1u16);
2072 assert_eq!(f.array, [1, 0]);
2073
2074 let f = Bn::<u16, 1>::from(65535u16);
2075 assert_eq!(f.array, [65535]);
2076
2077 #[cfg(feature = "nightly")]
2078 {
2079 const F1: Bn<u8, 2> = Bn::<u8, 2>::from(0x0102u16);
2080 assert_eq!(F1.array, [0x02, 0x01]);
2081 }
2082 }
2083
2084 #[test]
2085 fn test_core_convert_u32() {
2086 let f = Bn::<u8, 1>::from(1u32);
2087 assert_eq!(f.array, [1]);
2088 let f = Bn::<u8, 1>::from(256u32);
2089 assert_eq!(f.array, [0]);
2090
2091 let f = Bn::<u8, 2>::from(1u32);
2092 assert_eq!(f.array, [1, 0]);
2093 let f = Bn::<u8, 2>::from(257u32);
2094 assert_eq!(f.array, [1, 1]);
2095 let f = Bn::<u8, 2>::from(65535u32);
2096 assert_eq!(f.array, [255, 255]);
2097
2098 let f = Bn::<u8, 4>::from(1u32);
2099 assert_eq!(f.array, [1, 0, 0, 0]);
2100 let f = Bn::<u8, 4>::from(257u32);
2101 assert_eq!(f.array, [1, 1, 0, 0]);
2102 let f = Bn::<u8, 4>::from(u32::max_value());
2103 assert_eq!(f.array, [255, 255, 255, 255]);
2104
2105 let f = Bn::<u8, 1>::from(1u32);
2106 assert_eq!(f.array, [1]);
2107 let f = Bn::<u8, 1>::from(256u32);
2108 assert_eq!(f.array, [0]);
2109
2110 let f = Bn::<u16, 2>::from(65537u32);
2111 assert_eq!(f.array, [1, 1]);
2112
2113 let f = Bn::<u32, 1>::from(1u32);
2114 assert_eq!(f.array, [1]);
2115 let f = Bn::<u32, 2>::from(1u32);
2116 assert_eq!(f.array, [1, 0]);
2117
2118 let f = Bn::<u32, 1>::from(65537u32);
2119 assert_eq!(f.array, [65537]);
2120
2121 let f = Bn::<u32, 1>::from(u32::max_value());
2122 assert_eq!(f.array, [4294967295]);
2123
2124 #[cfg(feature = "nightly")]
2125 {
2126 const F1: Bn<u8, 4> = Bn::<u8, 4>::from(0x01020304u32);
2127 assert_eq!(F1.array, [0x04, 0x03, 0x02, 0x01]);
2128 }
2129 }
2130
2131 #[test]
2132 fn test_core_convert_u64() {
2133 let f = Bn::<u8, 8>::from(0x0102030405060708u64);
2134 assert_eq!(f.array, [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
2135
2136 let f = Bn::<u16, 4>::from(0x0102030405060708u64);
2137 assert_eq!(f.array, [0x0708, 0x0506, 0x0304, 0x0102]);
2138
2139 let f = Bn::<u32, 2>::from(0x0102030405060708u64);
2140 assert_eq!(f.array, [0x05060708, 0x01020304]);
2141
2142 let f = Bn::<u64, 1>::from(0x0102030405060708u64);
2143 assert_eq!(f.array, [0x0102030405060708]);
2144
2145 #[cfg(feature = "nightly")]
2146 {
2147 const F1: Bn<u8, 8> = Bn::<u8, 8>::from(0x0102030405060708u64);
2148 assert_eq!(F1.array, [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
2149 }
2150 }
2151
2152 #[test]
2153 fn testsimple() {
2154 assert_eq!(Bn::<u8, 8>::new(), Bn::<u8, 8>::new());
2155
2156 assert_eq!(Bn::<u8, 8>::from_u8(3).unwrap().to_u32(), Some(3));
2157 assert_eq!(Bn::<u16, 4>::from_u8(3).unwrap().to_u32(), Some(3));
2158 assert_eq!(Bn::<u32, 2>::from_u8(3).unwrap().to_u32(), Some(3));
2159 assert_eq!(Bn::<u32, 2>::from_u64(3).unwrap().to_u32(), Some(3));
2160 assert_eq!(Bn::<u8, 8>::from_u64(255).unwrap().to_u32(), Some(255));
2161 assert_eq!(Bn::<u8, 8>::from_u64(256).unwrap().to_u32(), Some(256));
2162 assert_eq!(Bn::<u8, 8>::from_u64(65536).unwrap().to_u32(), Some(65536));
2163 }
2164 #[test]
2165 fn testfrom() {
2166 let mut n1 = Bn::<u8, 8>::new();
2167 n1.array[0] = 1;
2168 assert_eq!(Some(1), n1.to_u32());
2169 n1.array[1] = 1;
2170 assert_eq!(Some(257), n1.to_u32());
2171
2172 let mut n2 = Bn::<u16, 8>::new();
2173 n2.array[0] = 0xffff;
2174 assert_eq!(Some(65535), n2.to_u32());
2175 n2.array[0] = 0x0;
2176 n2.array[2] = 0x1;
2177 assert_eq!(None, n2.to_u32());
2179 assert_eq!(Some(0x100000000), n2.to_u64());
2180 }
2181
2182 #[test]
2183 fn test_from_str_bitlengths() {
2184 let test_s64 = "81906f5e4d3c2c01";
2185 let test_u64: u64 = 0x81906f5e4d3c2c01;
2186 let bb = Bn8::from_str_radix(test_s64, 16).unwrap();
2187 let cc = Bn8::from_u64(test_u64).unwrap();
2188 assert_eq!(cc.array, [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81]);
2189 assert_eq!(bb.array, [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81]);
2190 let dd = Bn16::from_u64(test_u64).unwrap();
2191 let ff = Bn16::from_str_radix(test_s64, 16).unwrap();
2192 assert_eq!(dd.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190]);
2193 assert_eq!(ff.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190]);
2194 let ee = Bn32::from_u64(test_u64).unwrap();
2195 let gg = Bn32::from_str_radix(test_s64, 16).unwrap();
2196 assert_eq!(ee.array, [0x4d3c2c01, 0x81906f5e]);
2197 assert_eq!(gg.array, [0x4d3c2c01, 0x81906f5e]);
2198 }
2199
2200 #[test]
2201 fn test_from_str_stringlengths() {
2202 let ab = Bn::<u8, 9>::from_str_radix("2281906f5e4d3c2c01", 16).unwrap();
2203 assert_eq!(
2204 ab.array,
2205 [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81, 0x22]
2206 );
2207 assert_eq!(
2208 [0x2c01, 0x4d3c, 0x6f5e, 0],
2209 Bn::<u16, 4>::from_str_radix("6f5e4d3c2c01", 16)
2210 .unwrap()
2211 .array
2212 );
2213 assert_eq!(
2214 [0x2c01, 0x4d3c, 0x6f5e, 0x190],
2215 Bn::<u16, 4>::from_str_radix("1906f5e4d3c2c01", 16)
2216 .unwrap()
2217 .array
2218 );
2219 assert_eq!(
2220 Err(make_overflow_err()),
2221 Bn::<u16, 4>::from_str_radix("f81906f5e4d3c2c01", 16)
2222 );
2223 assert_eq!(
2224 Err(make_overflow_err()),
2225 Bn::<u16, 4>::from_str_radix("af81906f5e4d3c2c01", 16)
2226 );
2227 assert_eq!(
2228 Err(make_overflow_err()),
2229 Bn::<u16, 4>::from_str_radix("baaf81906f5e4d3c2c01", 16)
2230 );
2231 let ac = Bn::<u16, 5>::from_str_radix("baaf81906f5e4d3c2c01", 16).unwrap();
2232 assert_eq!(ac.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190, 0xbaaf]);
2233 }
2234
2235 #[test]
2236 fn test_resize() {
2237 type TestInt1 = FixedUInt<u32, 1>;
2238 type TestInt2 = FixedUInt<u32, 2>;
2239
2240 let a = TestInt1::from(u32::MAX);
2241 let b: TestInt2 = a.resize();
2242 assert_eq!(b, TestInt2::from([u32::MAX, 0]));
2243
2244 let a = TestInt2::from([u32::MAX, u32::MAX]);
2245 let b: TestInt1 = a.resize();
2246 assert_eq!(b, TestInt1::from(u32::MAX));
2247 }
2248
2249 #[test]
2250 fn test_bit_length() {
2251 assert_eq!(0, Bn8::from_u8(0).unwrap().bit_length());
2252 assert_eq!(1, Bn8::from_u8(1).unwrap().bit_length());
2253 assert_eq!(2, Bn8::from_u8(2).unwrap().bit_length());
2254 assert_eq!(2, Bn8::from_u8(3).unwrap().bit_length());
2255 assert_eq!(7, Bn8::from_u8(0x70).unwrap().bit_length());
2256 assert_eq!(8, Bn8::from_u8(0xF0).unwrap().bit_length());
2257 assert_eq!(9, Bn8::from_u16(0x1F0).unwrap().bit_length());
2258
2259 assert_eq!(20, Bn8::from_u64(990223).unwrap().bit_length());
2260 assert_eq!(32, Bn8::from_u64(0xefffffff).unwrap().bit_length());
2261 assert_eq!(32, Bn8::from_u64(0x8fffffff).unwrap().bit_length());
2262 assert_eq!(31, Bn8::from_u64(0x7fffffff).unwrap().bit_length());
2263 assert_eq!(34, Bn8::from_u64(0x3ffffffff).unwrap().bit_length());
2264
2265 assert_eq!(0, Bn32::from_u8(0).unwrap().bit_length());
2266 assert_eq!(1, Bn32::from_u8(1).unwrap().bit_length());
2267 assert_eq!(2, Bn32::from_u8(2).unwrap().bit_length());
2268 assert_eq!(2, Bn32::from_u8(3).unwrap().bit_length());
2269 assert_eq!(7, Bn32::from_u8(0x70).unwrap().bit_length());
2270 assert_eq!(8, Bn32::from_u8(0xF0).unwrap().bit_length());
2271 assert_eq!(9, Bn32::from_u16(0x1F0).unwrap().bit_length());
2272
2273 assert_eq!(20, Bn32::from_u64(990223).unwrap().bit_length());
2274 assert_eq!(32, Bn32::from_u64(0xefffffff).unwrap().bit_length());
2275 assert_eq!(32, Bn32::from_u64(0x8fffffff).unwrap().bit_length());
2276 assert_eq!(31, Bn32::from_u64(0x7fffffff).unwrap().bit_length());
2277 assert_eq!(34, Bn32::from_u64(0x3ffffffff).unwrap().bit_length());
2278 }
2279
2280 #[test]
2281 fn test_bit_length_1000() {
2282 let value = Bn32::from_u16(1000).unwrap();
2284
2285 assert_eq!(value.to_u32().unwrap(), 1000);
2288 assert_eq!(value.bit_length(), 10);
2289
2290 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);
2297 assert_eq!(Bn16::from_u16(1000).unwrap().bit_length(), 10);
2298
2299 let value_from_str = Bn32::from_str_radix("1000", 10).unwrap();
2301 assert_eq!(value_from_str.bit_length(), 10);
2302
2303 let value_from_bytes = Bn32::from_le_bytes(&1000u16.to_le_bytes());
2305 assert_eq!(
2307 value_from_bytes.to_u32().unwrap_or(0),
2308 1000,
2309 "from_le_bytes didn't create the correct value"
2310 );
2311 assert_eq!(value_from_bytes.bit_length(), 10);
2312 }
2313 #[test]
2314 fn test_cmp() {
2315 let f0 = <Bn8 as Zero>::zero();
2316 let f1 = <Bn8 as Zero>::zero();
2317 let f2 = <Bn8 as One>::one();
2318 assert_eq!(f0, f1);
2319 assert!(f2 > f0);
2320 assert!(f0 < f2);
2321 let f3 = Bn32::from_u64(990223).unwrap();
2322 assert_eq!(f3, Bn32::from_u64(990223).unwrap());
2323 let f4 = Bn32::from_u64(990224).unwrap();
2324 assert!(f4 > Bn32::from_u64(990223).unwrap());
2325
2326 let f3 = Bn8::from_u64(990223).unwrap();
2327 assert_eq!(f3, Bn8::from_u64(990223).unwrap());
2328 let f4 = Bn8::from_u64(990224).unwrap();
2329 assert!(f4 > Bn8::from_u64(990223).unwrap());
2330
2331 #[cfg(feature = "nightly")]
2332 {
2333 use core::cmp::Ordering;
2334
2335 const A: FixedUInt<u8, 2> = FixedUInt::from_array([10, 0]);
2336 const B: FixedUInt<u8, 2> = FixedUInt::from_array([20, 0]);
2337 const C: FixedUInt<u8, 2> = FixedUInt::from_array([10, 0]);
2338
2339 const CMP_LT: Ordering = A.cmp(&B);
2340 const CMP_GT: Ordering = B.cmp(&A);
2341 const CMP_EQ: Ordering = A.cmp(&C);
2342 const EQ_TRUE: bool = A.eq(&C);
2343 const EQ_FALSE: bool = A.eq(&B);
2344
2345 assert_eq!(CMP_LT, Ordering::Less);
2346 assert_eq!(CMP_GT, Ordering::Greater);
2347 assert_eq!(CMP_EQ, Ordering::Equal);
2348 assert!(EQ_TRUE);
2349 assert!(!EQ_FALSE);
2350 }
2351 }
2352
2353 #[test]
2354 fn test_default() {
2355 let d: Bn8 = Default::default();
2356 assert!(Zero::is_zero(&d));
2357
2358 #[cfg(feature = "nightly")]
2359 {
2360 const D: FixedUInt<u8, 2> = <FixedUInt<u8, 2> as Default>::default();
2361 assert!(Zero::is_zero(&D));
2362 }
2363 }
2364
2365 #[test]
2366 fn test_clone() {
2367 let a: Bn8 = 42u8.into();
2368 let b = a.clone();
2369 assert_eq!(a, b);
2370
2371 #[cfg(feature = "nightly")]
2372 {
2373 const A: FixedUInt<u8, 2> = FixedUInt::from_array([42, 0]);
2374 const B: FixedUInt<u8, 2> = A.clone();
2375 assert_eq!(A.array, B.array);
2376 }
2377 }
2378
2379 #[test]
2380 fn test_le_be_bytes() {
2381 let le_bytes = [1, 2, 3, 4];
2382 let be_bytes = [4, 3, 2, 1];
2383 let u8_ver = FixedUInt::<u8, 4>::from_le_bytes(&le_bytes);
2384 let u16_ver = FixedUInt::<u16, 2>::from_le_bytes(&le_bytes);
2385 let u32_ver = FixedUInt::<u32, 1>::from_le_bytes(&le_bytes);
2386 let u8_ver_be = FixedUInt::<u8, 4>::from_be_bytes(&be_bytes);
2387 let u16_ver_be = FixedUInt::<u16, 2>::from_be_bytes(&be_bytes);
2388 let u32_ver_be = FixedUInt::<u32, 1>::from_be_bytes(&be_bytes);
2389
2390 assert_eq!(u8_ver.array, [1, 2, 3, 4]);
2391 assert_eq!(u16_ver.array, [0x0201, 0x0403]);
2392 assert_eq!(u32_ver.array, [0x04030201]);
2393 assert_eq!(u8_ver_be.array, [1, 2, 3, 4]);
2394 assert_eq!(u16_ver_be.array, [0x0201, 0x0403]);
2395 assert_eq!(u32_ver_be.array, [0x04030201]);
2396
2397 let mut output_buffer = [0u8; 16];
2398 assert_eq!(u8_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
2399 assert_eq!(u8_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
2400 assert_eq!(u16_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
2401 assert_eq!(u16_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
2402 assert_eq!(u32_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
2403 assert_eq!(u32_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
2404 }
2405
2406 #[test]
2408 fn test_div_small() {
2409 type TestInt = FixedUInt<u8, 2>;
2410
2411 let test_cases = [
2413 (20u16, 3u16, 6u16), (100u16, 7u16, 14u16), (255u16, 5u16, 51u16), (65535u16, 256u16, 255u16), ];
2418
2419 for (dividend_val, divisor_val, expected) in test_cases {
2420 let dividend = TestInt::from(dividend_val);
2421 let divisor = TestInt::from(divisor_val);
2422 let expected_result = TestInt::from(expected);
2423
2424 assert_eq!(
2425 dividend / divisor,
2426 expected_result,
2427 "Division failed for {} / {} = {}",
2428 dividend_val,
2429 divisor_val,
2430 expected
2431 );
2432 }
2433 }
2434
2435 #[test]
2436 fn test_div_edge_cases() {
2437 type TestInt = FixedUInt<u16, 2>;
2438
2439 let dividend = TestInt::from(1000u16);
2441 let divisor = TestInt::from(1u16);
2442 assert_eq!(dividend / divisor, TestInt::from(1000u16));
2443
2444 let dividend = TestInt::from(42u16);
2446 let divisor = TestInt::from(42u16);
2447 assert_eq!(dividend / divisor, TestInt::from(1u16));
2448
2449 let dividend = TestInt::from(5u16);
2451 let divisor = TestInt::from(10u16);
2452 assert_eq!(dividend / divisor, TestInt::from(0u16));
2453
2454 let dividend = TestInt::from(1024u16);
2456 let divisor = TestInt::from(4u16);
2457 assert_eq!(dividend / divisor, TestInt::from(256u16));
2458 }
2459
2460 #[test]
2461 fn test_helper_methods() {
2462 type TestInt = FixedUInt<u8, 2>;
2463
2464 let mut val = <TestInt as Zero>::zero();
2466 const_set_bit(&mut val.array, 0);
2467 assert_eq!(val, TestInt::from(1u8));
2468
2469 const_set_bit(&mut val.array, 8);
2470 assert_eq!(val, TestInt::from(257u16)); let a = TestInt::from(8u8); let b = TestInt::from(1u8); assert_eq!(
2478 const_cmp_shifted(&a.array, &b.array, 3),
2479 core::cmp::Ordering::Equal
2480 );
2481
2482 assert_eq!(
2484 const_cmp_shifted(&a.array, &b.array, 2),
2485 core::cmp::Ordering::Greater
2486 );
2487
2488 assert_eq!(
2490 const_cmp_shifted(&a.array, &b.array, 4),
2491 core::cmp::Ordering::Less
2492 );
2493
2494 let mut val = TestInt::from(10u8);
2496 let one = TestInt::from(1u8);
2497 const_sub_shifted(&mut val.array, &one.array, 2); assert_eq!(val, TestInt::from(6u8)); }
2500
2501 #[test]
2502 fn test_shifted_operations_comprehensive() {
2503 type TestInt = FixedUInt<u32, 2>;
2504
2505 let a = TestInt::from(0x12345678u32);
2507 let b = TestInt::from(0x12345678u32);
2508
2509 assert_eq!(
2511 const_cmp_shifted(&a.array, &b.array, 0),
2512 core::cmp::Ordering::Equal
2513 );
2514
2515 let c = TestInt::from(0x123u32); let d = TestInt::from(0x48d159e2u32); assert_eq!(
2521 const_cmp_shifted(&d.array, &c.array, 16),
2522 core::cmp::Ordering::Greater
2523 );
2524
2525 let e = TestInt::from(1u32);
2527 let zero = TestInt::from(0u32);
2528 assert_eq!(
2529 const_cmp_shifted(&e.array, &zero.array, 100),
2530 core::cmp::Ordering::Greater
2531 );
2532 assert_eq!(
2534 const_cmp_shifted(&zero.array, &e.array, 100),
2535 core::cmp::Ordering::Equal
2536 );
2537
2538 let mut val = TestInt::from(0x10000u32); let one = TestInt::from(1u32);
2541 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)); }
2549
2550 #[test]
2551 fn test_shifted_operations_edge_cases() {
2552 type TestInt = FixedUInt<u32, 2>;
2553
2554 let a = TestInt::from(42u32);
2556 let a2 = TestInt::from(42u32);
2557 assert_eq!(
2558 const_cmp_shifted(&a.array, &a2.array, 0),
2559 core::cmp::Ordering::Equal
2560 );
2561
2562 let mut b = TestInt::from(42u32);
2563 let ten = TestInt::from(10u32);
2564 const_sub_shifted(&mut b.array, &ten.array, 0);
2565 assert_eq!(b, TestInt::from(32u32));
2566
2567 let c = TestInt::from(123u32);
2569 let large = TestInt::from(456u32);
2570 assert_eq!(
2571 const_cmp_shifted(&c.array, &large.array, 200),
2572 core::cmp::Ordering::Greater
2573 );
2574
2575 let mut d = TestInt::from(123u32);
2576 const_sub_shifted(&mut d.array, &large.array, 200); assert_eq!(d, TestInt::from(123u32));
2578
2579 let zero = TestInt::from(0u32);
2581 let one = TestInt::from(1u32);
2582 assert_eq!(
2583 const_cmp_shifted(&zero.array, &zero.array, 10),
2584 core::cmp::Ordering::Equal
2585 );
2586 assert_eq!(
2587 const_cmp_shifted(&one.array, &zero.array, 10),
2588 core::cmp::Ordering::Greater
2589 );
2590 }
2591
2592 #[test]
2593 fn test_shifted_operations_equivalence() {
2594 type TestInt = FixedUInt<u32, 2>;
2595
2596 let test_cases = [
2598 (0x12345u32, 0x678u32, 4),
2599 (0x1000u32, 0x10u32, 8),
2600 (0xABCDu32, 0x1u32, 16),
2601 (0x80000000u32, 0x1u32, 1),
2602 ];
2603
2604 for (a_val, b_val, shift) in test_cases {
2605 let a = TestInt::from(a_val);
2606 let b = TestInt::from(b_val);
2607
2608 let optimized_cmp = const_cmp_shifted(&a.array, &b.array, shift);
2610 let naive_cmp = a.cmp(&(b << shift));
2611 assert_eq!(
2612 optimized_cmp, naive_cmp,
2613 "cmp_shifted mismatch: {} vs ({} << {})",
2614 a_val, b_val, shift
2615 );
2616
2617 if a >= (b << shift) {
2619 let mut optimized_result = a;
2620 const_sub_shifted(&mut optimized_result.array, &b.array, shift);
2621
2622 let naive_result = a - (b << shift);
2623 assert_eq!(
2624 optimized_result, naive_result,
2625 "sub_shifted mismatch: {} - ({} << {})",
2626 a_val, b_val, shift
2627 );
2628 }
2629 }
2630 }
2631
2632 #[test]
2633 fn test_div_assign_in_place_optimization() {
2634 type TestInt = FixedUInt<u32, 2>;
2635
2636 let test_cases = [
2638 (100u32, 10u32, 10u32, 0u32), (123u32, 7u32, 17u32, 4u32), (1000u32, 13u32, 76u32, 12u32), (65535u32, 255u32, 257u32, 0u32), ];
2643
2644 for (dividend_val, divisor_val, expected_quotient, expected_remainder) in test_cases {
2645 let mut dividend = TestInt::from(dividend_val);
2647 let divisor = TestInt::from(divisor_val);
2648
2649 dividend /= divisor;
2650 assert_eq!(
2651 dividend,
2652 TestInt::from(expected_quotient),
2653 "div_assign: {} / {} should be {}",
2654 dividend_val,
2655 divisor_val,
2656 expected_quotient
2657 );
2658
2659 let dividend2 = TestInt::from(dividend_val);
2661 let (quotient, remainder) = dividend2.div_rem(&divisor);
2662 assert_eq!(
2663 quotient,
2664 TestInt::from(expected_quotient),
2665 "div_rem quotient: {} / {} should be {}",
2666 dividend_val,
2667 divisor_val,
2668 expected_quotient
2669 );
2670 assert_eq!(
2671 remainder,
2672 TestInt::from(expected_remainder),
2673 "div_rem remainder: {} % {} should be {}",
2674 dividend_val,
2675 divisor_val,
2676 expected_remainder
2677 );
2678
2679 assert_eq!(
2681 quotient * divisor + remainder,
2682 TestInt::from(dividend_val),
2683 "Property check failed for {}",
2684 dividend_val
2685 );
2686 }
2687 }
2688
2689 #[test]
2690 fn test_div_assign_stack_efficiency() {
2691 type TestInt = FixedUInt<u32, 4>; let mut dividend = TestInt::from(0x123456789ABCDEFu64);
2695 let divisor = TestInt::from(0x12345u32);
2696 let original_dividend = dividend;
2697
2698 dividend /= divisor;
2700
2701 let remainder = original_dividend % divisor;
2703 assert_eq!(dividend * divisor + remainder, original_dividend);
2704 }
2705
2706 #[test]
2707 fn test_rem_assign_optimization() {
2708 type TestInt = FixedUInt<u32, 2>;
2709
2710 let test_cases = [
2711 (100u32, 10u32, 0u32), (123u32, 7u32, 4u32), (1000u32, 13u32, 12u32), (65535u32, 255u32, 0u32), ];
2716
2717 for (dividend_val, divisor_val, expected_remainder) in test_cases {
2718 let mut dividend = TestInt::from(dividend_val);
2719 let divisor = TestInt::from(divisor_val);
2720
2721 dividend %= divisor;
2722 assert_eq!(
2723 dividend,
2724 TestInt::from(expected_remainder),
2725 "rem_assign: {} % {} should be {}",
2726 dividend_val,
2727 divisor_val,
2728 expected_remainder
2729 );
2730 }
2731 }
2732
2733 #[test]
2734 fn test_div_with_remainder_property() {
2735 type TestInt = FixedUInt<u32, 2>;
2736
2737 let test_cases = [
2739 (100u32, 10u32, 10u32), (123u32, 7u32, 17u32), (1000u32, 13u32, 76u32), (65535u32, 255u32, 257u32), ];
2744
2745 for (dividend_val, divisor_val, expected_quotient) in test_cases {
2746 let dividend = TestInt::from(dividend_val);
2747 let divisor = TestInt::from(divisor_val);
2748
2749 let quotient = dividend / divisor;
2751 assert_eq!(
2752 quotient,
2753 TestInt::from(expected_quotient),
2754 "Division: {} / {} should be {}",
2755 dividend_val,
2756 divisor_val,
2757 expected_quotient
2758 );
2759
2760 let remainder = dividend % divisor;
2762 assert_eq!(
2763 quotient * divisor + remainder,
2764 dividend,
2765 "Division property check failed for {}",
2766 dividend_val
2767 );
2768 }
2769 }
2770
2771 #[test]
2772 fn test_code_simplification_benefits() {
2773 type TestInt = FixedUInt<u32, 2>;
2774
2775 let dividend = TestInt::from(12345u32);
2777 let divisor = TestInt::from(67u32);
2778 let quotient = dividend / divisor;
2779 let remainder = dividend % divisor;
2780
2781 assert_eq!(quotient * divisor + remainder, dividend);
2783 }
2784
2785 #[test]
2786 fn test_rem_assign_correctness_after_fix() {
2787 type TestInt = FixedUInt<u32, 2>;
2788
2789 let mut a = TestInt::from(17u32);
2791 let b = TestInt::from(5u32);
2792
2793 a %= b;
2796 assert_eq!(a, TestInt::from(2u32), "17 % 5 should be 2");
2797
2798 let mut test_val = TestInt::from(100u32);
2800 test_val %= TestInt::from(7u32);
2801 assert_eq!(
2802 test_val,
2803 TestInt::from(2u32),
2804 "100 % 7 should be 2 (not 14, the quotient)"
2805 );
2806 }
2807
2808 #[test]
2809 fn test_div_property_based() {
2810 type TestInt = FixedUInt<u16, 2>;
2811
2812 let test_pairs = [
2814 (12345u16, 67u16),
2815 (1000u16, 13u16),
2816 (65535u16, 255u16),
2817 (5000u16, 7u16),
2818 ];
2819
2820 for (dividend_val, divisor_val) in test_pairs {
2821 let dividend = TestInt::from(dividend_val);
2822 let divisor = TestInt::from(divisor_val);
2823
2824 let quotient = dividend / divisor;
2825
2826 let remainder = dividend - (quotient * divisor);
2828 let reconstructed = quotient * divisor + remainder;
2829
2830 assert_eq!(
2831 reconstructed,
2832 dividend,
2833 "Property failed for {} / {}: {} * {} + {} != {}",
2834 dividend_val,
2835 divisor_val,
2836 quotient.to_u32().unwrap_or(0),
2837 divisor_val,
2838 remainder.to_u32().unwrap_or(0),
2839 dividend_val
2840 );
2841
2842 assert!(
2844 remainder < divisor,
2845 "Remainder {} >= divisor {} for {} / {}",
2846 remainder.to_u32().unwrap_or(0),
2847 divisor_val,
2848 dividend_val,
2849 divisor_val
2850 );
2851 }
2852 }
2853}