fixed_bigint/
fixeduint.rs

1// Copyright 2021 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use num_traits::ops::overflowing::{OverflowingAdd, OverflowingSub};
16use num_traits::{Bounded, One, PrimInt, ToPrimitive, Zero};
17
18use core::convert::TryFrom;
19use core::fmt::Write;
20
21use crate::machineword::MachineWord;
22
23#[allow(unused_imports)]
24use num_traits::{FromPrimitive, Num};
25
26mod add_sub_impl;
27mod bit_ops_impl;
28mod euclid;
29mod iter_impl;
30mod mul_div_impl;
31mod num_integer_impl;
32mod num_traits_casts;
33mod num_traits_identity;
34mod prim_int_impl;
35mod roots_impl;
36mod string_conversion;
37mod to_from_bytes;
38
39/// Fixed-size unsigned integer, represented by array of N words of builtin unsigned type T
40#[derive(Debug, Clone, Copy, core::cmp::PartialEq, core::cmp::Eq)]
41pub struct FixedUInt<T, const N: usize>
42where
43    T: MachineWord,
44{
45    /// Little-endian word array
46    array: [T; N],
47}
48
49const LONGEST_WORD_IN_BITS: usize = 128;
50
51impl<T: MachineWord, const N: usize> FixedUInt<T, N> {
52    const WORD_SIZE: usize = core::mem::size_of::<T>();
53    const WORD_BITS: usize = Self::WORD_SIZE * 8;
54    const BYTE_SIZE: usize = Self::WORD_SIZE * N;
55    const BIT_SIZE: usize = Self::BYTE_SIZE * 8;
56
57    /// Creates and zero-initializes a FixedUInt.
58    pub fn new() -> FixedUInt<T, N> {
59        FixedUInt {
60            array: [T::zero(); N],
61        }
62    }
63
64    /// Returns number of used bits.
65    pub fn bit_length(&self) -> u32 {
66        Self::BIT_SIZE as u32 - self.leading_zeros()
67    }
68
69    /// Performs a division, returning both the quotient and reminder in a tuple.
70    pub fn div_rem(&self, divisor: &Self) -> (Self, Self) {
71        let quot = *self / *divisor;
72        let tmp = quot * *divisor;
73        let rem = *self - tmp;
74        (quot, rem)
75    }
76
77    /// Create a little-endian integer value from its representation as a byte array in little endian.
78    pub fn from_le_bytes(bytes: &[u8]) -> Self {
79        let mut ret = Self::new();
80        let total_bytes = core::cmp::min(bytes.len(), N * Self::WORD_SIZE);
81
82        for (byte_index, &byte) in bytes.iter().enumerate().take(total_bytes) {
83            let word_index = byte_index / Self::WORD_SIZE;
84            let byte_in_word = byte_index % Self::WORD_SIZE;
85
86            let byte_value: T = byte.into();
87            let shifted_value = byte_value.shl(byte_in_word * 8);
88            ret.array[word_index] |= shifted_value;
89        }
90        ret
91    }
92
93    /// Create a big-endian integer value from its representation as a byte array in big endian.
94    pub fn from_be_bytes(bytes: &[u8]) -> Self {
95        let mut ret = Self::new();
96        let capacity_bytes = N * Self::WORD_SIZE;
97        let total_bytes = core::cmp::min(bytes.len(), capacity_bytes);
98
99        // For consistent truncation semantics with from_le_bytes, always take the
100        // least significant bytes (rightmost bytes in big-endian representation)
101        let start_offset = if bytes.len() > capacity_bytes {
102            bytes.len() - capacity_bytes
103        } else {
104            0
105        };
106
107        for (byte_index, _) in (0..total_bytes).enumerate() {
108            // Take bytes from the end of the input (least significant in BE)
109            let be_byte_index = start_offset + total_bytes - 1 - byte_index;
110            let word_index = byte_index / Self::WORD_SIZE;
111            let byte_in_word = byte_index % Self::WORD_SIZE;
112
113            let byte_value: T = bytes[be_byte_index].into();
114            let shifted_value = byte_value.shl(byte_in_word * 8);
115            ret.array[word_index] |= shifted_value;
116        }
117        ret
118    }
119
120    /// Converts the FixedUInt into a little-endian byte array.
121    pub fn to_le_bytes<'a>(&self, output_buffer: &'a mut [u8]) -> Result<&'a [u8], bool> {
122        let total_bytes = N * Self::WORD_SIZE;
123        if output_buffer.len() < total_bytes {
124            return Err(false); // Buffer too small
125        }
126        for (i, word) in self.array.iter().enumerate() {
127            let start = i * Self::WORD_SIZE;
128            let end = start + Self::WORD_SIZE;
129            let word_bytes = word.to_le_bytes();
130            output_buffer[start..end].copy_from_slice(word_bytes.as_ref());
131        }
132        Ok(&output_buffer[..total_bytes])
133    }
134
135    /// Converts the FixedUInt into a big-endian byte array.
136    pub fn to_be_bytes<'a>(&self, output_buffer: &'a mut [u8]) -> Result<&'a [u8], bool> {
137        let total_bytes = N * Self::WORD_SIZE;
138        if output_buffer.len() < total_bytes {
139            return Err(false); // Buffer too small
140        }
141        for (i, word) in self.array.iter().rev().enumerate() {
142            let start = i * Self::WORD_SIZE;
143            let end = start + Self::WORD_SIZE;
144            let word_bytes = word.to_be_bytes();
145            output_buffer[start..end].copy_from_slice(word_bytes.as_ref());
146        }
147        Ok(&output_buffer[..total_bytes])
148    }
149
150    /// Converts to hex string, given a buffer. CAVEAT: This method removes any leading zeroes
151    pub fn to_hex_str<'a>(&self, result: &'a mut [u8]) -> Result<&'a str, core::fmt::Error> {
152        type Error = core::fmt::Error;
153
154        let word_size = Self::WORD_SIZE;
155        // need length minus leading zeros
156        let need_bits = self.bit_length() as usize;
157        // number of needed characters (bits/4 = bytes * 2)
158        let need_chars = if need_bits > 0 { need_bits / 4 } else { 0 };
159
160        if result.len() < need_chars {
161            // not enough space in result...
162            return Err(Error {});
163        }
164        let offset = result.len() - need_chars;
165        for i in result.iter_mut() {
166            *i = b'0';
167        }
168
169        for iter_words in 0..self.array.len() {
170            let word = self.array[iter_words];
171            let mut encoded = [0u8; LONGEST_WORD_IN_BITS / 4];
172            let encode_slice = &mut encoded[0..word_size * 2];
173            let mut wordbytes = word.to_le_bytes();
174            wordbytes.as_mut().reverse();
175            let wordslice = wordbytes.as_ref();
176            to_slice_hex(wordslice, encode_slice).map_err(|_| Error {})?;
177            for iter_chars in 0..encode_slice.len() {
178                let copy_char_to = (iter_words * word_size * 2) + iter_chars;
179                if copy_char_to <= need_chars {
180                    let reverse_index = offset + (need_chars - copy_char_to);
181                    if reverse_index <= result.len() && reverse_index > 0 {
182                        let current_char = encode_slice[(encode_slice.len() - 1) - iter_chars];
183                        result[reverse_index - 1] = current_char;
184                    }
185                }
186            }
187        }
188
189        let convert = core::str::from_utf8(result).map_err(|_| Error {})?;
190        let pos = convert.find(|c: char| c != '0');
191        match pos {
192            Some(x) => Ok(&convert[x..convert.len()]),
193            None => {
194                if convert.starts_with('0') {
195                    Ok("0")
196                } else {
197                    Ok(convert)
198                }
199            }
200        }
201    }
202
203    /// Converts to decimal string, given a buffer. CAVEAT: This method removes any leading zeroes
204    pub fn to_radix_str<'a>(
205        &self,
206        result: &'a mut [u8],
207        radix: u8,
208    ) -> Result<&'a str, core::fmt::Error> {
209        type Error = core::fmt::Error;
210
211        if !(2..=16).contains(&radix) {
212            return Err(Error {}); // Radix out of supported range
213        }
214        for byte in result.iter_mut() {
215            *byte = b'0';
216        }
217        if self.is_zero() {
218            if !result.is_empty() {
219                result[0] = b'0';
220                return core::str::from_utf8(&result[0..1]).map_err(|_| Error {});
221            } else {
222                return Err(Error {});
223            }
224        }
225
226        let mut number = *self;
227        let mut idx = result.len();
228
229        let radix_t = Self::from(radix);
230
231        while !number.is_zero() {
232            if idx == 0 {
233                return Err(Error {}); // not enough space in result...
234            }
235
236            idx -= 1;
237            let (quotient, remainder) = number.div_rem(&radix_t);
238
239            let digit = remainder.to_u8().unwrap();
240            result[idx] = match digit {
241                0..=9 => b'0' + digit,          // digits
242                10..=16 => b'a' + (digit - 10), // alphabetic digits for bases > 10
243                _ => return Err(Error {}),
244            };
245
246            number = quotient;
247        }
248
249        let start = result[idx..].iter().position(|&c| c != b'0').unwrap_or(0);
250        let radix_str = core::str::from_utf8(&result[idx + start..]).map_err(|_| Error {})?;
251        Ok(radix_str)
252    }
253
254    fn hex_fmt(
255        &self,
256        formatter: &mut core::fmt::Formatter<'_>,
257        uppercase: bool,
258    ) -> Result<(), core::fmt::Error>
259    where
260        u8: core::convert::TryFrom<T>,
261    {
262        type Err = core::fmt::Error;
263
264        fn to_casedigit(byte: u8, uppercase: bool) -> Result<char, core::fmt::Error> {
265            let digit = core::char::from_digit(byte as u32, 16).ok_or(Err {})?;
266            if uppercase {
267                digit.to_uppercase().next().ok_or(Err {})
268            } else {
269                digit.to_lowercase().next().ok_or(Err {})
270            }
271        }
272
273        let mut leading_zero: bool = true;
274
275        let mut maybe_write = |nibble: char| -> Result<(), core::fmt::Error> {
276            leading_zero &= nibble == '0';
277            if !leading_zero {
278                formatter.write_char(nibble)?;
279            }
280            Ok(())
281        };
282
283        for index in (0..N).rev() {
284            let val = self.array[index];
285            let mask: T = 0xff.into();
286            for j in (0..Self::WORD_SIZE as u32).rev() {
287                let masked = val & mask.shl((j * 8) as usize);
288
289                let byte = u8::try_from(masked.shr((j * 8) as usize)).map_err(|_| Err {})?;
290
291                maybe_write(to_casedigit((byte & 0xf0) >> 4, uppercase)?)?;
292                maybe_write(to_casedigit(byte & 0x0f, uppercase)?)?;
293            }
294        }
295        Ok(())
296    }
297    // Add other to target, return overflow status
298    // Note: in-place, no extra storage is used
299    fn add_impl(target: &mut Self, other: &Self) -> bool {
300        let mut carry = T::zero();
301        for i in 0..N {
302            let (res, carry1) = target.array[i].overflowing_add(&other.array[i]);
303            let (res, carry2) = res.overflowing_add(&carry);
304            carry = if carry1 || carry2 {
305                T::one()
306            } else {
307                T::zero()
308            };
309            target.array[i] = res
310        }
311        !carry.is_zero()
312    }
313
314    // Here to avoid duplicating this in two traits
315    fn saturating_add_impl(self, other: &Self) -> Self {
316        let res = self.overflowing_add(other);
317        if res.1 {
318            Self::max_value()
319        } else {
320            res.0
321        }
322    }
323
324    // Subtract other from target, return overflow status
325    // Note: in-place, no extra storage is used
326    fn sub_impl(target: &mut Self, other: &Self) -> bool {
327        let mut borrow = T::zero();
328        for i in 0..N {
329            let (res, borrow1) = target.array[i].overflowing_sub(&other.array[i]);
330            let (res, borrow2) = res.overflowing_sub(&borrow);
331            borrow = if borrow1 || borrow2 {
332                T::one()
333            } else {
334                T::zero()
335            };
336            target.array[i] = res;
337        }
338        !borrow.is_zero()
339    }
340
341    fn saturating_sub_impl(self, other: &Self) -> Self {
342        let res = self.overflowing_sub(other);
343        if res.1 {
344            Self::zero()
345        } else {
346            res.0
347        }
348    }
349
350    // Multiply op1 with op2, return overflow status
351    // Note: uses extra `result` variable, not sure if in-place multiply is possible at all.
352    fn mul_impl<const CHECK_OVERFLOW: bool>(op1: &Self, op2: &Self) -> (Self, bool) {
353        let mut result = Self::zero();
354        let mut overflowed = false;
355        // Calculate N+1 rounds, to check for overflow
356        let max_rounds = if CHECK_OVERFLOW { N + 1 } else { N };
357        let t_max = T::max_value().to_double();
358        for i in 0..N {
359            let mut carry = T::DoubleWord::zero();
360            for j in 0..N {
361                let round = i + j;
362                if round < max_rounds {
363                    let mul_res = op1.array[i].to_double() * op2.array[j].to_double();
364                    let mut accumulator = T::DoubleWord::zero();
365                    if round < N {
366                        accumulator = result.array[round].to_double();
367                    }
368                    accumulator = accumulator + mul_res + carry;
369
370                    if accumulator > t_max {
371                        carry = accumulator >> Self::WORD_BITS;
372                        accumulator = accumulator & t_max;
373                    } else {
374                        carry = T::DoubleWord::zero();
375                    }
376                    if round < N {
377                        result.array[round] = T::from_double(accumulator);
378                    } else if CHECK_OVERFLOW {
379                        overflowed = overflowed || !accumulator.is_zero();
380                    }
381                }
382            }
383            if !carry.is_zero() && CHECK_OVERFLOW {
384                overflowed = true;
385            }
386        }
387        (result, overflowed)
388    }
389
390    /// Set a specific bit in the array without full array operations
391    fn set_bit(&mut self, pos: usize) {
392        if pos >= Self::BIT_SIZE {
393            return; // No-op for out-of-bounds
394        }
395        let word_idx = pos / Self::WORD_BITS;
396        let bit_idx = pos % Self::WORD_BITS;
397        self.array[word_idx] |= T::one().shl(bit_idx);
398    }
399
400    /// Compare self vs other << shift_bits without allocating
401    fn cmp_shifted(&self, other: &Self, shift_bits: usize) -> core::cmp::Ordering {
402        if shift_bits == 0 {
403            return self.cmp(other);
404        }
405
406        if shift_bits >= Self::BIT_SIZE {
407            // other << shift_bits would be 0, so compare self vs 0
408            if self.is_zero() {
409                return core::cmp::Ordering::Equal;
410            } else {
411                return core::cmp::Ordering::Greater;
412            }
413        }
414
415        // Calculate word and bit offsets
416        let word_shift = shift_bits / Self::WORD_BITS;
417        let bit_shift = shift_bits % Self::WORD_BITS;
418
419        // Compare from most significant words down
420        for i in (0..N).rev() {
421            let self_word = self.array[i];
422            let other_shifted_word = self.get_shifted_word(other, i, word_shift, bit_shift);
423
424            match self_word.cmp(&other_shifted_word) {
425                core::cmp::Ordering::Equal => continue,
426                other_result => return other_result,
427            }
428        }
429
430        core::cmp::Ordering::Equal
431    }
432
433    /// Get the value of other's word at position `word_idx` when other is logically shifted left
434    fn get_shifted_word(
435        &self,
436        other: &Self,
437        word_idx: usize,
438        word_shift: usize,
439        bit_shift: usize,
440    ) -> T {
441        if word_idx < word_shift {
442            // This word position would be filled with zeros from the shift
443            return T::zero();
444        }
445
446        let source_idx = word_idx - word_shift;
447
448        if bit_shift == 0 {
449            // Simple word alignment
450            if source_idx < N {
451                other.array[source_idx]
452            } else {
453                T::zero()
454            }
455        } else {
456            // Need to combine bits from two source words
457            let mut result = T::zero();
458
459            // Get bits from the primary source word
460            if source_idx < N {
461                result |= other.array[source_idx].shl(bit_shift);
462            }
463
464            // Get high bits from the next lower word (if it exists and we need them)
465            if source_idx > 0 && (source_idx - 1) < N {
466                let high_bits = other.array[source_idx - 1].shr(Self::WORD_BITS - bit_shift);
467                result |= high_bits;
468            }
469
470            result
471        }
472    }
473
474    /// Subtract other << shift_bits from self in-place
475    fn sub_shifted(&mut self, other: &Self, shift_bits: usize) {
476        if shift_bits == 0 {
477            *self -= *other;
478            return;
479        }
480
481        if shift_bits >= Self::BIT_SIZE {
482            // other << shift_bits would be 0, so no-op
483            return;
484        }
485
486        // Calculate word and bit offsets
487        let word_shift = shift_bits / Self::WORD_BITS;
488        let bit_shift = shift_bits % Self::WORD_BITS;
489
490        let mut borrow = false;
491
492        // Process each word from least significant to most significant
493        for i in 0..N {
494            let other_shifted_word = self.get_shifted_word(other, i, word_shift, bit_shift);
495
496            // Perform subtraction with borrow
497            let (result, new_borrow) =
498                Self::sub_with_borrow(self.array[i], other_shifted_word, borrow);
499            self.array[i] = result;
500            borrow = new_borrow;
501        }
502    }
503
504    /// Subtract with borrow: a - b - borrow, returns (result, did_borrow)
505    fn sub_with_borrow(a: T, b: T, borrow: bool) -> (T, bool) {
506        // First subtract the borrow
507        let (temp, borrow1) = if borrow {
508            a.overflowing_sub(&T::one())
509        } else {
510            (a, false)
511        };
512
513        // Then subtract b
514        let (result, borrow2) = temp.overflowing_sub(&b);
515        (result, borrow1 || borrow2)
516    }
517
518    /// In-place division: dividend becomes quotient, returns remainder
519    fn div_assign_impl(dividend: &mut Self, divisor: &Self) -> Self {
520        if *dividend < *divisor {
521            let remainder = *dividend;
522            *dividend = Self::zero();
523            return remainder;
524        }
525        if *dividend == *divisor {
526            *dividend = Self::one();
527            return Self::zero();
528        }
529
530        let mut quotient = Self::zero();
531
532        // Calculate initial bit position
533        let dividend_bits = dividend.bit_length() as usize;
534        let divisor_bits = divisor.bit_length() as usize;
535
536        let mut bit_pos = dividend_bits.saturating_sub(divisor_bits);
537
538        // Adjust bit position to find the first position where divisor can be subtracted
539        while bit_pos > 0 && dividend.cmp_shifted(divisor, bit_pos) == core::cmp::Ordering::Less {
540            bit_pos -= 1;
541        }
542
543        // Main division loop - dividend is both remainder and gets modified in-place
544        loop {
545            if dividend.cmp_shifted(divisor, bit_pos) != core::cmp::Ordering::Less {
546                dividend.sub_shifted(divisor, bit_pos);
547                quotient.set_bit(bit_pos);
548            }
549
550            if bit_pos == 0 {
551                break;
552            }
553            bit_pos -= 1;
554        }
555
556        let remainder = *dividend;
557        *dividend = quotient;
558        remainder
559    }
560
561    // Divide dividend with divisor, return result
562    fn div_impl(dividend: &Self, divisor: &Self) -> Self {
563        let mut dividend_copy = *dividend;
564        Self::div_assign_impl(&mut dividend_copy, divisor);
565        dividend_copy
566    }
567
568    // Shifts left by bits, in-place
569    fn shl_impl(target: &mut Self, bits: usize) {
570        let nwords = bits / Self::WORD_BITS;
571        let nbits = bits - nwords * Self::WORD_BITS;
572
573        // Move words
574        for i in (nwords..N).rev() {
575            target.array[i] = target.array[i - nwords];
576        }
577        // Zero out the remainder
578        for i in 0..nwords {
579            target.array[i] = T::zero();
580        }
581
582        if nbits != 0 {
583            // Shift remaining bits
584            for i in (1..N).rev() {
585                let right = target.array[i].shl(nbits);
586                let left = target.array[i - 1].shr(Self::WORD_BITS - nbits);
587                target.array[i] = right | left;
588            }
589            target.array[0] = target.array[0].shl(nbits);
590        }
591    }
592
593    // Shifts right by bits, in-place
594    fn shr_impl(target: &mut Self, bits: usize) {
595        let nwords = bits / Self::WORD_BITS;
596        let nbits = bits - nwords * Self::WORD_BITS;
597
598        let last_index = N - 1;
599        let last_word = N - nwords;
600
601        // Move words
602        for i in 0..last_word {
603            target.array[i] = target.array[i + nwords];
604        }
605
606        // Zero out the remainder
607        for i in last_word..N {
608            target.array[i] = T::zero();
609        }
610
611        if nbits != 0 {
612            // Shift remaining bits
613            for i in 0..last_index {
614                let left = target.array[i].shr(nbits);
615                let right = target.array[i + 1].shl(Self::WORD_BITS - nbits);
616                target.array[i] = left | right;
617            }
618            target.array[last_index] = target.array[last_index].shr(nbits);
619        }
620    }
621
622    // Normalize shift amounts for rotations
623    fn normalize_shift(bits: u32) -> u32 {
624        bits % (Self::BIT_SIZE as u32)
625    }
626}
627
628impl<T: MachineWord, const N: usize> Default for FixedUInt<T, N> {
629    fn default() -> Self {
630        Self::new()
631    }
632}
633
634impl<T: MachineWord, const N: usize> num_traits::Unsigned for FixedUInt<T, N> {}
635
636// #region Ordering
637
638impl<T: MachineWord, const N: usize> core::cmp::Ord for FixedUInt<T, N> {
639    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
640        for i in (0..N).rev() {
641            if self.array[i] > other.array[i] {
642                return core::cmp::Ordering::Greater;
643            }
644            if self.array[i] < other.array[i] {
645                return core::cmp::Ordering::Less;
646            }
647        }
648        core::cmp::Ordering::Equal
649    }
650}
651
652impl<T: MachineWord, const N: usize> core::cmp::PartialOrd for FixedUInt<T, N> {
653    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
654        Some(self.cmp(other))
655    }
656}
657
658// #endregion Ordering
659
660// #region core::convert::From<primitive>
661
662impl<T: MachineWord, const N: usize> core::convert::From<u8> for FixedUInt<T, N> {
663    fn from(x: u8) -> Self {
664        let mut ret = Self::new();
665        ret.array[0] = x.into();
666        ret
667    }
668}
669
670impl<T: MachineWord, const N: usize> core::convert::From<u16> for FixedUInt<T, N> {
671    fn from(x: u16) -> Self {
672        Self::from_le_bytes(&x.to_le_bytes())
673    }
674}
675
676impl<T: MachineWord, const N: usize> core::convert::From<u32> for FixedUInt<T, N> {
677    fn from(x: u32) -> Self {
678        Self::from_le_bytes(&x.to_le_bytes())
679    }
680}
681
682impl<T: MachineWord, const N: usize> core::convert::From<u64> for FixedUInt<T, N> {
683    fn from(x: u64) -> Self {
684        Self::from_le_bytes(&x.to_le_bytes())
685    }
686}
687
688// #endregion core::convert::From<primitive>
689
690// #region helpers
691
692// This is slightly less than ideal, but PIE isn't directly constructible
693// due to unstable members.
694fn make_parse_int_err() -> core::num::ParseIntError {
695    <u8>::from_str_radix("-", 2).err().unwrap()
696}
697fn make_overflow_err() -> core::num::ParseIntError {
698    <u8>::from_str_radix("101", 16).err().unwrap()
699}
700fn make_empty_error() -> core::num::ParseIntError {
701    <u8>::from_str_radix("", 8).err().unwrap()
702}
703
704fn to_slice_hex<T: AsRef<[u8]>>(
705    input: T,
706    output: &mut [u8],
707) -> Result<(), core::num::ParseIntError> {
708    fn from_digit(byte: u8) -> Option<char> {
709        core::char::from_digit(byte as u32, 16)
710    }
711    let r = input.as_ref();
712    if r.len() * 2 != output.len() {
713        return Err(make_parse_int_err());
714    }
715    for i in 0..r.len() {
716        let byte = r[i];
717        output[i * 2] = from_digit((byte & 0xf0) >> 4).ok_or_else(make_parse_int_err)? as u8;
718        output[i * 2 + 1] = from_digit(byte & 0x0f).ok_or_else(make_parse_int_err)? as u8;
719    }
720
721    Ok(())
722}
723
724enum PanicReason {
725    Add,
726    Sub,
727    Mul,
728    DivByZero,
729    RemByZero,
730}
731
732fn maybe_panic(r: PanicReason) {
733    match r {
734        PanicReason::Add => panic!("attempt to add with overflow"),
735        PanicReason::Sub => panic!("attempt to subtract with overflow"),
736        PanicReason::Mul => panic!("attempt to multiply with overflow"),
737        PanicReason::DivByZero => panic!("attempt to divide by zero"),
738        PanicReason::RemByZero => {
739            panic!("attempt to calculate the remainder with a divisor of zero")
740        }
741    }
742}
743
744// #endregion helpers
745
746#[cfg(test)]
747mod tests {
748    use super::FixedUInt as Bn;
749    use super::*;
750
751    type Bn8 = Bn<u8, 8>;
752    type Bn16 = Bn<u16, 4>;
753    type Bn32 = Bn<u32, 2>;
754
755    #[test]
756    fn test_core_convert_u8() {
757        let f = Bn::<u8, 1>::from(1u8);
758        assert_eq!(f.array, [1]);
759        let f = Bn::<u8, 2>::from(1u8);
760        assert_eq!(f.array, [1, 0]);
761
762        let f = Bn::<u16, 1>::from(1u8);
763        assert_eq!(f.array, [1]);
764        let f = Bn::<u16, 2>::from(1u8);
765        assert_eq!(f.array, [1, 0]);
766    }
767
768    #[test]
769    fn test_core_convert_u16() {
770        let f = Bn::<u8, 1>::from(1u16);
771        assert_eq!(f.array, [1]);
772        let f = Bn::<u8, 2>::from(1u16);
773        assert_eq!(f.array, [1, 0]);
774
775        let f = Bn::<u8, 1>::from(256u16);
776        assert_eq!(f.array, [0]);
777        let f = Bn::<u8, 2>::from(257u16);
778        assert_eq!(f.array, [1, 1]);
779        let f = Bn::<u8, 2>::from(65535u16);
780        assert_eq!(f.array, [255, 255]);
781
782        let f = Bn::<u16, 1>::from(1u16);
783        assert_eq!(f.array, [1]);
784        let f = Bn::<u16, 2>::from(1u16);
785        assert_eq!(f.array, [1, 0]);
786
787        let f = Bn::<u16, 1>::from(65535u16);
788        assert_eq!(f.array, [65535]);
789    }
790
791    #[test]
792    fn test_core_convert_u32() {
793        let f = Bn::<u8, 1>::from(1u32);
794        assert_eq!(f.array, [1]);
795        let f = Bn::<u8, 1>::from(256u32);
796        assert_eq!(f.array, [0]);
797
798        let f = Bn::<u8, 2>::from(1u32);
799        assert_eq!(f.array, [1, 0]);
800        let f = Bn::<u8, 2>::from(257u32);
801        assert_eq!(f.array, [1, 1]);
802        let f = Bn::<u8, 2>::from(65535u32);
803        assert_eq!(f.array, [255, 255]);
804
805        let f = Bn::<u8, 4>::from(1u32);
806        assert_eq!(f.array, [1, 0, 0, 0]);
807        let f = Bn::<u8, 4>::from(257u32);
808        assert_eq!(f.array, [1, 1, 0, 0]);
809        let f = Bn::<u8, 4>::from(u32::max_value());
810        assert_eq!(f.array, [255, 255, 255, 255]);
811
812        let f = Bn::<u8, 1>::from(1u32);
813        assert_eq!(f.array, [1]);
814        let f = Bn::<u8, 1>::from(256u32);
815        assert_eq!(f.array, [0]);
816
817        let f = Bn::<u16, 2>::from(65537u32);
818        assert_eq!(f.array, [1, 1]);
819
820        let f = Bn::<u32, 1>::from(1u32);
821        assert_eq!(f.array, [1]);
822        let f = Bn::<u32, 2>::from(1u32);
823        assert_eq!(f.array, [1, 0]);
824
825        let f = Bn::<u32, 1>::from(65537u32);
826        assert_eq!(f.array, [65537]);
827
828        let f = Bn::<u32, 1>::from(u32::max_value());
829        assert_eq!(f.array, [4294967295]);
830    }
831
832    #[test]
833    fn testsimple() {
834        assert_eq!(Bn::<u8, 8>::new(), Bn::<u8, 8>::new());
835
836        assert_eq!(Bn::<u8, 8>::from_u8(3).unwrap().to_u32(), Some(3));
837        assert_eq!(Bn::<u16, 4>::from_u8(3).unwrap().to_u32(), Some(3));
838        assert_eq!(Bn::<u32, 2>::from_u8(3).unwrap().to_u32(), Some(3));
839        assert_eq!(Bn::<u32, 2>::from_u64(3).unwrap().to_u32(), Some(3));
840        assert_eq!(Bn::<u8, 8>::from_u64(255).unwrap().to_u32(), Some(255));
841        assert_eq!(Bn::<u8, 8>::from_u64(256).unwrap().to_u32(), Some(256));
842        assert_eq!(Bn::<u8, 8>::from_u64(65536).unwrap().to_u32(), Some(65536));
843    }
844    #[test]
845    fn testfrom() {
846        let mut n1 = Bn::<u8, 8>::new();
847        n1.array[0] = 1;
848        assert_eq!(Some(1), n1.to_u32());
849        n1.array[1] = 1;
850        assert_eq!(Some(257), n1.to_u32());
851
852        let mut n2 = Bn::<u16, 8>::new();
853        n2.array[0] = 0xffff;
854        assert_eq!(Some(65535), n2.to_u32());
855        n2.array[0] = 0x0;
856        n2.array[2] = 0x1;
857        // Overflow
858        assert_eq!(None, n2.to_u32());
859        assert_eq!(Some(0x100000000), n2.to_u64());
860    }
861
862    #[test]
863    fn test_from_str_bitlengths() {
864        let test_s64 = "81906f5e4d3c2c01";
865        let test_u64: u64 = 0x81906f5e4d3c2c01;
866        let bb = Bn8::from_str_radix(test_s64, 16).unwrap();
867        let cc = Bn8::from_u64(test_u64).unwrap();
868        assert_eq!(cc.array, [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81]);
869        assert_eq!(bb.array, [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81]);
870        let dd = Bn16::from_u64(test_u64).unwrap();
871        let ff = Bn16::from_str_radix(test_s64, 16).unwrap();
872        assert_eq!(dd.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190]);
873        assert_eq!(ff.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190]);
874        let ee = Bn32::from_u64(test_u64).unwrap();
875        let gg = Bn32::from_str_radix(test_s64, 16).unwrap();
876        assert_eq!(ee.array, [0x4d3c2c01, 0x81906f5e]);
877        assert_eq!(gg.array, [0x4d3c2c01, 0x81906f5e]);
878    }
879
880    #[test]
881    fn test_from_str_stringlengths() {
882        let ab = Bn::<u8, 9>::from_str_radix("2281906f5e4d3c2c01", 16).unwrap();
883        assert_eq!(
884            ab.array,
885            [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81, 0x22]
886        );
887        assert_eq!(
888            [0x2c01, 0x4d3c, 0x6f5e, 0],
889            Bn::<u16, 4>::from_str_radix("6f5e4d3c2c01", 16)
890                .unwrap()
891                .array
892        );
893        assert_eq!(
894            [0x2c01, 0x4d3c, 0x6f5e, 0x190],
895            Bn::<u16, 4>::from_str_radix("1906f5e4d3c2c01", 16)
896                .unwrap()
897                .array
898        );
899        assert_eq!(
900            Err(make_overflow_err()),
901            Bn::<u16, 4>::from_str_radix("f81906f5e4d3c2c01", 16)
902        );
903        assert_eq!(
904            Err(make_overflow_err()),
905            Bn::<u16, 4>::from_str_radix("af81906f5e4d3c2c01", 16)
906        );
907        assert_eq!(
908            Err(make_overflow_err()),
909            Bn::<u16, 4>::from_str_radix("baaf81906f5e4d3c2c01", 16)
910        );
911        let ac = Bn::<u16, 5>::from_str_radix("baaf81906f5e4d3c2c01", 16).unwrap();
912        assert_eq!(ac.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190, 0xbaaf]);
913    }
914
915    #[test]
916    fn test_bit_length() {
917        assert_eq!(0, Bn8::from_u8(0).unwrap().bit_length());
918        assert_eq!(1, Bn8::from_u8(1).unwrap().bit_length());
919        assert_eq!(2, Bn8::from_u8(2).unwrap().bit_length());
920        assert_eq!(2, Bn8::from_u8(3).unwrap().bit_length());
921        assert_eq!(7, Bn8::from_u8(0x70).unwrap().bit_length());
922        assert_eq!(8, Bn8::from_u8(0xF0).unwrap().bit_length());
923        assert_eq!(9, Bn8::from_u16(0x1F0).unwrap().bit_length());
924
925        assert_eq!(20, Bn8::from_u64(990223).unwrap().bit_length());
926        assert_eq!(32, Bn8::from_u64(0xefffffff).unwrap().bit_length());
927        assert_eq!(32, Bn8::from_u64(0x8fffffff).unwrap().bit_length());
928        assert_eq!(31, Bn8::from_u64(0x7fffffff).unwrap().bit_length());
929        assert_eq!(34, Bn8::from_u64(0x3ffffffff).unwrap().bit_length());
930
931        assert_eq!(0, Bn32::from_u8(0).unwrap().bit_length());
932        assert_eq!(1, Bn32::from_u8(1).unwrap().bit_length());
933        assert_eq!(2, Bn32::from_u8(2).unwrap().bit_length());
934        assert_eq!(2, Bn32::from_u8(3).unwrap().bit_length());
935        assert_eq!(7, Bn32::from_u8(0x70).unwrap().bit_length());
936        assert_eq!(8, Bn32::from_u8(0xF0).unwrap().bit_length());
937        assert_eq!(9, Bn32::from_u16(0x1F0).unwrap().bit_length());
938
939        assert_eq!(20, Bn32::from_u64(990223).unwrap().bit_length());
940        assert_eq!(32, Bn32::from_u64(0xefffffff).unwrap().bit_length());
941        assert_eq!(32, Bn32::from_u64(0x8fffffff).unwrap().bit_length());
942        assert_eq!(31, Bn32::from_u64(0x7fffffff).unwrap().bit_length());
943        assert_eq!(34, Bn32::from_u64(0x3ffffffff).unwrap().bit_length());
944    }
945
946    #[test]
947    fn test_bit_length_1000() {
948        // Test bit_length with value 1000
949        let value = Bn32::from_u16(1000).unwrap();
950
951        // 1000 in binary is 1111101000, which has 10 bits
952        // Let's verify the implementation is working correctly
953        assert_eq!(value.to_u32().unwrap(), 1000);
954        assert_eq!(value.bit_length(), 10);
955
956        // Test some edge cases around 1000
957        assert_eq!(Bn32::from_u16(512).unwrap().bit_length(), 10); // 2^9 = 512
958        assert_eq!(Bn32::from_u16(1023).unwrap().bit_length(), 10); // 2^10 - 1 = 1023
959        assert_eq!(Bn32::from_u16(1024).unwrap().bit_length(), 11); // 2^10 = 1024
960
961        // Test with different word sizes to see if this makes a difference
962        assert_eq!(Bn8::from_u16(1000).unwrap().bit_length(), 10);
963        assert_eq!(Bn16::from_u16(1000).unwrap().bit_length(), 10);
964
965        // Test with different initialization methods
966        let value_from_str = Bn32::from_str_radix("1000", 10).unwrap();
967        assert_eq!(value_from_str.bit_length(), 10);
968
969        // This is the problematic case - let's debug it
970        let value_from_bytes = Bn32::from_le_bytes(&1000u16.to_le_bytes());
971        // Let's see what the actual value is
972        assert_eq!(
973            value_from_bytes.to_u32().unwrap_or(0),
974            1000,
975            "from_le_bytes didn't create the correct value"
976        );
977        assert_eq!(value_from_bytes.bit_length(), 10);
978    }
979    #[test]
980    fn test_cmp() {
981        let f0 = Bn8::zero();
982        let f1 = Bn8::zero();
983        let f2 = Bn8::one();
984        assert_eq!(f0, f1);
985        assert!(f2 > f0);
986        assert!(f0 < f2);
987        let f3 = Bn32::from_u64(990223).unwrap();
988        assert_eq!(f3, Bn32::from_u64(990223).unwrap());
989        let f4 = Bn32::from_u64(990224).unwrap();
990        assert!(f4 > Bn32::from_u64(990223).unwrap());
991
992        let f3 = Bn8::from_u64(990223).unwrap();
993        assert_eq!(f3, Bn8::from_u64(990223).unwrap());
994        let f4 = Bn8::from_u64(990224).unwrap();
995        assert!(f4 > Bn8::from_u64(990223).unwrap());
996    }
997
998    #[test]
999    fn test_le_be_bytes() {
1000        let le_bytes = [1, 2, 3, 4];
1001        let be_bytes = [4, 3, 2, 1];
1002        let u8_ver = FixedUInt::<u8, 4>::from_le_bytes(&le_bytes);
1003        let u16_ver = FixedUInt::<u16, 2>::from_le_bytes(&le_bytes);
1004        let u32_ver = FixedUInt::<u32, 1>::from_le_bytes(&le_bytes);
1005        let u8_ver_be = FixedUInt::<u8, 4>::from_be_bytes(&be_bytes);
1006        let u16_ver_be = FixedUInt::<u16, 2>::from_be_bytes(&be_bytes);
1007        let u32_ver_be = FixedUInt::<u32, 1>::from_be_bytes(&be_bytes);
1008
1009        assert_eq!(u8_ver.array, [1, 2, 3, 4]);
1010        assert_eq!(u16_ver.array, [0x0201, 0x0403]);
1011        assert_eq!(u32_ver.array, [0x04030201]);
1012        assert_eq!(u8_ver_be.array, [1, 2, 3, 4]);
1013        assert_eq!(u16_ver_be.array, [0x0201, 0x0403]);
1014        assert_eq!(u32_ver_be.array, [0x04030201]);
1015
1016        let mut output_buffer = [0u8; 16];
1017        assert_eq!(u8_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
1018        assert_eq!(u8_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
1019        assert_eq!(u16_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
1020        assert_eq!(u16_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
1021        assert_eq!(u32_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
1022        assert_eq!(u32_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
1023    }
1024
1025    // Test suite for optimized division implementation
1026    #[test]
1027    fn test_div_impl_equivalence_small() {
1028        type TestInt = FixedUInt<u8, 2>;
1029
1030        // Test small values
1031        let test_cases = [
1032            (20u16, 3u16, 6u16),        // 20 / 3 = 6
1033            (100u16, 7u16, 14u16),      // 100 / 7 = 14
1034            (255u16, 5u16, 51u16),      // 255 / 5 = 51
1035            (65535u16, 256u16, 255u16), // max u16 / 256 = 255
1036        ];
1037
1038        for (dividend_val, divisor_val, expected) in test_cases {
1039            let dividend = TestInt::from(dividend_val);
1040            let divisor = TestInt::from(divisor_val);
1041            let expected_result = TestInt::from(expected);
1042
1043            let result = TestInt::div_impl(&dividend, &divisor);
1044
1045            assert_eq!(
1046                result, expected_result,
1047                "Division failed for {} / {} = {}",
1048                dividend_val, divisor_val, expected
1049            );
1050        }
1051    }
1052
1053    #[test]
1054    fn test_div_impl_equivalence_edge_cases() {
1055        type TestInt = FixedUInt<u16, 2>;
1056
1057        // Edge cases
1058        let dividend = TestInt::from(1000u16);
1059        let divisor = TestInt::from(1u16);
1060        assert_eq!(
1061            TestInt::div_impl(&dividend, &divisor),
1062            TestInt::from(1000u16)
1063        );
1064
1065        // Equal values
1066        let dividend = TestInt::from(42u16);
1067        let divisor = TestInt::from(42u16);
1068        assert_eq!(TestInt::div_impl(&dividend, &divisor), TestInt::from(1u16));
1069
1070        // Dividend < divisor
1071        let dividend = TestInt::from(5u16);
1072        let divisor = TestInt::from(10u16);
1073        assert_eq!(TestInt::div_impl(&dividend, &divisor), TestInt::from(0u16));
1074
1075        // Powers of 2
1076        let dividend = TestInt::from(1024u16);
1077        let divisor = TestInt::from(4u16);
1078        assert_eq!(
1079            TestInt::div_impl(&dividend, &divisor),
1080            TestInt::from(256u16)
1081        );
1082    }
1083
1084    #[test]
1085    fn test_helper_methods() {
1086        type TestInt = FixedUInt<u8, 2>;
1087
1088        // Test set_bit
1089        let mut val = TestInt::zero();
1090        val.set_bit(0);
1091        assert_eq!(val, TestInt::from(1u8));
1092
1093        val.set_bit(8);
1094        assert_eq!(val, TestInt::from(257u16)); // bit 0 + bit 8 = 1 + 256 = 257
1095
1096        // Test cmp_shifted
1097        let a = TestInt::from(8u8); // 1000 in binary
1098        let b = TestInt::from(1u8); // 0001 in binary
1099
1100        // b << 3 = 8, so a == (b << 3)
1101        assert_eq!(a.cmp_shifted(&b, 3), core::cmp::Ordering::Equal);
1102
1103        // a > (b << 2) because b << 2 = 4
1104        assert_eq!(a.cmp_shifted(&b, 2), core::cmp::Ordering::Greater);
1105
1106        // a < (b << 4) because b << 4 = 16
1107        assert_eq!(a.cmp_shifted(&b, 4), core::cmp::Ordering::Less);
1108
1109        // Test sub_shifted
1110        let mut val = TestInt::from(10u8);
1111        val.sub_shifted(&TestInt::from(1u8), 2); // subtract 1 << 2 = 4
1112        assert_eq!(val, TestInt::from(6u8)); // 10 - 4 = 6
1113    }
1114
1115    #[test]
1116    fn test_shifted_operations_comprehensive() {
1117        type TestInt = FixedUInt<u32, 2>;
1118
1119        // Test cmp_shifted with various word boundary cases
1120        let a = TestInt::from(0x12345678u32);
1121        let b = TestInt::from(0x12345678u32);
1122
1123        // Equal comparison
1124        assert_eq!(a.cmp_shifted(&b, 0), core::cmp::Ordering::Equal);
1125
1126        // Test shifts that cross word boundaries (assuming 32-bit words)
1127        let c = TestInt::from(0x123u32); // Small number
1128        let d = TestInt::from(0x48d159e2u32); // c << 16 + some bits
1129
1130        // c << 16 should be less than d
1131        assert_eq!(d.cmp_shifted(&c, 16), core::cmp::Ordering::Greater);
1132
1133        // Test large shifts (beyond bit size, so shifted value becomes 0)
1134        let e = TestInt::from(1u32);
1135        assert_eq!(
1136            e.cmp_shifted(&TestInt::from(0u32), 100),
1137            core::cmp::Ordering::Greater
1138        );
1139        // When shift is beyond bit size, 1 << 100 becomes 0, so 0 == 0
1140        assert_eq!(
1141            TestInt::from(0u32).cmp_shifted(&e, 100),
1142            core::cmp::Ordering::Equal
1143        );
1144
1145        // Test sub_shifted with word boundary crossing
1146        let mut val = TestInt::from(0x10000u32); // 65536
1147        val.sub_shifted(&TestInt::from(1u32), 15); // subtract 1 << 15 = 32768
1148        assert_eq!(val, TestInt::from(0x8000u32)); // 65536 - 32768 = 32768
1149
1150        // Test sub_shifted with multi-word operations
1151        let mut big_val = TestInt::from(0x100000000u64); // 2^32
1152        big_val.sub_shifted(&TestInt::from(1u32), 31); // subtract 1 << 31 = 2^31
1153        assert_eq!(big_val, TestInt::from(0x80000000u64)); // 2^32 - 2^31 = 2^31
1154    }
1155
1156    #[test]
1157    fn test_shifted_operations_edge_cases() {
1158        type TestInt = FixedUInt<u32, 2>;
1159
1160        // Test zero shifts
1161        let a = TestInt::from(42u32);
1162        assert_eq!(
1163            a.cmp_shifted(&TestInt::from(42u32), 0),
1164            core::cmp::Ordering::Equal
1165        );
1166
1167        let mut b = TestInt::from(42u32);
1168        b.sub_shifted(&TestInt::from(10u32), 0);
1169        assert_eq!(b, TestInt::from(32u32));
1170
1171        // Test massive shifts (beyond bit size)
1172        let c = TestInt::from(123u32);
1173        assert_eq!(
1174            c.cmp_shifted(&TestInt::from(456u32), 200),
1175            core::cmp::Ordering::Greater
1176        );
1177
1178        let mut d = TestInt::from(123u32);
1179        d.sub_shifted(&TestInt::from(456u32), 200); // Should be no-op
1180        assert_eq!(d, TestInt::from(123u32));
1181
1182        // Test with zero values
1183        let zero = TestInt::from(0u32);
1184        assert_eq!(zero.cmp_shifted(&zero, 10), core::cmp::Ordering::Equal);
1185        assert_eq!(
1186            TestInt::from(1u32).cmp_shifted(&zero, 10),
1187            core::cmp::Ordering::Greater
1188        );
1189    }
1190
1191    #[test]
1192    fn test_shifted_operations_equivalence() {
1193        type TestInt = FixedUInt<u32, 2>;
1194
1195        // Test that optimized operations give same results as naive shift+op
1196        let test_cases = [
1197            (0x12345u32, 0x678u32, 4),
1198            (0x1000u32, 0x10u32, 8),
1199            (0xABCDu32, 0x1u32, 16),
1200            (0x80000000u32, 0x1u32, 1),
1201        ];
1202
1203        for (a_val, b_val, shift) in test_cases {
1204            let a = TestInt::from(a_val);
1205            let b = TestInt::from(b_val);
1206
1207            // Test cmp_shifted equivalence
1208            let optimized_cmp = a.cmp_shifted(&b, shift);
1209            let naive_cmp = a.cmp(&(b << shift));
1210            assert_eq!(
1211                optimized_cmp, naive_cmp,
1212                "cmp_shifted mismatch: {} vs ({} << {})",
1213                a_val, b_val, shift
1214            );
1215
1216            // Test sub_shifted equivalence (if subtraction won't underflow)
1217            if a >= (b << shift) {
1218                let mut optimized_result = a;
1219                optimized_result.sub_shifted(&b, shift);
1220
1221                let naive_result = a - (b << shift);
1222                assert_eq!(
1223                    optimized_result, naive_result,
1224                    "sub_shifted mismatch: {} - ({} << {})",
1225                    a_val, b_val, shift
1226                );
1227            }
1228        }
1229    }
1230
1231    #[test]
1232    fn test_div_assign_in_place_optimization() {
1233        type TestInt = FixedUInt<u32, 2>;
1234
1235        // Test that div_assign uses the optimized in-place algorithm
1236        let test_cases = [
1237            (100u32, 10u32, 10u32, 0u32),     // 100 / 10 = 10 remainder 0
1238            (123u32, 7u32, 17u32, 4u32),      // 123 / 7 = 17 remainder 4
1239            (1000u32, 13u32, 76u32, 12u32),   // 1000 / 13 = 76 remainder 12
1240            (65535u32, 255u32, 257u32, 0u32), // 65535 / 255 = 257 remainder 0
1241        ];
1242
1243        for (dividend_val, divisor_val, expected_quotient, expected_remainder) in test_cases {
1244            // Test div_assign
1245            let mut dividend = TestInt::from(dividend_val);
1246            let divisor = TestInt::from(divisor_val);
1247
1248            dividend /= divisor;
1249            assert_eq!(
1250                dividend,
1251                TestInt::from(expected_quotient),
1252                "div_assign: {} / {} should be {}",
1253                dividend_val,
1254                divisor_val,
1255                expected_quotient
1256            );
1257
1258            // Test div_assign_impl directly and verify it returns remainder
1259            let mut dividend2 = TestInt::from(dividend_val);
1260            let remainder = TestInt::div_assign_impl(&mut dividend2, &divisor);
1261            assert_eq!(
1262                dividend2,
1263                TestInt::from(expected_quotient),
1264                "div_assign_impl quotient: {} / {} should be {}",
1265                dividend_val,
1266                divisor_val,
1267                expected_quotient
1268            );
1269            assert_eq!(
1270                remainder,
1271                TestInt::from(expected_remainder),
1272                "div_assign_impl remainder: {} % {} should be {}",
1273                dividend_val,
1274                divisor_val,
1275                expected_remainder
1276            );
1277
1278            // Verify: quotient * divisor + remainder == original dividend
1279            assert_eq!(
1280                dividend2 * divisor + remainder,
1281                TestInt::from(dividend_val),
1282                "Property check failed for {}",
1283                dividend_val
1284            );
1285        }
1286    }
1287
1288    #[test]
1289    fn test_div_assign_stack_efficiency() {
1290        type TestInt = FixedUInt<u32, 4>; // 16 bytes each
1291
1292        // Create test values
1293        let mut dividend = TestInt::from(0x123456789ABCDEFu64);
1294        let divisor = TestInt::from(0x12345u32);
1295        let original_dividend = dividend;
1296
1297        // Perform in-place division
1298        dividend /= divisor;
1299
1300        // Verify correctness
1301        let remainder = original_dividend % divisor;
1302        assert_eq!(dividend * divisor + remainder, original_dividend);
1303    }
1304
1305    #[test]
1306    fn test_rem_assign_optimization() {
1307        type TestInt = FixedUInt<u32, 2>;
1308
1309        let test_cases = [
1310            (100u32, 10u32, 0u32),    // 100 % 10 = 0
1311            (123u32, 7u32, 4u32),     // 123 % 7 = 4
1312            (1000u32, 13u32, 12u32),  // 1000 % 13 = 12
1313            (65535u32, 255u32, 0u32), // 65535 % 255 = 0
1314        ];
1315
1316        for (dividend_val, divisor_val, expected_remainder) in test_cases {
1317            let mut dividend = TestInt::from(dividend_val);
1318            let divisor = TestInt::from(divisor_val);
1319
1320            dividend %= divisor;
1321            assert_eq!(
1322                dividend,
1323                TestInt::from(expected_remainder),
1324                "rem_assign: {} % {} should be {}",
1325                dividend_val,
1326                divisor_val,
1327                expected_remainder
1328            );
1329        }
1330    }
1331
1332    #[test]
1333    fn test_div_impl_forwarding() {
1334        type TestInt = FixedUInt<u32, 2>;
1335
1336        // Test that div_impl forwarding works correctly
1337        let test_cases = [
1338            (100u32, 10u32, 10u32),     // 100 / 10 = 10
1339            (123u32, 7u32, 17u32),      // 123 / 7 = 17
1340            (1000u32, 13u32, 76u32),    // 1000 / 13 = 76
1341            (65535u32, 255u32, 257u32), // 65535 / 255 = 257
1342        ];
1343
1344        for (dividend_val, divisor_val, expected_quotient) in test_cases {
1345            let dividend = TestInt::from(dividend_val);
1346            let divisor = TestInt::from(divisor_val);
1347
1348            // Test that div operator (which uses div_impl) works correctly
1349            let quotient = dividend / divisor;
1350            assert_eq!(
1351                quotient,
1352                TestInt::from(expected_quotient),
1353                "div_impl forwarding: {} / {} should be {}",
1354                dividend_val,
1355                divisor_val,
1356                expected_quotient
1357            );
1358
1359            // Verify the division property still holds
1360            let remainder = dividend % divisor;
1361            assert_eq!(
1362                quotient * divisor + remainder,
1363                dividend,
1364                "Division property check failed for {}",
1365                dividend_val
1366            );
1367        }
1368    }
1369
1370    #[test]
1371    fn test_code_simplification_benefits() {
1372        type TestInt = FixedUInt<u32, 2>;
1373
1374        // Verify that the simplified div_impl maintains same behavior
1375        let dividend = TestInt::from(12345u32);
1376        let divisor = TestInt::from(67u32);
1377        let quotient = dividend / divisor;
1378        let remainder = dividend % divisor;
1379
1380        // The division property should still hold
1381        assert_eq!(quotient * divisor + remainder, dividend);
1382    }
1383
1384    #[test]
1385    fn test_rem_assign_correctness_after_fix() {
1386        type TestInt = FixedUInt<u32, 2>;
1387
1388        // Test specific case: 17 % 5 = 2
1389        let mut a = TestInt::from(17u32);
1390        let b = TestInt::from(5u32);
1391
1392        // Before fix: div_assign_impl would modify a to quotient (3), then assign remainder (2)
1393        // After fix: div_rem properly computes remainder without corrupting intermediate state
1394        a %= b;
1395        assert_eq!(a, TestInt::from(2u32), "17 % 5 should be 2");
1396
1397        // Test that the original RemAssign bug would have failed this
1398        let mut test_val = TestInt::from(100u32);
1399        test_val %= TestInt::from(7u32);
1400        assert_eq!(
1401            test_val,
1402            TestInt::from(2u32),
1403            "100 % 7 should be 2 (not 14, the quotient)"
1404        );
1405    }
1406
1407    #[test]
1408    fn test_div_property_based() {
1409        type TestInt = FixedUInt<u16, 2>;
1410
1411        // Property: quotient * divisor + remainder == dividend
1412        let test_pairs = [
1413            (12345u16, 67u16),
1414            (1000u16, 13u16),
1415            (65535u16, 255u16),
1416            (5000u16, 7u16),
1417        ];
1418
1419        for (dividend_val, divisor_val) in test_pairs {
1420            let dividend = TestInt::from(dividend_val);
1421            let divisor = TestInt::from(divisor_val);
1422
1423            let quotient = TestInt::div_impl(&dividend, &divisor);
1424
1425            // Property verification: quotient * divisor + remainder == dividend
1426            let remainder = dividend - (quotient * divisor);
1427            let reconstructed = quotient * divisor + remainder;
1428
1429            assert_eq!(
1430                reconstructed,
1431                dividend,
1432                "Property failed for {} / {}: {} * {} + {} != {}",
1433                dividend_val,
1434                divisor_val,
1435                quotient.to_u32().unwrap_or(0),
1436                divisor_val,
1437                remainder.to_u32().unwrap_or(0),
1438                dividend_val
1439            );
1440
1441            // Remainder should be less than divisor
1442            assert!(
1443                remainder < divisor,
1444                "Remainder {} >= divisor {} for {} / {}",
1445                remainder.to_u32().unwrap_or(0),
1446                divisor_val,
1447                dividend_val,
1448                divisor_val
1449            );
1450        }
1451    }
1452}