1use num_traits::{ToPrimitive, Zero};
16
17use core::convert::TryFrom;
18use core::fmt::Write;
19
20pub use crate::const_numtraits::{
21 ConstAbsDiff, ConstBorrowingSub, ConstBounded, ConstCarryingAdd, ConstCarryingMul,
22 ConstCheckedPow, ConstDivCeil, ConstIlog, ConstIsqrt, ConstMultiple, ConstOne, ConstPowerOfTwo,
23 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
58#[cfg(feature = "zeroize")]
59use zeroize::DefaultIsZeroes;
60
61#[derive(Debug, Copy)]
63pub struct FixedUInt<T, const N: usize>
64where
65 T: MachineWord,
66{
67 pub(super) array: [T; N],
69}
70
71#[cfg(feature = "zeroize")]
72impl<T: MachineWord, const N: usize> DefaultIsZeroes for FixedUInt<T, N> {}
73
74impl<T, const N: usize> From<[T; N]> for FixedUInt<T, N>
75where
76 T: MachineWord,
77{
78 fn from(array: [T; N]) -> Self {
79 Self { array }
80 }
81}
82
83const LONGEST_WORD_IN_BITS: usize = 128;
84
85impl<T: MachineWord, const N: usize> FixedUInt<T, N> {
86 const WORD_SIZE: usize = core::mem::size_of::<T>();
87 const WORD_BITS: usize = Self::WORD_SIZE * 8;
88 const BYTE_SIZE: usize = Self::WORD_SIZE * N;
89 const BIT_SIZE: usize = Self::BYTE_SIZE * 8;
90
91 pub fn new() -> FixedUInt<T, N> {
93 FixedUInt {
94 array: [T::zero(); N],
95 }
96 }
97
98 pub fn words(&self) -> &[T; N] {
100 &self.array
101 }
102
103 pub fn bit_length(&self) -> u32 {
105 Self::BIT_SIZE as u32 - ConstPrimInt::leading_zeros(*self)
106 }
107
108 pub fn div_rem(&self, divisor: &Self) -> (Self, Self) {
110 let (quotient, remainder) = const_div_rem(&self.array, &divisor.array);
111 (Self { array: quotient }, Self { array: remainder })
112 }
113}
114
115c0nst::c0nst! {
117 pub(crate) c0nst fn impl_from_le_bytes_slice<T: [c0nst] ConstMachineWord, const N: usize>(
120 bytes: &[u8],
121 ) -> [T; N] {
122 let word_size = core::mem::size_of::<T>();
123 let mut ret: [T; N] = [T::zero(); N];
124 let capacity = N * word_size;
125 let total_bytes = if bytes.len() < capacity { bytes.len() } else { capacity };
126
127 let mut byte_index = 0;
128 while byte_index < total_bytes {
129 let word_index = byte_index / word_size;
130 let byte_in_word = byte_index % word_size;
131
132 let byte_value: T = T::from(bytes[byte_index]);
133 let shifted_value = byte_value.shl(byte_in_word * 8);
134 ret[word_index] = ret[word_index].bitor(shifted_value);
135 byte_index += 1;
136 }
137 ret
138 }
139
140 pub(crate) c0nst fn impl_from_be_bytes_slice<T: [c0nst] ConstMachineWord, const N: usize>(
143 bytes: &[u8],
144 ) -> [T; N] {
145 let word_size = core::mem::size_of::<T>();
146 let mut ret: [T; N] = [T::zero(); N];
147 let capacity_bytes = N * word_size;
148 let total_bytes = if bytes.len() < capacity_bytes { bytes.len() } else { capacity_bytes };
149
150 let start_offset = if bytes.len() > capacity_bytes {
153 bytes.len() - capacity_bytes
154 } else {
155 0
156 };
157
158 let mut byte_index = 0;
159 while byte_index < total_bytes {
160 let be_byte_index = start_offset + total_bytes - 1 - byte_index;
162 let word_index = byte_index / word_size;
163 let byte_in_word = byte_index % word_size;
164
165 let byte_value: T = T::from(bytes[be_byte_index]);
166 let shifted_value = byte_value.shl(byte_in_word * 8);
167 ret[word_index] = ret[word_index].bitor(shifted_value);
168 byte_index += 1;
169 }
170 ret
171 }
172}
173
174impl<T: MachineWord, const N: usize> FixedUInt<T, N> {
176 pub fn from_le_bytes(bytes: &[u8]) -> Self {
178 Self {
179 array: impl_from_le_bytes_slice::<T, N>(bytes),
180 }
181 }
182
183 pub fn from_be_bytes(bytes: &[u8]) -> Self {
185 Self {
186 array: impl_from_be_bytes_slice::<T, N>(bytes),
187 }
188 }
189}
190
191impl<T: MachineWord, const N: usize> FixedUInt<T, N> {
192 pub fn to_le_bytes<'a>(&self, output_buffer: &'a mut [u8]) -> Result<&'a [u8], bool> {
194 let total_bytes = N * Self::WORD_SIZE;
195 if output_buffer.len() < total_bytes {
196 return Err(false); }
198 for (i, word) in self.array.iter().enumerate() {
199 let start = i * Self::WORD_SIZE;
200 let end = start + Self::WORD_SIZE;
201 let word_bytes = word.to_le_bytes();
202 output_buffer[start..end].copy_from_slice(word_bytes.as_ref());
203 }
204 Ok(&output_buffer[..total_bytes])
205 }
206
207 pub fn to_be_bytes<'a>(&self, output_buffer: &'a mut [u8]) -> Result<&'a [u8], bool> {
209 let total_bytes = N * Self::WORD_SIZE;
210 if output_buffer.len() < total_bytes {
211 return Err(false); }
213 for (i, word) in self.array.iter().rev().enumerate() {
214 let start = i * Self::WORD_SIZE;
215 let end = start + Self::WORD_SIZE;
216 let word_bytes = word.to_be_bytes();
217 output_buffer[start..end].copy_from_slice(word_bytes.as_ref());
218 }
219 Ok(&output_buffer[..total_bytes])
220 }
221
222 pub fn to_hex_str<'a>(&self, result: &'a mut [u8]) -> Result<&'a str, core::fmt::Error> {
224 type Error = core::fmt::Error;
225
226 let word_size = Self::WORD_SIZE;
227 let need_bits = self.bit_length() as usize;
229 let need_chars = if need_bits > 0 { need_bits / 4 } else { 0 };
231
232 if result.len() < need_chars {
233 return Err(Error {});
235 }
236 let offset = result.len() - need_chars;
237 for i in result.iter_mut() {
238 *i = b'0';
239 }
240
241 for iter_words in 0..self.array.len() {
242 let word = self.array[iter_words];
243 let mut encoded = [0u8; LONGEST_WORD_IN_BITS / 4];
244 let encode_slice = &mut encoded[0..word_size * 2];
245 let mut wordbytes = word.to_le_bytes();
246 wordbytes.as_mut().reverse();
247 let wordslice = wordbytes.as_ref();
248 to_slice_hex(wordslice, encode_slice).map_err(|_| Error {})?;
249 for iter_chars in 0..encode_slice.len() {
250 let copy_char_to = (iter_words * word_size * 2) + iter_chars;
251 if copy_char_to <= need_chars {
252 let reverse_index = offset + (need_chars - copy_char_to);
253 if reverse_index <= result.len() && reverse_index > 0 {
254 let current_char = encode_slice[(encode_slice.len() - 1) - iter_chars];
255 result[reverse_index - 1] = current_char;
256 }
257 }
258 }
259 }
260
261 let convert = core::str::from_utf8(result).map_err(|_| Error {})?;
262 let pos = convert.find(|c: char| c != '0');
263 match pos {
264 Some(x) => Ok(&convert[x..convert.len()]),
265 None => {
266 if convert.starts_with('0') {
267 Ok("0")
268 } else {
269 Ok(convert)
270 }
271 }
272 }
273 }
274
275 pub fn to_radix_str<'a>(
277 &self,
278 result: &'a mut [u8],
279 radix: u8,
280 ) -> Result<&'a str, core::fmt::Error> {
281 type Error = core::fmt::Error;
282
283 if !(2..=16).contains(&radix) {
284 return Err(Error {}); }
286 for byte in result.iter_mut() {
287 *byte = b'0';
288 }
289 if Zero::is_zero(self) {
290 if !result.is_empty() {
291 result[0] = b'0';
292 return core::str::from_utf8(&result[0..1]).map_err(|_| Error {});
293 } else {
294 return Err(Error {});
295 }
296 }
297
298 let mut number = *self;
299 let mut idx = result.len();
300
301 let radix_t = Self::from(radix);
302
303 while !Zero::is_zero(&number) {
304 if idx == 0 {
305 return Err(Error {}); }
307
308 idx -= 1;
309 let (quotient, remainder) = number.div_rem(&radix_t);
310
311 let digit = remainder.to_u8().unwrap();
312 result[idx] = match digit {
313 0..=9 => b'0' + digit, 10..=16 => b'a' + (digit - 10), _ => return Err(Error {}),
316 };
317
318 number = quotient;
319 }
320
321 let start = result[idx..].iter().position(|&c| c != b'0').unwrap_or(0);
322 let radix_str = core::str::from_utf8(&result[idx + start..]).map_err(|_| Error {})?;
323 Ok(radix_str)
324 }
325
326 #[must_use]
331 pub fn resize<const N2: usize>(&self) -> FixedUInt<T, N2> {
332 let mut array = [T::zero(); N2];
333 let min_size = N.min(N2);
334 array[..min_size].copy_from_slice(&self.array[..min_size]);
335 FixedUInt { array }
336 }
337
338 fn hex_fmt(
339 &self,
340 formatter: &mut core::fmt::Formatter<'_>,
341 uppercase: bool,
342 ) -> Result<(), core::fmt::Error>
343 where
344 u8: core::convert::TryFrom<T>,
345 {
346 type Err = core::fmt::Error;
347
348 fn to_casedigit(byte: u8, uppercase: bool) -> Result<char, core::fmt::Error> {
349 let digit = core::char::from_digit(byte as u32, 16).ok_or(Err {})?;
350 if uppercase {
351 digit.to_uppercase().next().ok_or(Err {})
352 } else {
353 digit.to_lowercase().next().ok_or(Err {})
354 }
355 }
356
357 let mut leading_zero: bool = true;
358
359 let mut maybe_write = |nibble: char| -> Result<(), core::fmt::Error> {
360 leading_zero &= nibble == '0';
361 if !leading_zero {
362 formatter.write_char(nibble)?;
363 }
364 Ok(())
365 };
366
367 for index in (0..N).rev() {
368 let val = self.array[index];
369 let mask: T = 0xff.into();
370 for j in (0..Self::WORD_SIZE as u32).rev() {
371 let masked = val & mask.shl((j * 8) as usize);
372
373 let byte = u8::try_from(masked.shr((j * 8) as usize)).map_err(|_| Err {})?;
374
375 maybe_write(to_casedigit((byte & 0xf0) >> 4, uppercase)?)?;
376 maybe_write(to_casedigit(byte & 0x0f, uppercase)?)?;
377 }
378 }
379 Ok(())
380 }
381}
382
383c0nst::c0nst! {
384 pub(crate) c0nst fn add_impl<T: [c0nst] ConstMachineWord, const N: usize>(
386 target: &mut [T; N],
387 other: &[T; N]
388 ) -> bool {
389 let mut carry = T::zero();
390 let mut i = 0usize;
391 while i < N {
392 let (res, carry1) = target[i].overflowing_add(&other[i]);
393 let (res, carry2) = res.overflowing_add(&carry);
394 carry = if carry1 || carry2 {
395 T::one()
396 } else {
397 T::zero()
398 };
399 target[i] = res;
400 i += 1;
401 }
402 !carry.is_zero()
403 }
404}
405
406c0nst::c0nst! {
407 pub(crate) c0nst fn sub_impl<T: [c0nst] ConstMachineWord, const N: usize>(
409 target: &mut [T; N],
410 other: &[T; N]
411 ) -> bool {
412 let mut borrow = T::zero();
413 let mut i = 0usize;
414 while i < N {
415 let (res, borrow1) = target[i].overflowing_sub(&other[i]);
416 let (res, borrow2) = res.overflowing_sub(&borrow);
417 borrow = if borrow1 || borrow2 {
418 T::one()
419 } else {
420 T::zero()
421 };
422 target[i] = res;
423 i += 1;
424 }
425 !borrow.is_zero()
426 }
427}
428
429c0nst::c0nst! {
430 pub(crate) c0nst fn const_shl_impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
432 target: &mut FixedUInt<T, N>,
433 bits: usize,
434 ) {
435 if N == 0 {
436 return;
437 }
438 let word_bits = FixedUInt::<T, N>::WORD_BITS;
439 let nwords = bits / word_bits;
440 let nbits = bits - nwords * word_bits;
441
442 if nwords >= N {
444 let mut i = 0;
445 while i < N {
446 target.array[i] = T::zero();
447 i += 1;
448 }
449 return;
450 }
451
452 let mut i = N;
454 while i > nwords {
455 i -= 1;
456 target.array[i] = target.array[i - nwords];
457 }
458 let mut i = 0;
460 while i < nwords {
461 target.array[i] = T::zero();
462 i += 1;
463 }
464
465 if nbits != 0 {
466 let mut i = N;
468 while i > 1 {
469 i -= 1;
470 let right = target.array[i] << nbits;
471 let left = target.array[i - 1] >> (word_bits - nbits);
472 target.array[i] = right | left;
473 }
474 target.array[0] <<= nbits;
475 }
476 }
477
478 pub(crate) c0nst fn const_shr_impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
480 target: &mut FixedUInt<T, N>,
481 bits: usize,
482 ) {
483 if N == 0 {
484 return;
485 }
486 let word_bits = FixedUInt::<T, N>::WORD_BITS;
487 let nwords = bits / word_bits;
488 let nbits = bits - nwords * word_bits;
489
490 if nwords >= N {
492 let mut i = 0;
493 while i < N {
494 target.array[i] = T::zero();
495 i += 1;
496 }
497 return;
498 }
499
500 let last_index = N - 1;
501 let last_word = N - nwords;
502
503 let mut i = 0;
505 while i < last_word {
506 target.array[i] = target.array[i + nwords];
507 i += 1;
508 }
509
510 let mut i = last_word;
512 while i < N {
513 target.array[i] = T::zero();
514 i += 1;
515 }
516
517 if nbits != 0 {
518 let mut i = 0;
520 while i < last_index {
521 let left = target.array[i] >> nbits;
522 let right = target.array[i + 1] << (word_bits - nbits);
523 target.array[i] = left | right;
524 i += 1;
525 }
526 target.array[last_index] >>= nbits;
527 }
528 }
529
530 pub(crate) c0nst fn const_mul<T: [c0nst] ConstMachineWord, const N: usize, const CHECK_OVERFLOW: bool>(
533 op1: &[T; N],
534 op2: &[T; N],
535 word_bits: usize,
536 ) -> ([T; N], bool) {
537 let mut result: [T; N] = [<T as ConstZero>::zero(); N];
538 let mut overflowed = false;
539 let t_max = <T as ConstMachineWord>::to_double(<T as ConstBounded>::max_value());
540 let dw_zero = <<T as ConstMachineWord>::ConstDoubleWord as ConstZero>::zero();
542
543 let mut i = 0;
544 while i < N {
545 let mut carry = dw_zero;
546 let mut j = 0;
547 while j < N {
548 let round = i + j;
549 let op1_dw = <T as ConstMachineWord>::to_double(op1[i]);
550 let op2_dw = <T as ConstMachineWord>::to_double(op2[j]);
551 let mul_res = op1_dw * op2_dw;
552 let mut accumulator = if round < N {
553 <T as ConstMachineWord>::to_double(result[round])
554 } else {
555 dw_zero
556 };
557 accumulator = accumulator + mul_res + carry;
558
559 if accumulator > t_max {
560 carry = accumulator >> word_bits;
561 accumulator &= t_max;
562 } else {
563 carry = dw_zero;
564 }
565 if round < N {
566 result[round] = <T as ConstMachineWord>::from_double(accumulator);
567 } else if CHECK_OVERFLOW {
568 overflowed = overflowed || accumulator != dw_zero;
569 }
570 j += 1;
571 }
572 if carry != dw_zero && CHECK_OVERFLOW {
573 overflowed = true;
574 }
575 i += 1;
576 }
577 (result, overflowed)
578 }
579
580 pub(crate) c0nst fn const_word_bits<T>() -> usize {
582 core::mem::size_of::<T>() * 8
583 }
584
585 pub(crate) c0nst fn const_cmp_words<T: [c0nst] ConstMachineWord>(a: T, b: T) -> Option<core::cmp::Ordering> {
587 if a > b {
588 Some(core::cmp::Ordering::Greater)
589 } else if a < b {
590 Some(core::cmp::Ordering::Less)
591 } else {
592 None
593 }
594 }
595
596 pub(crate) c0nst fn const_leading_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
598 array: &[T; N],
599 ) -> u32 {
600 let mut ret = 0u32;
601 let mut index = N;
602 while index > 0 {
603 index -= 1;
604 let v = array[index];
605 ret += <T as ConstPrimInt>::leading_zeros(v);
606 if !<T as ConstZero>::is_zero(&v) {
607 break;
608 }
609 }
610 ret
611 }
612
613 pub(crate) c0nst fn const_trailing_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
615 array: &[T; N],
616 ) -> u32 {
617 let mut ret = 0u32;
618 let mut index = 0;
619 while index < N {
620 let v = array[index];
621 ret += <T as ConstPrimInt>::trailing_zeros(v);
622 if !<T as ConstZero>::is_zero(&v) {
623 break;
624 }
625 index += 1;
626 }
627 ret
628 }
629
630 pub(crate) c0nst fn const_bit_length<T: [c0nst] ConstMachineWord, const N: usize>(
632 array: &[T; N],
633 ) -> usize {
634 let word_bits = const_word_bits::<T>();
635 let bit_size = N * word_bits;
636 bit_size - const_leading_zeros::<T, N>(array) as usize
637 }
638
639 pub(crate) c0nst fn const_is_zero<T: [c0nst] ConstMachineWord, const N: usize>(
641 array: &[T; N],
642 ) -> bool {
643 let mut index = 0;
644 while index < N {
645 if !<T as ConstZero>::is_zero(&array[index]) {
646 return false;
647 }
648 index += 1;
649 }
650 true
651 }
652
653 pub(crate) c0nst fn const_set_bit<T: [c0nst] ConstMachineWord, const N: usize>(
659 array: &mut [T; N],
660 pos: usize,
661 ) {
662 let word_bits = const_word_bits::<T>();
663 let word_idx = pos / word_bits;
664 if word_idx >= N {
665 return;
666 }
667 let bit_idx = pos % word_bits;
668 array[word_idx] |= <T as ConstOne>::one() << bit_idx;
669 }
670
671 pub(crate) c0nst fn const_cmp<T: [c0nst] ConstMachineWord, const N: usize>(
676 a: &[T; N],
677 b: &[T; N],
678 ) -> core::cmp::Ordering {
679 let mut index = N;
680 while index > 0 {
681 index -= 1;
682 if let Some(ord) = const_cmp_words(a[index], b[index]) {
683 return ord;
684 }
685 }
686 core::cmp::Ordering::Equal
687 }
688
689 pub(crate) c0nst fn const_get_shifted_word<T: [c0nst] ConstMachineWord, const N: usize>(
694 array: &[T; N],
695 word_idx: usize,
696 word_shift: usize,
697 bit_shift: usize,
698 ) -> T {
699 let word_bits = const_word_bits::<T>();
700
701 if bit_shift >= word_bits {
703 return <T as ConstZero>::zero();
704 }
705
706 if word_idx < word_shift {
707 return <T as ConstZero>::zero();
708 }
709
710 let source_idx = word_idx - word_shift;
711
712 if bit_shift == 0 {
713 if source_idx < N {
714 array[source_idx]
715 } else {
716 <T as ConstZero>::zero()
717 }
718 } else {
719 let mut result = <T as ConstZero>::zero();
720
721 if source_idx < N {
723 result |= array[source_idx] << bit_shift;
724 }
725
726 if source_idx > 0 && source_idx - 1 < N {
728 let high_bits = array[source_idx - 1] >> (word_bits - bit_shift);
729 result |= high_bits;
730 }
731
732 result
733 }
734 }
735
736 pub(crate) c0nst fn const_cmp_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
741 array: &[T; N],
742 other: &[T; N],
743 shift_bits: usize,
744 ) -> core::cmp::Ordering {
745 let word_bits = const_word_bits::<T>();
746
747 if shift_bits == 0 {
748 return const_cmp::<T, N>(array, other);
749 }
750
751 let word_shift = shift_bits / word_bits;
752 if word_shift >= N {
753 if const_is_zero::<T, N>(array) {
755 return core::cmp::Ordering::Equal;
756 } else {
757 return core::cmp::Ordering::Greater;
758 }
759 }
760
761 let bit_shift = shift_bits % word_bits;
762
763 let mut index = N;
765 while index > 0 {
766 index -= 1;
767 let self_word = array[index];
768 let other_shifted_word = const_get_shifted_word::<T, N>(
769 other, index, word_shift, bit_shift
770 );
771
772 if let Some(ord) = const_cmp_words(self_word, other_shifted_word) {
773 return ord;
774 }
775 }
776
777 core::cmp::Ordering::Equal
778 }
779
780 pub(crate) c0nst fn const_sub_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
785 array: &mut [T; N],
786 other: &[T; N],
787 shift_bits: usize,
788 ) {
789 let word_bits = const_word_bits::<T>();
790
791 if shift_bits == 0 {
792 sub_impl::<T, N>(array, other);
793 return;
794 }
795
796 let word_shift = shift_bits / word_bits;
797 if word_shift >= N {
798 return;
799 }
800
801 let bit_shift = shift_bits % word_bits;
802 let mut borrow = T::zero();
803 let mut index = 0;
804 while index < N {
805 let other_word = const_get_shifted_word::<T, N>(other, index, word_shift, bit_shift);
806 let (res, borrow1) = array[index].overflowing_sub(&other_word);
807 let (res, borrow2) = res.overflowing_sub(&borrow);
808 borrow = if borrow1 || borrow2 { T::one() } else { T::zero() };
809 array[index] = res;
810 index += 1;
811 }
812 }
813
814 pub(crate) c0nst fn const_div<T: [c0nst] ConstMachineWord, const N: usize>(
818 dividend: &mut [T; N],
819 divisor: &[T; N],
820 ) -> [T; N] {
821 use core::cmp::Ordering;
822
823 match const_cmp::<T, N>(dividend, divisor) {
824 Ordering::Less => {
826 let remainder = *dividend;
827 let mut i = 0;
828 while i < N {
829 dividend[i] = <T as ConstZero>::zero();
830 i += 1;
831 }
832 return remainder;
833 }
834 Ordering::Equal => {
836 let mut i = 0;
837 while i < N {
838 dividend[i] = <T as ConstZero>::zero();
839 i += 1;
840 }
841 if N > 0 {
842 dividend[0] = <T as ConstOne>::one();
843 }
844 return [<T as ConstZero>::zero(); N];
845 }
846 Ordering::Greater => {}
847 }
848
849 let mut quotient = [<T as ConstZero>::zero(); N];
850
851 let dividend_bits = const_bit_length::<T, N>(dividend);
853 let divisor_bits = const_bit_length::<T, N>(divisor);
854
855 let mut bit_pos = if dividend_bits >= divisor_bits {
856 dividend_bits - divisor_bits
857 } else {
858 0
859 };
860
861 while bit_pos > 0 {
863 let cmp = const_cmp_shifted::<T, N>(dividend, divisor, bit_pos);
864 if !matches!(cmp, Ordering::Less) {
865 break;
866 }
867 bit_pos -= 1;
868 }
869
870 loop {
872 let cmp = const_cmp_shifted::<T, N>(dividend, divisor, bit_pos);
873 if !matches!(cmp, Ordering::Less) {
874 const_sub_shifted::<T, N>(dividend, divisor, bit_pos);
875 const_set_bit::<T, N>(&mut quotient, bit_pos);
876 }
877
878 if bit_pos == 0 {
879 break;
880 }
881 bit_pos -= 1;
882 }
883
884 let remainder = *dividend;
885 *dividend = quotient;
886 remainder
887 }
888
889 pub(crate) c0nst fn const_div_rem<T: [c0nst] ConstMachineWord, const N: usize>(
893 dividend: &[T; N],
894 divisor: &[T; N],
895 ) -> ([T; N], [T; N]) {
896 if const_is_zero(divisor) {
897 maybe_panic(PanicReason::DivByZero)
898 }
899 let mut quotient = *dividend;
900 let remainder = const_div(&mut quotient, divisor);
901 (quotient, remainder)
902 }
903}
904
905c0nst::c0nst! {
906 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst Default for FixedUInt<T, N> {
907 fn default() -> Self {
908 <Self as ConstZero>::zero()
909 }
910 }
911
912 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst Clone for FixedUInt<T, N> {
913 fn clone(&self) -> Self {
914 *self
915 }
916 }
917}
918
919impl<T: MachineWord, const N: usize> num_traits::Unsigned for FixedUInt<T, N> {}
920
921c0nst::c0nst! {
924 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::cmp::PartialEq for FixedUInt<T, N> {
925 fn eq(&self, other: &Self) -> bool {
926 self.array == other.array
927 }
928 }
929
930 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::cmp::Eq for FixedUInt<T, N> {}
931
932 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::cmp::Ord for FixedUInt<T, N> {
933 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
934 const_cmp(&self.array, &other.array)
935 }
936 }
937
938 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::cmp::PartialOrd for FixedUInt<T, N> {
939 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
940 Some(self.cmp(other))
941 }
942 }
943}
944
945c0nst::c0nst! {
950 c0nst fn const_from_le_bytes<T: [c0nst] ConstMachineWord, const N: usize, const B: usize>(
953 bytes: [u8; B],
954 ) -> [T; N] {
955 impl_from_le_bytes_slice::<T, N>(&bytes)
956 }
957
958 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::convert::From<u8> for FixedUInt<T, N> {
959 fn from(x: u8) -> Self {
960 Self { array: const_from_le_bytes(x.to_le_bytes()) }
961 }
962 }
963
964 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::convert::From<u16> for FixedUInt<T, N> {
965 fn from(x: u16) -> Self {
966 Self { array: const_from_le_bytes(x.to_le_bytes()) }
967 }
968 }
969
970 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::convert::From<u32> for FixedUInt<T, N> {
971 fn from(x: u32) -> Self {
972 Self { array: const_from_le_bytes(x.to_le_bytes()) }
973 }
974 }
975
976 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::convert::From<u64> for FixedUInt<T, N> {
977 fn from(x: u64) -> Self {
978 Self { array: const_from_le_bytes(x.to_le_bytes()) }
979 }
980 }
981}
982
983fn make_parse_int_err() -> core::num::ParseIntError {
990 <u8>::from_str_radix("-", 2).err().unwrap()
991}
992fn make_overflow_err() -> core::num::ParseIntError {
993 <u8>::from_str_radix("101", 16).err().unwrap()
994}
995fn make_empty_error() -> core::num::ParseIntError {
996 <u8>::from_str_radix("", 8).err().unwrap()
997}
998
999fn to_slice_hex<T: AsRef<[u8]>>(
1000 input: T,
1001 output: &mut [u8],
1002) -> Result<(), core::num::ParseIntError> {
1003 fn from_digit(byte: u8) -> Option<char> {
1004 core::char::from_digit(byte as u32, 16)
1005 }
1006 let r = input.as_ref();
1007 if r.len() * 2 != output.len() {
1008 return Err(make_parse_int_err());
1009 }
1010 for i in 0..r.len() {
1011 let byte = r[i];
1012 output[i * 2] = from_digit((byte & 0xf0) >> 4).ok_or_else(make_parse_int_err)? as u8;
1013 output[i * 2 + 1] = from_digit(byte & 0x0f).ok_or_else(make_parse_int_err)? as u8;
1014 }
1015
1016 Ok(())
1017}
1018
1019pub(super) enum PanicReason {
1020 Add,
1021 Sub,
1022 Mul,
1023 DivByZero,
1024}
1025
1026c0nst::c0nst! {
1027 pub(super) c0nst fn maybe_panic(r: PanicReason) {
1028 match r {
1029 PanicReason::Add => panic!("attempt to add with overflow"),
1030 PanicReason::Sub => panic!("attempt to subtract with overflow"),
1031 PanicReason::Mul => panic!("attempt to multiply with overflow"),
1032 PanicReason::DivByZero => panic!("attempt to divide by zero"),
1033 }
1034 }
1035}
1036
1037#[cfg(test)]
1040mod tests {
1041 use super::FixedUInt as Bn;
1042 use super::*;
1043 use num_traits::One;
1044
1045 type Bn8 = Bn<u8, 8>;
1046 type Bn16 = Bn<u16, 4>;
1047 type Bn32 = Bn<u32, 2>;
1048
1049 c0nst::c0nst! {
1050 pub c0nst fn test_add<T: [c0nst] ConstMachineWord, const N: usize>(
1051 a: &mut [T; N],
1052 b: &[T; N]
1053 ) -> bool {
1054 add_impl(a, b)
1055 }
1056
1057 pub c0nst fn test_sub<T: [c0nst] ConstMachineWord, const N: usize>(
1058 a: &mut [T; N],
1059 b: &[T; N]
1060 ) -> bool {
1061 sub_impl(a, b)
1062 }
1063
1064 pub c0nst fn test_mul<T: [c0nst] ConstMachineWord, const N: usize>(
1065 a: &[T; N],
1066 b: &[T; N],
1067 word_bits: usize,
1068 ) -> ([T; N], bool) {
1069 const_mul::<T, N, true>(a, b, word_bits)
1070 }
1071
1072 pub c0nst fn arr_leading_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1073 a: &[T; N],
1074 ) -> u32 {
1075 const_leading_zeros::<T, N>(a)
1076 }
1077
1078 pub c0nst fn arr_trailing_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1079 a: &[T; N],
1080 ) -> u32 {
1081 const_trailing_zeros::<T, N>(a)
1082 }
1083
1084 pub c0nst fn arr_bit_length<T: [c0nst] ConstMachineWord, const N: usize>(
1085 a: &[T; N],
1086 ) -> usize {
1087 const_bit_length::<T, N>(a)
1088 }
1089
1090 pub c0nst fn arr_is_zero<T: [c0nst] ConstMachineWord, const N: usize>(
1091 a: &[T; N],
1092 ) -> bool {
1093 const_is_zero::<T, N>(a)
1094 }
1095
1096 pub c0nst fn arr_set_bit<T: [c0nst] ConstMachineWord, const N: usize>(
1097 a: &mut [T; N],
1098 pos: usize,
1099 ) {
1100 const_set_bit::<T, N>(a, pos)
1101 }
1102
1103 pub c0nst fn arr_cmp<T: [c0nst] ConstMachineWord, const N: usize>(
1104 a: &[T; N],
1105 b: &[T; N],
1106 ) -> core::cmp::Ordering {
1107 const_cmp::<T, N>(a, b)
1108 }
1109
1110 pub c0nst fn arr_cmp_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
1111 a: &[T; N],
1112 b: &[T; N],
1113 shift_bits: usize,
1114 ) -> core::cmp::Ordering {
1115 const_cmp_shifted::<T, N>(a, b, shift_bits)
1116 }
1117
1118 pub c0nst fn arr_get_shifted_word<T: [c0nst] ConstMachineWord, const N: usize>(
1119 a: &[T; N],
1120 word_idx: usize,
1121 word_shift: usize,
1122 bit_shift: usize,
1123 ) -> T {
1124 const_get_shifted_word::<T, N>(a, word_idx, word_shift, bit_shift)
1125 }
1126 }
1127
1128 #[test]
1129 fn test_const_add_impl() {
1130 let mut a: [u8; 4] = [1, 0, 0, 0];
1132 let b: [u8; 4] = [2, 0, 0, 0];
1133 let overflow = test_add(&mut a, &b);
1134 assert_eq!(a, [3, 0, 0, 0]);
1135 assert!(!overflow);
1136
1137 let mut a: [u8; 4] = [255, 0, 0, 0];
1139 let b: [u8; 4] = [1, 0, 0, 0];
1140 let overflow = test_add(&mut a, &b);
1141 assert_eq!(a, [0, 1, 0, 0]);
1142 assert!(!overflow);
1143
1144 let mut a: [u8; 4] = [255, 255, 255, 255];
1146 let b: [u8; 4] = [1, 0, 0, 0];
1147 let overflow = test_add(&mut a, &b);
1148 assert_eq!(a, [0, 0, 0, 0]);
1149 assert!(overflow);
1150
1151 let mut a: [u32; 2] = [0xFFFFFFFF, 0];
1153 let b: [u32; 2] = [1, 0];
1154 let overflow = test_add(&mut a, &b);
1155 assert_eq!(a, [0, 1]);
1156 assert!(!overflow);
1157
1158 #[cfg(feature = "nightly")]
1159 {
1160 const ADD_RESULT: ([u8; 4], bool) = {
1161 let mut a = [1u8, 0, 0, 0];
1162 let b = [2u8, 0, 0, 0];
1163 let overflow = test_add(&mut a, &b);
1164 (a, overflow)
1165 };
1166 assert_eq!(ADD_RESULT, ([3, 0, 0, 0], false));
1167 }
1168 }
1169
1170 #[test]
1171 fn test_const_sub_impl() {
1172 let mut a: [u8; 4] = [3, 0, 0, 0];
1174 let b: [u8; 4] = [1, 0, 0, 0];
1175 let overflow = test_sub(&mut a, &b);
1176 assert_eq!(a, [2, 0, 0, 0]);
1177 assert!(!overflow);
1178
1179 let mut a: [u8; 4] = [0, 1, 0, 0];
1181 let b: [u8; 4] = [1, 0, 0, 0];
1182 let overflow = test_sub(&mut a, &b);
1183 assert_eq!(a, [255, 0, 0, 0]);
1184 assert!(!overflow);
1185
1186 let mut a: [u8; 4] = [0, 0, 0, 0];
1188 let b: [u8; 4] = [1, 0, 0, 0];
1189 let overflow = test_sub(&mut a, &b);
1190 assert_eq!(a, [255, 255, 255, 255]);
1191 assert!(overflow);
1192
1193 let mut a: [u32; 2] = [0, 1];
1195 let b: [u32; 2] = [1, 0];
1196 let overflow = test_sub(&mut a, &b);
1197 assert_eq!(a, [0xFFFFFFFF, 0]);
1198 assert!(!overflow);
1199
1200 #[cfg(feature = "nightly")]
1201 {
1202 const SUB_RESULT: ([u8; 4], bool) = {
1203 let mut a = [3u8, 0, 0, 0];
1204 let b = [1u8, 0, 0, 0];
1205 let overflow = test_sub(&mut a, &b);
1206 (a, overflow)
1207 };
1208 assert_eq!(SUB_RESULT, ([2, 0, 0, 0], false));
1209 }
1210 }
1211
1212 #[test]
1213 fn test_const_mul_impl() {
1214 let a: [u8; 2] = [3, 0];
1216 let b: [u8; 2] = [4, 0];
1217 let (result, overflow) = test_mul(&a, &b, 8);
1218 assert_eq!(result, [12, 0]);
1219 assert!(!overflow);
1220
1221 let a: [u8; 2] = [200, 0];
1223 let b: [u8; 2] = [2, 0];
1224 let (result, overflow) = test_mul(&a, &b, 8);
1225 assert_eq!(result, [0x90, 0x01]);
1226 assert!(!overflow);
1227
1228 let a: [u8; 2] = [0, 1]; let b: [u8; 2] = [0, 1]; let (_result, overflow) = test_mul(&a, &b, 8);
1232 assert!(overflow);
1233
1234 let a: [u8; 3] = [0, 0, 1];
1238 let b: [u8; 3] = [0, 0, 1];
1239 let (_result, overflow) = test_mul(&a, &b, 8);
1240 assert!(overflow, "N=3 high-position overflow not detected");
1241
1242 let a: [u8; 3] = [0, 0, 2];
1246 let b: [u8; 3] = [0, 0, 2];
1247 let (_result, overflow) = test_mul(&a, &b, 8);
1248 assert!(
1249 overflow,
1250 "N=3 high-position overflow with larger values not detected"
1251 );
1252
1253 let a: [u8; 3] = [0, 1, 0];
1257 let b: [u8; 3] = [0, 1, 0];
1258 let (result, overflow) = test_mul(&a, &b, 8);
1259 assert_eq!(result, [0, 0, 1]);
1260 assert!(
1261 !overflow,
1262 "N=3 non-overflow incorrectly detected as overflow"
1263 );
1264
1265 let a: [u8; 3] = [255, 0, 0];
1269 let b: [u8; 3] = [255, 0, 0];
1270 let (result, overflow) = test_mul(&a, &b, 8);
1271 assert_eq!(result, [0x01, 0xFE, 0x00]);
1272 assert!(!overflow);
1273
1274 #[cfg(feature = "nightly")]
1275 {
1276 const MUL_RESULT: ([u8; 2], bool) = test_mul(&[3u8, 0], &[4u8, 0], 8);
1277 assert_eq!(MUL_RESULT, ([12, 0], false));
1278 }
1279 }
1280
1281 #[test]
1282 fn test_const_helpers() {
1283 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]));
1308 assert!(!arr_is_zero(&[1u8, 0, 0, 0]));
1309 assert!(!arr_is_zero(&[0u8, 0, 0, 1]));
1310 assert!(!arr_is_zero(&[0u8, 1, 0, 0]));
1311
1312 let mut arr: [u8; 4] = [0, 0, 0, 0];
1314 arr_set_bit(&mut arr, 0);
1315 assert_eq!(arr, [1, 0, 0, 0]);
1316
1317 let mut arr: [u8; 4] = [0, 0, 0, 0];
1318 arr_set_bit(&mut arr, 8);
1319 assert_eq!(arr, [0, 1, 0, 0]);
1320
1321 let mut arr: [u8; 4] = [0, 0, 0, 0];
1322 arr_set_bit(&mut arr, 31);
1323 assert_eq!(arr, [0, 0, 0, 0x80]);
1324
1325 let mut arr: [u8; 4] = [0, 0, 0, 0];
1327 arr_set_bit(&mut arr, 0);
1328 arr_set_bit(&mut arr, 3);
1329 arr_set_bit(&mut arr, 8);
1330 assert_eq!(arr, [0b00001001, 1, 0, 0]);
1331
1332 let mut arr: [u8; 4] = [0, 0, 0, 0];
1334 arr_set_bit(&mut arr, 32);
1335 assert_eq!(arr, [0, 0, 0, 0]);
1336
1337 assert_eq!(arr_leading_zeros(&[0u32, 0]), 64);
1339 assert_eq!(arr_leading_zeros(&[1u32, 0]), 63);
1340 assert_eq!(arr_leading_zeros(&[0u32, 1]), 31);
1341 assert_eq!(arr_trailing_zeros(&[0u32, 0]), 64);
1342 assert_eq!(arr_trailing_zeros(&[0u32, 1]), 32);
1343 assert_eq!(arr_bit_length(&[0u32, 0]), 0);
1344 assert_eq!(arr_bit_length(&[1u32, 0]), 1);
1345 assert_eq!(arr_bit_length(&[0u32, 1]), 33);
1346
1347 #[cfg(feature = "nightly")]
1348 {
1349 const LEADING: u32 = arr_leading_zeros(&[0u8, 0, 1, 0]);
1350 assert_eq!(LEADING, 15);
1351
1352 const TRAILING: u32 = arr_trailing_zeros(&[0u8, 0, 1, 0]);
1353 assert_eq!(TRAILING, 16);
1354
1355 const BIT_LEN: usize = arr_bit_length(&[0u8, 0, 1, 0]);
1356 assert_eq!(BIT_LEN, 17);
1357
1358 const IS_ZERO: bool = arr_is_zero(&[0u8, 0, 0, 0]);
1359 assert!(IS_ZERO);
1360
1361 const NOT_ZERO: bool = arr_is_zero(&[0u8, 1, 0, 0]);
1362 assert!(!NOT_ZERO);
1363
1364 const SET_BIT_RESULT: [u8; 4] = {
1365 let mut arr = [0u8, 0, 0, 0];
1366 arr_set_bit(&mut arr, 10);
1367 arr
1368 };
1369 assert_eq!(SET_BIT_RESULT, [0, 0b00000100, 0, 0]);
1370 }
1371 }
1372
1373 #[test]
1374 fn test_const_cmp() {
1375 use core::cmp::Ordering;
1376
1377 assert_eq!(arr_cmp(&[1u8, 2, 3, 4], &[1u8, 2, 3, 4]), Ordering::Equal);
1379 assert_eq!(arr_cmp(&[0u8, 0, 0, 0], &[0u8, 0, 0, 0]), Ordering::Equal);
1380
1381 assert_eq!(arr_cmp(&[0u8, 0, 0, 2], &[0u8, 0, 0, 1]), Ordering::Greater);
1383
1384 assert_eq!(arr_cmp(&[0u8, 0, 0, 1], &[0u8, 0, 0, 2]), Ordering::Less);
1386
1387 assert_eq!(arr_cmp(&[2u8, 0, 0, 0], &[1u8, 0, 0, 0]), Ordering::Greater);
1389
1390 assert_eq!(arr_cmp(&[1u8, 0, 0, 0], &[2u8, 0, 0, 0]), Ordering::Less);
1392
1393 assert_eq!(arr_cmp(&[0u32, 1], &[0u32, 1]), Ordering::Equal);
1395 assert_eq!(arr_cmp(&[0u32, 2], &[0u32, 1]), Ordering::Greater);
1396 assert_eq!(arr_cmp(&[0u32, 1], &[0u32, 2]), Ordering::Less);
1397
1398 #[cfg(feature = "nightly")]
1399 {
1400 const CMP_EQ: Ordering = arr_cmp(&[1u8, 2, 3, 4], &[1u8, 2, 3, 4]);
1401 const CMP_GT: Ordering = arr_cmp(&[0u8, 0, 0, 2], &[0u8, 0, 0, 1]);
1402 const CMP_LT: Ordering = arr_cmp(&[0u8, 0, 0, 1], &[0u8, 0, 0, 2]);
1403 assert_eq!(CMP_EQ, Ordering::Equal);
1404 assert_eq!(CMP_GT, Ordering::Greater);
1405 assert_eq!(CMP_LT, Ordering::Less);
1406 }
1407 }
1408
1409 #[test]
1410 fn test_const_cmp_shifted() {
1411 use core::cmp::Ordering;
1412
1413 assert_eq!(
1415 arr_cmp_shifted(&[1u8, 0, 0, 0], &[1u8, 0, 0, 0], 0),
1416 Ordering::Equal
1417 );
1418
1419 assert_eq!(
1421 arr_cmp_shifted(&[0u8, 1, 0, 0], &[1u8, 0, 0, 0], 8),
1422 Ordering::Equal
1423 );
1424
1425 assert_eq!(
1427 arr_cmp_shifted(&[0u8, 2, 0, 0], &[1u8, 0, 0, 0], 8),
1428 Ordering::Greater
1429 );
1430
1431 assert_eq!(
1433 arr_cmp_shifted(&[0u8, 0, 0, 0], &[1u8, 0, 0, 0], 8),
1434 Ordering::Less
1435 );
1436
1437 assert_eq!(
1440 arr_cmp_shifted(&[1u8, 0, 0, 0], &[1u8, 0, 0, 0], 32),
1441 Ordering::Greater
1442 );
1443
1444 assert_eq!(
1446 arr_cmp_shifted(&[0u8, 0, 0, 0], &[255u8, 255, 255, 255], 32),
1447 Ordering::Equal
1448 );
1449
1450 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 0, 1, 0), 0);
1454 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 1, 1, 0), 1);
1455 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 2, 1, 0), 2);
1456
1457 assert_eq!(arr_get_shifted_word(&[0x0Fu8, 0xF0, 0, 0], 0, 0, 4), 0xF0);
1461 assert_eq!(arr_get_shifted_word(&[0x0Fu8, 0xF0, 0, 0], 1, 0, 4), 0x00);
1463
1464 assert_eq!(arr_get_shifted_word(&[0xFFu8, 0x00, 0, 0], 0, 0, 4), 0xF0);
1467 assert_eq!(arr_get_shifted_word(&[0xFFu8, 0x00, 0, 0], 1, 0, 4), 0x0F);
1469
1470 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 0, 1, 4), 0);
1474 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 1, 1, 4), 0xB0);
1476 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 2, 1, 4), 0xDA);
1478
1479 #[cfg(feature = "nightly")]
1480 {
1481 const CMP_SHIFTED_EQ: Ordering = arr_cmp_shifted(&[0u8, 1, 0, 0], &[1u8, 0, 0, 0], 8);
1482 const CMP_SHIFTED_GT: Ordering = arr_cmp_shifted(&[0u8, 2, 0, 0], &[1u8, 0, 0, 0], 8);
1483 assert_eq!(CMP_SHIFTED_EQ, Ordering::Equal);
1484 assert_eq!(CMP_SHIFTED_GT, Ordering::Greater);
1485 }
1486 }
1487
1488 #[test]
1489 fn test_core_convert_u8() {
1490 let f = Bn::<u8, 1>::from(1u8);
1491 assert_eq!(f.array, [1]);
1492 let f = Bn::<u8, 2>::from(1u8);
1493 assert_eq!(f.array, [1, 0]);
1494
1495 let f = Bn::<u16, 1>::from(1u8);
1496 assert_eq!(f.array, [1]);
1497 let f = Bn::<u16, 2>::from(1u8);
1498 assert_eq!(f.array, [1, 0]);
1499
1500 #[cfg(feature = "nightly")]
1501 {
1502 const F1: Bn<u8, 2> = Bn::<u8, 2>::from(42u8);
1503 assert_eq!(F1.array, [42, 0]);
1504 }
1505 }
1506
1507 #[test]
1508 fn test_core_convert_u16() {
1509 let f = Bn::<u8, 1>::from(1u16);
1510 assert_eq!(f.array, [1]);
1511 let f = Bn::<u8, 2>::from(1u16);
1512 assert_eq!(f.array, [1, 0]);
1513
1514 let f = Bn::<u8, 1>::from(256u16);
1515 assert_eq!(f.array, [0]);
1516 let f = Bn::<u8, 2>::from(257u16);
1517 assert_eq!(f.array, [1, 1]);
1518 let f = Bn::<u8, 2>::from(65535u16);
1519 assert_eq!(f.array, [255, 255]);
1520
1521 let f = Bn::<u16, 1>::from(1u16);
1522 assert_eq!(f.array, [1]);
1523 let f = Bn::<u16, 2>::from(1u16);
1524 assert_eq!(f.array, [1, 0]);
1525
1526 let f = Bn::<u16, 1>::from(65535u16);
1527 assert_eq!(f.array, [65535]);
1528
1529 #[cfg(feature = "nightly")]
1530 {
1531 const F1: Bn<u8, 2> = Bn::<u8, 2>::from(0x0102u16);
1532 assert_eq!(F1.array, [0x02, 0x01]);
1533 }
1534 }
1535
1536 #[test]
1537 fn test_core_convert_u32() {
1538 let f = Bn::<u8, 1>::from(1u32);
1539 assert_eq!(f.array, [1]);
1540 let f = Bn::<u8, 1>::from(256u32);
1541 assert_eq!(f.array, [0]);
1542
1543 let f = Bn::<u8, 2>::from(1u32);
1544 assert_eq!(f.array, [1, 0]);
1545 let f = Bn::<u8, 2>::from(257u32);
1546 assert_eq!(f.array, [1, 1]);
1547 let f = Bn::<u8, 2>::from(65535u32);
1548 assert_eq!(f.array, [255, 255]);
1549
1550 let f = Bn::<u8, 4>::from(1u32);
1551 assert_eq!(f.array, [1, 0, 0, 0]);
1552 let f = Bn::<u8, 4>::from(257u32);
1553 assert_eq!(f.array, [1, 1, 0, 0]);
1554 let f = Bn::<u8, 4>::from(u32::max_value());
1555 assert_eq!(f.array, [255, 255, 255, 255]);
1556
1557 let f = Bn::<u8, 1>::from(1u32);
1558 assert_eq!(f.array, [1]);
1559 let f = Bn::<u8, 1>::from(256u32);
1560 assert_eq!(f.array, [0]);
1561
1562 let f = Bn::<u16, 2>::from(65537u32);
1563 assert_eq!(f.array, [1, 1]);
1564
1565 let f = Bn::<u32, 1>::from(1u32);
1566 assert_eq!(f.array, [1]);
1567 let f = Bn::<u32, 2>::from(1u32);
1568 assert_eq!(f.array, [1, 0]);
1569
1570 let f = Bn::<u32, 1>::from(65537u32);
1571 assert_eq!(f.array, [65537]);
1572
1573 let f = Bn::<u32, 1>::from(u32::max_value());
1574 assert_eq!(f.array, [4294967295]);
1575
1576 #[cfg(feature = "nightly")]
1577 {
1578 const F1: Bn<u8, 4> = Bn::<u8, 4>::from(0x01020304u32);
1579 assert_eq!(F1.array, [0x04, 0x03, 0x02, 0x01]);
1580 }
1581 }
1582
1583 #[test]
1584 fn test_core_convert_u64() {
1585 let f = Bn::<u8, 8>::from(0x0102030405060708u64);
1586 assert_eq!(f.array, [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
1587
1588 let f = Bn::<u16, 4>::from(0x0102030405060708u64);
1589 assert_eq!(f.array, [0x0708, 0x0506, 0x0304, 0x0102]);
1590
1591 let f = Bn::<u32, 2>::from(0x0102030405060708u64);
1592 assert_eq!(f.array, [0x05060708, 0x01020304]);
1593
1594 let f = Bn::<u64, 1>::from(0x0102030405060708u64);
1595 assert_eq!(f.array, [0x0102030405060708]);
1596
1597 #[cfg(feature = "nightly")]
1598 {
1599 const F1: Bn<u8, 8> = Bn::<u8, 8>::from(0x0102030405060708u64);
1600 assert_eq!(F1.array, [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
1601 }
1602 }
1603
1604 #[test]
1605 fn testsimple() {
1606 assert_eq!(Bn::<u8, 8>::new(), Bn::<u8, 8>::new());
1607
1608 assert_eq!(Bn::<u8, 8>::from_u8(3).unwrap().to_u32(), Some(3));
1609 assert_eq!(Bn::<u16, 4>::from_u8(3).unwrap().to_u32(), Some(3));
1610 assert_eq!(Bn::<u32, 2>::from_u8(3).unwrap().to_u32(), Some(3));
1611 assert_eq!(Bn::<u32, 2>::from_u64(3).unwrap().to_u32(), Some(3));
1612 assert_eq!(Bn::<u8, 8>::from_u64(255).unwrap().to_u32(), Some(255));
1613 assert_eq!(Bn::<u8, 8>::from_u64(256).unwrap().to_u32(), Some(256));
1614 assert_eq!(Bn::<u8, 8>::from_u64(65536).unwrap().to_u32(), Some(65536));
1615 }
1616 #[test]
1617 fn testfrom() {
1618 let mut n1 = Bn::<u8, 8>::new();
1619 n1.array[0] = 1;
1620 assert_eq!(Some(1), n1.to_u32());
1621 n1.array[1] = 1;
1622 assert_eq!(Some(257), n1.to_u32());
1623
1624 let mut n2 = Bn::<u16, 8>::new();
1625 n2.array[0] = 0xffff;
1626 assert_eq!(Some(65535), n2.to_u32());
1627 n2.array[0] = 0x0;
1628 n2.array[2] = 0x1;
1629 assert_eq!(None, n2.to_u32());
1631 assert_eq!(Some(0x100000000), n2.to_u64());
1632 }
1633
1634 #[test]
1635 fn test_from_str_bitlengths() {
1636 let test_s64 = "81906f5e4d3c2c01";
1637 let test_u64: u64 = 0x81906f5e4d3c2c01;
1638 let bb = Bn8::from_str_radix(test_s64, 16).unwrap();
1639 let cc = Bn8::from_u64(test_u64).unwrap();
1640 assert_eq!(cc.array, [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81]);
1641 assert_eq!(bb.array, [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81]);
1642 let dd = Bn16::from_u64(test_u64).unwrap();
1643 let ff = Bn16::from_str_radix(test_s64, 16).unwrap();
1644 assert_eq!(dd.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190]);
1645 assert_eq!(ff.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190]);
1646 let ee = Bn32::from_u64(test_u64).unwrap();
1647 let gg = Bn32::from_str_radix(test_s64, 16).unwrap();
1648 assert_eq!(ee.array, [0x4d3c2c01, 0x81906f5e]);
1649 assert_eq!(gg.array, [0x4d3c2c01, 0x81906f5e]);
1650 }
1651
1652 #[test]
1653 fn test_from_str_stringlengths() {
1654 let ab = Bn::<u8, 9>::from_str_radix("2281906f5e4d3c2c01", 16).unwrap();
1655 assert_eq!(
1656 ab.array,
1657 [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81, 0x22]
1658 );
1659 assert_eq!(
1660 [0x2c01, 0x4d3c, 0x6f5e, 0],
1661 Bn::<u16, 4>::from_str_radix("6f5e4d3c2c01", 16)
1662 .unwrap()
1663 .array
1664 );
1665 assert_eq!(
1666 [0x2c01, 0x4d3c, 0x6f5e, 0x190],
1667 Bn::<u16, 4>::from_str_radix("1906f5e4d3c2c01", 16)
1668 .unwrap()
1669 .array
1670 );
1671 assert_eq!(
1672 Err(make_overflow_err()),
1673 Bn::<u16, 4>::from_str_radix("f81906f5e4d3c2c01", 16)
1674 );
1675 assert_eq!(
1676 Err(make_overflow_err()),
1677 Bn::<u16, 4>::from_str_radix("af81906f5e4d3c2c01", 16)
1678 );
1679 assert_eq!(
1680 Err(make_overflow_err()),
1681 Bn::<u16, 4>::from_str_radix("baaf81906f5e4d3c2c01", 16)
1682 );
1683 let ac = Bn::<u16, 5>::from_str_radix("baaf81906f5e4d3c2c01", 16).unwrap();
1684 assert_eq!(ac.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190, 0xbaaf]);
1685 }
1686
1687 #[test]
1688 fn test_resize() {
1689 type TestInt1 = FixedUInt<u32, 1>;
1690 type TestInt2 = FixedUInt<u32, 2>;
1691
1692 let a = TestInt1::from(u32::MAX);
1693 let b: TestInt2 = a.resize();
1694 assert_eq!(b, TestInt2::from([u32::MAX, 0]));
1695
1696 let a = TestInt2::from([u32::MAX, u32::MAX]);
1697 let b: TestInt1 = a.resize();
1698 assert_eq!(b, TestInt1::from(u32::MAX));
1699 }
1700
1701 #[test]
1702 fn test_bit_length() {
1703 assert_eq!(0, Bn8::from_u8(0).unwrap().bit_length());
1704 assert_eq!(1, Bn8::from_u8(1).unwrap().bit_length());
1705 assert_eq!(2, Bn8::from_u8(2).unwrap().bit_length());
1706 assert_eq!(2, Bn8::from_u8(3).unwrap().bit_length());
1707 assert_eq!(7, Bn8::from_u8(0x70).unwrap().bit_length());
1708 assert_eq!(8, Bn8::from_u8(0xF0).unwrap().bit_length());
1709 assert_eq!(9, Bn8::from_u16(0x1F0).unwrap().bit_length());
1710
1711 assert_eq!(20, Bn8::from_u64(990223).unwrap().bit_length());
1712 assert_eq!(32, Bn8::from_u64(0xefffffff).unwrap().bit_length());
1713 assert_eq!(32, Bn8::from_u64(0x8fffffff).unwrap().bit_length());
1714 assert_eq!(31, Bn8::from_u64(0x7fffffff).unwrap().bit_length());
1715 assert_eq!(34, Bn8::from_u64(0x3ffffffff).unwrap().bit_length());
1716
1717 assert_eq!(0, Bn32::from_u8(0).unwrap().bit_length());
1718 assert_eq!(1, Bn32::from_u8(1).unwrap().bit_length());
1719 assert_eq!(2, Bn32::from_u8(2).unwrap().bit_length());
1720 assert_eq!(2, Bn32::from_u8(3).unwrap().bit_length());
1721 assert_eq!(7, Bn32::from_u8(0x70).unwrap().bit_length());
1722 assert_eq!(8, Bn32::from_u8(0xF0).unwrap().bit_length());
1723 assert_eq!(9, Bn32::from_u16(0x1F0).unwrap().bit_length());
1724
1725 assert_eq!(20, Bn32::from_u64(990223).unwrap().bit_length());
1726 assert_eq!(32, Bn32::from_u64(0xefffffff).unwrap().bit_length());
1727 assert_eq!(32, Bn32::from_u64(0x8fffffff).unwrap().bit_length());
1728 assert_eq!(31, Bn32::from_u64(0x7fffffff).unwrap().bit_length());
1729 assert_eq!(34, Bn32::from_u64(0x3ffffffff).unwrap().bit_length());
1730 }
1731
1732 #[test]
1733 fn test_bit_length_1000() {
1734 let value = Bn32::from_u16(1000).unwrap();
1736
1737 assert_eq!(value.to_u32().unwrap(), 1000);
1740 assert_eq!(value.bit_length(), 10);
1741
1742 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);
1749 assert_eq!(Bn16::from_u16(1000).unwrap().bit_length(), 10);
1750
1751 let value_from_str = Bn32::from_str_radix("1000", 10).unwrap();
1753 assert_eq!(value_from_str.bit_length(), 10);
1754
1755 let value_from_bytes = Bn32::from_le_bytes(&1000u16.to_le_bytes());
1757 assert_eq!(
1759 value_from_bytes.to_u32().unwrap_or(0),
1760 1000,
1761 "from_le_bytes didn't create the correct value"
1762 );
1763 assert_eq!(value_from_bytes.bit_length(), 10);
1764 }
1765 #[test]
1766 fn test_cmp() {
1767 let f0 = <Bn8 as Zero>::zero();
1768 let f1 = <Bn8 as Zero>::zero();
1769 let f2 = <Bn8 as One>::one();
1770 assert_eq!(f0, f1);
1771 assert!(f2 > f0);
1772 assert!(f0 < f2);
1773 let f3 = Bn32::from_u64(990223).unwrap();
1774 assert_eq!(f3, Bn32::from_u64(990223).unwrap());
1775 let f4 = Bn32::from_u64(990224).unwrap();
1776 assert!(f4 > Bn32::from_u64(990223).unwrap());
1777
1778 let f3 = Bn8::from_u64(990223).unwrap();
1779 assert_eq!(f3, Bn8::from_u64(990223).unwrap());
1780 let f4 = Bn8::from_u64(990224).unwrap();
1781 assert!(f4 > Bn8::from_u64(990223).unwrap());
1782
1783 #[cfg(feature = "nightly")]
1784 {
1785 use core::cmp::Ordering;
1786
1787 const A: FixedUInt<u8, 2> = FixedUInt { array: [10, 0] };
1788 const B: FixedUInt<u8, 2> = FixedUInt { array: [20, 0] };
1789 const C: FixedUInt<u8, 2> = FixedUInt { array: [10, 0] };
1790
1791 const CMP_LT: Ordering = A.cmp(&B);
1792 const CMP_GT: Ordering = B.cmp(&A);
1793 const CMP_EQ: Ordering = A.cmp(&C);
1794 const EQ_TRUE: bool = A.eq(&C);
1795 const EQ_FALSE: bool = A.eq(&B);
1796
1797 assert_eq!(CMP_LT, Ordering::Less);
1798 assert_eq!(CMP_GT, Ordering::Greater);
1799 assert_eq!(CMP_EQ, Ordering::Equal);
1800 assert!(EQ_TRUE);
1801 assert!(!EQ_FALSE);
1802 }
1803 }
1804
1805 #[test]
1806 fn test_default() {
1807 let d: Bn8 = Default::default();
1808 assert!(Zero::is_zero(&d));
1809
1810 #[cfg(feature = "nightly")]
1811 {
1812 const D: FixedUInt<u8, 2> = <FixedUInt<u8, 2> as Default>::default();
1813 assert!(Zero::is_zero(&D));
1814 }
1815 }
1816
1817 #[test]
1818 fn test_clone() {
1819 let a: Bn8 = 42u8.into();
1820 let b = a.clone();
1821 assert_eq!(a, b);
1822
1823 #[cfg(feature = "nightly")]
1824 {
1825 const A: FixedUInt<u8, 2> = FixedUInt { array: [42, 0] };
1826 const B: FixedUInt<u8, 2> = A.clone();
1827 assert_eq!(A.array, B.array);
1828 }
1829 }
1830
1831 #[test]
1832 fn test_le_be_bytes() {
1833 let le_bytes = [1, 2, 3, 4];
1834 let be_bytes = [4, 3, 2, 1];
1835 let u8_ver = FixedUInt::<u8, 4>::from_le_bytes(&le_bytes);
1836 let u16_ver = FixedUInt::<u16, 2>::from_le_bytes(&le_bytes);
1837 let u32_ver = FixedUInt::<u32, 1>::from_le_bytes(&le_bytes);
1838 let u8_ver_be = FixedUInt::<u8, 4>::from_be_bytes(&be_bytes);
1839 let u16_ver_be = FixedUInt::<u16, 2>::from_be_bytes(&be_bytes);
1840 let u32_ver_be = FixedUInt::<u32, 1>::from_be_bytes(&be_bytes);
1841
1842 assert_eq!(u8_ver.array, [1, 2, 3, 4]);
1843 assert_eq!(u16_ver.array, [0x0201, 0x0403]);
1844 assert_eq!(u32_ver.array, [0x04030201]);
1845 assert_eq!(u8_ver_be.array, [1, 2, 3, 4]);
1846 assert_eq!(u16_ver_be.array, [0x0201, 0x0403]);
1847 assert_eq!(u32_ver_be.array, [0x04030201]);
1848
1849 let mut output_buffer = [0u8; 16];
1850 assert_eq!(u8_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
1851 assert_eq!(u8_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
1852 assert_eq!(u16_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
1853 assert_eq!(u16_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
1854 assert_eq!(u32_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
1855 assert_eq!(u32_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
1856 }
1857
1858 #[test]
1860 fn test_div_small() {
1861 type TestInt = FixedUInt<u8, 2>;
1862
1863 let test_cases = [
1865 (20u16, 3u16, 6u16), (100u16, 7u16, 14u16), (255u16, 5u16, 51u16), (65535u16, 256u16, 255u16), ];
1870
1871 for (dividend_val, divisor_val, expected) in test_cases {
1872 let dividend = TestInt::from(dividend_val);
1873 let divisor = TestInt::from(divisor_val);
1874 let expected_result = TestInt::from(expected);
1875
1876 assert_eq!(
1877 dividend / divisor,
1878 expected_result,
1879 "Division failed for {} / {} = {}",
1880 dividend_val,
1881 divisor_val,
1882 expected
1883 );
1884 }
1885 }
1886
1887 #[test]
1888 fn test_div_edge_cases() {
1889 type TestInt = FixedUInt<u16, 2>;
1890
1891 let dividend = TestInt::from(1000u16);
1893 let divisor = TestInt::from(1u16);
1894 assert_eq!(dividend / divisor, TestInt::from(1000u16));
1895
1896 let dividend = TestInt::from(42u16);
1898 let divisor = TestInt::from(42u16);
1899 assert_eq!(dividend / divisor, TestInt::from(1u16));
1900
1901 let dividend = TestInt::from(5u16);
1903 let divisor = TestInt::from(10u16);
1904 assert_eq!(dividend / divisor, TestInt::from(0u16));
1905
1906 let dividend = TestInt::from(1024u16);
1908 let divisor = TestInt::from(4u16);
1909 assert_eq!(dividend / divisor, TestInt::from(256u16));
1910 }
1911
1912 #[test]
1913 fn test_helper_methods() {
1914 type TestInt = FixedUInt<u8, 2>;
1915
1916 let mut val = <TestInt as Zero>::zero();
1918 const_set_bit(&mut val.array, 0);
1919 assert_eq!(val, TestInt::from(1u8));
1920
1921 const_set_bit(&mut val.array, 8);
1922 assert_eq!(val, TestInt::from(257u16)); let a = TestInt::from(8u8); let b = TestInt::from(1u8); assert_eq!(
1930 const_cmp_shifted(&a.array, &b.array, 3),
1931 core::cmp::Ordering::Equal
1932 );
1933
1934 assert_eq!(
1936 const_cmp_shifted(&a.array, &b.array, 2),
1937 core::cmp::Ordering::Greater
1938 );
1939
1940 assert_eq!(
1942 const_cmp_shifted(&a.array, &b.array, 4),
1943 core::cmp::Ordering::Less
1944 );
1945
1946 let mut val = TestInt::from(10u8);
1948 let one = TestInt::from(1u8);
1949 const_sub_shifted(&mut val.array, &one.array, 2); assert_eq!(val, TestInt::from(6u8)); }
1952
1953 #[test]
1954 fn test_shifted_operations_comprehensive() {
1955 type TestInt = FixedUInt<u32, 2>;
1956
1957 let a = TestInt::from(0x12345678u32);
1959 let b = TestInt::from(0x12345678u32);
1960
1961 assert_eq!(
1963 const_cmp_shifted(&a.array, &b.array, 0),
1964 core::cmp::Ordering::Equal
1965 );
1966
1967 let c = TestInt::from(0x123u32); let d = TestInt::from(0x48d159e2u32); assert_eq!(
1973 const_cmp_shifted(&d.array, &c.array, 16),
1974 core::cmp::Ordering::Greater
1975 );
1976
1977 let e = TestInt::from(1u32);
1979 let zero = TestInt::from(0u32);
1980 assert_eq!(
1981 const_cmp_shifted(&e.array, &zero.array, 100),
1982 core::cmp::Ordering::Greater
1983 );
1984 assert_eq!(
1986 const_cmp_shifted(&zero.array, &e.array, 100),
1987 core::cmp::Ordering::Equal
1988 );
1989
1990 let mut val = TestInt::from(0x10000u32); let one = TestInt::from(1u32);
1993 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)); }
2001
2002 #[test]
2003 fn test_shifted_operations_edge_cases() {
2004 type TestInt = FixedUInt<u32, 2>;
2005
2006 let a = TestInt::from(42u32);
2008 let a2 = TestInt::from(42u32);
2009 assert_eq!(
2010 const_cmp_shifted(&a.array, &a2.array, 0),
2011 core::cmp::Ordering::Equal
2012 );
2013
2014 let mut b = TestInt::from(42u32);
2015 let ten = TestInt::from(10u32);
2016 const_sub_shifted(&mut b.array, &ten.array, 0);
2017 assert_eq!(b, TestInt::from(32u32));
2018
2019 let c = TestInt::from(123u32);
2021 let large = TestInt::from(456u32);
2022 assert_eq!(
2023 const_cmp_shifted(&c.array, &large.array, 200),
2024 core::cmp::Ordering::Greater
2025 );
2026
2027 let mut d = TestInt::from(123u32);
2028 const_sub_shifted(&mut d.array, &large.array, 200); assert_eq!(d, TestInt::from(123u32));
2030
2031 let zero = TestInt::from(0u32);
2033 let one = TestInt::from(1u32);
2034 assert_eq!(
2035 const_cmp_shifted(&zero.array, &zero.array, 10),
2036 core::cmp::Ordering::Equal
2037 );
2038 assert_eq!(
2039 const_cmp_shifted(&one.array, &zero.array, 10),
2040 core::cmp::Ordering::Greater
2041 );
2042 }
2043
2044 #[test]
2045 fn test_shifted_operations_equivalence() {
2046 type TestInt = FixedUInt<u32, 2>;
2047
2048 let test_cases = [
2050 (0x12345u32, 0x678u32, 4),
2051 (0x1000u32, 0x10u32, 8),
2052 (0xABCDu32, 0x1u32, 16),
2053 (0x80000000u32, 0x1u32, 1),
2054 ];
2055
2056 for (a_val, b_val, shift) in test_cases {
2057 let a = TestInt::from(a_val);
2058 let b = TestInt::from(b_val);
2059
2060 let optimized_cmp = const_cmp_shifted(&a.array, &b.array, shift);
2062 let naive_cmp = a.cmp(&(b << shift));
2063 assert_eq!(
2064 optimized_cmp, naive_cmp,
2065 "cmp_shifted mismatch: {} vs ({} << {})",
2066 a_val, b_val, shift
2067 );
2068
2069 if a >= (b << shift) {
2071 let mut optimized_result = a;
2072 const_sub_shifted(&mut optimized_result.array, &b.array, shift);
2073
2074 let naive_result = a - (b << shift);
2075 assert_eq!(
2076 optimized_result, naive_result,
2077 "sub_shifted mismatch: {} - ({} << {})",
2078 a_val, b_val, shift
2079 );
2080 }
2081 }
2082 }
2083
2084 #[test]
2085 fn test_div_assign_in_place_optimization() {
2086 type TestInt = FixedUInt<u32, 2>;
2087
2088 let test_cases = [
2090 (100u32, 10u32, 10u32, 0u32), (123u32, 7u32, 17u32, 4u32), (1000u32, 13u32, 76u32, 12u32), (65535u32, 255u32, 257u32, 0u32), ];
2095
2096 for (dividend_val, divisor_val, expected_quotient, expected_remainder) in test_cases {
2097 let mut dividend = TestInt::from(dividend_val);
2099 let divisor = TestInt::from(divisor_val);
2100
2101 dividend /= divisor;
2102 assert_eq!(
2103 dividend,
2104 TestInt::from(expected_quotient),
2105 "div_assign: {} / {} should be {}",
2106 dividend_val,
2107 divisor_val,
2108 expected_quotient
2109 );
2110
2111 let dividend2 = TestInt::from(dividend_val);
2113 let (quotient, remainder) = dividend2.div_rem(&divisor);
2114 assert_eq!(
2115 quotient,
2116 TestInt::from(expected_quotient),
2117 "div_rem quotient: {} / {} should be {}",
2118 dividend_val,
2119 divisor_val,
2120 expected_quotient
2121 );
2122 assert_eq!(
2123 remainder,
2124 TestInt::from(expected_remainder),
2125 "div_rem remainder: {} % {} should be {}",
2126 dividend_val,
2127 divisor_val,
2128 expected_remainder
2129 );
2130
2131 assert_eq!(
2133 quotient * divisor + remainder,
2134 TestInt::from(dividend_val),
2135 "Property check failed for {}",
2136 dividend_val
2137 );
2138 }
2139 }
2140
2141 #[test]
2142 fn test_div_assign_stack_efficiency() {
2143 type TestInt = FixedUInt<u32, 4>; let mut dividend = TestInt::from(0x123456789ABCDEFu64);
2147 let divisor = TestInt::from(0x12345u32);
2148 let original_dividend = dividend;
2149
2150 dividend /= divisor;
2152
2153 let remainder = original_dividend % divisor;
2155 assert_eq!(dividend * divisor + remainder, original_dividend);
2156 }
2157
2158 #[test]
2159 fn test_rem_assign_optimization() {
2160 type TestInt = FixedUInt<u32, 2>;
2161
2162 let test_cases = [
2163 (100u32, 10u32, 0u32), (123u32, 7u32, 4u32), (1000u32, 13u32, 12u32), (65535u32, 255u32, 0u32), ];
2168
2169 for (dividend_val, divisor_val, expected_remainder) in test_cases {
2170 let mut dividend = TestInt::from(dividend_val);
2171 let divisor = TestInt::from(divisor_val);
2172
2173 dividend %= divisor;
2174 assert_eq!(
2175 dividend,
2176 TestInt::from(expected_remainder),
2177 "rem_assign: {} % {} should be {}",
2178 dividend_val,
2179 divisor_val,
2180 expected_remainder
2181 );
2182 }
2183 }
2184
2185 #[test]
2186 fn test_div_with_remainder_property() {
2187 type TestInt = FixedUInt<u32, 2>;
2188
2189 let test_cases = [
2191 (100u32, 10u32, 10u32), (123u32, 7u32, 17u32), (1000u32, 13u32, 76u32), (65535u32, 255u32, 257u32), ];
2196
2197 for (dividend_val, divisor_val, expected_quotient) in test_cases {
2198 let dividend = TestInt::from(dividend_val);
2199 let divisor = TestInt::from(divisor_val);
2200
2201 let quotient = dividend / divisor;
2203 assert_eq!(
2204 quotient,
2205 TestInt::from(expected_quotient),
2206 "Division: {} / {} should be {}",
2207 dividend_val,
2208 divisor_val,
2209 expected_quotient
2210 );
2211
2212 let remainder = dividend % divisor;
2214 assert_eq!(
2215 quotient * divisor + remainder,
2216 dividend,
2217 "Division property check failed for {}",
2218 dividend_val
2219 );
2220 }
2221 }
2222
2223 #[test]
2224 fn test_code_simplification_benefits() {
2225 type TestInt = FixedUInt<u32, 2>;
2226
2227 let dividend = TestInt::from(12345u32);
2229 let divisor = TestInt::from(67u32);
2230 let quotient = dividend / divisor;
2231 let remainder = dividend % divisor;
2232
2233 assert_eq!(quotient * divisor + remainder, dividend);
2235 }
2236
2237 #[test]
2238 fn test_rem_assign_correctness_after_fix() {
2239 type TestInt = FixedUInt<u32, 2>;
2240
2241 let mut a = TestInt::from(17u32);
2243 let b = TestInt::from(5u32);
2244
2245 a %= b;
2248 assert_eq!(a, TestInt::from(2u32), "17 % 5 should be 2");
2249
2250 let mut test_val = TestInt::from(100u32);
2252 test_val %= TestInt::from(7u32);
2253 assert_eq!(
2254 test_val,
2255 TestInt::from(2u32),
2256 "100 % 7 should be 2 (not 14, the quotient)"
2257 );
2258 }
2259
2260 #[test]
2261 fn test_div_property_based() {
2262 type TestInt = FixedUInt<u16, 2>;
2263
2264 let test_pairs = [
2266 (12345u16, 67u16),
2267 (1000u16, 13u16),
2268 (65535u16, 255u16),
2269 (5000u16, 7u16),
2270 ];
2271
2272 for (dividend_val, divisor_val) in test_pairs {
2273 let dividend = TestInt::from(dividend_val);
2274 let divisor = TestInt::from(divisor_val);
2275
2276 let quotient = dividend / divisor;
2277
2278 let remainder = dividend - (quotient * divisor);
2280 let reconstructed = quotient * divisor + remainder;
2281
2282 assert_eq!(
2283 reconstructed,
2284 dividend,
2285 "Property failed for {} / {}: {} * {} + {} != {}",
2286 dividend_val,
2287 divisor_val,
2288 quotient.to_u32().unwrap_or(0),
2289 divisor_val,
2290 remainder.to_u32().unwrap_or(0),
2291 dividend_val
2292 );
2293
2294 assert!(
2296 remainder < divisor,
2297 "Remainder {} >= divisor {} for {} / {}",
2298 remainder.to_u32().unwrap_or(0),
2299 divisor_val,
2300 dividend_val,
2301 divisor_val
2302 );
2303 }
2304 }
2305}