Skip to main content

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::{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_div_impl;
42mod multiple_impl;
43mod num_integer_impl;
44mod num_traits_casts;
45mod num_traits_identity;
46mod power_of_two_impl;
47mod prim_int_impl;
48mod roots_impl;
49mod string_conversion;
50// ConstToBytes trait (nightly only, uses generic_const_exprs)
51#[cfg(feature = "nightly")]
52mod const_to_from_bytes;
53// num_traits::ToBytes/FromBytes (stable impl, no generic_const_exprs viral bounds)
54#[cfg(any(feature = "nightly", feature = "use-unsafe"))]
55mod to_from_bytes;
56
57#[cfg(feature = "zeroize")]
58use zeroize::DefaultIsZeroes;
59
60/// Fixed-size unsigned integer, represented by array of N words of builtin unsigned type T
61#[derive(Debug, Copy)]
62pub struct FixedUInt<T, const N: usize>
63where
64    T: MachineWord,
65{
66    /// Little-endian word array
67    pub(super) array: [T; N],
68}
69
70#[cfg(feature = "zeroize")]
71impl<T: MachineWord, const N: usize> DefaultIsZeroes for FixedUInt<T, N> {}
72
73impl<T, const N: usize> From<[T; N]> for FixedUInt<T, N>
74where
75    T: MachineWord,
76{
77    fn from(array: [T; N]) -> Self {
78        Self { array }
79    }
80}
81
82const LONGEST_WORD_IN_BITS: usize = 128;
83
84impl<T: MachineWord, const N: usize> FixedUInt<T, N> {
85    const WORD_SIZE: usize = core::mem::size_of::<T>();
86    const WORD_BITS: usize = Self::WORD_SIZE * 8;
87    const BYTE_SIZE: usize = Self::WORD_SIZE * N;
88    const BIT_SIZE: usize = Self::BYTE_SIZE * 8;
89
90    /// Creates and zero-initializes a FixedUInt.
91    pub fn new() -> FixedUInt<T, N> {
92        FixedUInt {
93            array: [T::zero(); N],
94        }
95    }
96
97    /// Returns the underlying array.
98    pub fn words(&self) -> &[T; N] {
99        &self.array
100    }
101
102    /// Returns number of used bits.
103    pub fn bit_length(&self) -> u32 {
104        Self::BIT_SIZE as u32 - ConstPrimInt::leading_zeros(*self)
105    }
106
107    /// Performs a division, returning both the quotient and remainder in a tuple.
108    pub fn div_rem(&self, divisor: &Self) -> (Self, Self) {
109        let (quotient, remainder) = const_div_rem(&self.array, &divisor.array);
110        (Self { array: quotient }, Self { array: remainder })
111    }
112}
113
114// Const-compatible from_bytes helper functions
115c0nst::c0nst! {
116    /// Const-compatible from_le_bytes implementation for slices.
117    /// Derives word_size internally from size_of::<T>().
118    pub(crate) c0nst fn impl_from_le_bytes_slice<T: [c0nst] ConstMachineWord, const N: usize>(
119        bytes: &[u8],
120    ) -> [T; N] {
121        let word_size = core::mem::size_of::<T>();
122        let mut ret: [T; N] = [T::zero(); N];
123        let capacity = N * word_size;
124        let total_bytes = if bytes.len() < capacity { bytes.len() } else { capacity };
125
126        let mut byte_index = 0;
127        while byte_index < total_bytes {
128            let word_index = byte_index / word_size;
129            let byte_in_word = byte_index % word_size;
130
131            let byte_value: T = T::from(bytes[byte_index]);
132            let shifted_value = byte_value.shl(byte_in_word * 8);
133            ret[word_index] = ret[word_index].bitor(shifted_value);
134            byte_index += 1;
135        }
136        ret
137    }
138
139    /// Const-compatible from_be_bytes implementation for slices.
140    /// Derives word_size internally from size_of::<T>().
141    pub(crate) c0nst fn impl_from_be_bytes_slice<T: [c0nst] ConstMachineWord, const N: usize>(
142        bytes: &[u8],
143    ) -> [T; N] {
144        let word_size = core::mem::size_of::<T>();
145        let mut ret: [T; N] = [T::zero(); N];
146        let capacity_bytes = N * word_size;
147        let total_bytes = if bytes.len() < capacity_bytes { bytes.len() } else { capacity_bytes };
148
149        // For consistent truncation semantics with from_le_bytes, always take the
150        // least significant bytes (rightmost bytes in big-endian representation)
151        let start_offset = if bytes.len() > capacity_bytes {
152            bytes.len() - capacity_bytes
153        } else {
154            0
155        };
156
157        let mut byte_index = 0;
158        while byte_index < total_bytes {
159            // Take bytes from the end of the input (least significant in BE)
160            let be_byte_index = start_offset + total_bytes - 1 - byte_index;
161            let word_index = byte_index / word_size;
162            let byte_in_word = byte_index % word_size;
163
164            let byte_value: T = T::from(bytes[be_byte_index]);
165            let shifted_value = byte_value.shl(byte_in_word * 8);
166            ret[word_index] = ret[word_index].bitor(shifted_value);
167            byte_index += 1;
168        }
169        ret
170    }
171}
172
173// Inherent from_bytes methods (not const - use ConstFromBytes trait for const access)
174impl<T: MachineWord, const N: usize> FixedUInt<T, N> {
175    /// Create a little-endian integer value from its representation as a byte array in little endian.
176    pub fn from_le_bytes(bytes: &[u8]) -> Self {
177        Self {
178            array: impl_from_le_bytes_slice::<T, N>(bytes),
179        }
180    }
181
182    /// Create a big-endian integer value from its representation as a byte array in big endian.
183    pub fn from_be_bytes(bytes: &[u8]) -> Self {
184        Self {
185            array: impl_from_be_bytes_slice::<T, N>(bytes),
186        }
187    }
188}
189
190impl<T: MachineWord, const N: usize> FixedUInt<T, N> {
191    /// Converts the FixedUInt into a little-endian byte array.
192    pub fn to_le_bytes<'a>(&self, output_buffer: &'a mut [u8]) -> Result<&'a [u8], bool> {
193        let total_bytes = N * Self::WORD_SIZE;
194        if output_buffer.len() < total_bytes {
195            return Err(false); // Buffer too small
196        }
197        for (i, word) in self.array.iter().enumerate() {
198            let start = i * Self::WORD_SIZE;
199            let end = start + Self::WORD_SIZE;
200            let word_bytes = word.to_le_bytes();
201            output_buffer[start..end].copy_from_slice(word_bytes.as_ref());
202        }
203        Ok(&output_buffer[..total_bytes])
204    }
205
206    /// Converts the FixedUInt into a big-endian byte array.
207    pub fn to_be_bytes<'a>(&self, output_buffer: &'a mut [u8]) -> Result<&'a [u8], bool> {
208        let total_bytes = N * Self::WORD_SIZE;
209        if output_buffer.len() < total_bytes {
210            return Err(false); // Buffer too small
211        }
212        for (i, word) in self.array.iter().rev().enumerate() {
213            let start = i * Self::WORD_SIZE;
214            let end = start + Self::WORD_SIZE;
215            let word_bytes = word.to_be_bytes();
216            output_buffer[start..end].copy_from_slice(word_bytes.as_ref());
217        }
218        Ok(&output_buffer[..total_bytes])
219    }
220
221    /// Converts to hex string, given a buffer. CAVEAT: This method removes any leading zeroes
222    pub fn to_hex_str<'a>(&self, result: &'a mut [u8]) -> Result<&'a str, core::fmt::Error> {
223        type Error = core::fmt::Error;
224
225        let word_size = Self::WORD_SIZE;
226        // need length minus leading zeros
227        let need_bits = self.bit_length() as usize;
228        // number of needed characters (bits/4 = bytes * 2)
229        let need_chars = if need_bits > 0 { need_bits / 4 } else { 0 };
230
231        if result.len() < need_chars {
232            // not enough space in result...
233            return Err(Error {});
234        }
235        let offset = result.len() - need_chars;
236        for i in result.iter_mut() {
237            *i = b'0';
238        }
239
240        for iter_words in 0..self.array.len() {
241            let word = self.array[iter_words];
242            let mut encoded = [0u8; LONGEST_WORD_IN_BITS / 4];
243            let encode_slice = &mut encoded[0..word_size * 2];
244            let mut wordbytes = word.to_le_bytes();
245            wordbytes.as_mut().reverse();
246            let wordslice = wordbytes.as_ref();
247            to_slice_hex(wordslice, encode_slice).map_err(|_| Error {})?;
248            for iter_chars in 0..encode_slice.len() {
249                let copy_char_to = (iter_words * word_size * 2) + iter_chars;
250                if copy_char_to <= need_chars {
251                    let reverse_index = offset + (need_chars - copy_char_to);
252                    if reverse_index <= result.len() && reverse_index > 0 {
253                        let current_char = encode_slice[(encode_slice.len() - 1) - iter_chars];
254                        result[reverse_index - 1] = current_char;
255                    }
256                }
257            }
258        }
259
260        let convert = core::str::from_utf8(result).map_err(|_| Error {})?;
261        let pos = convert.find(|c: char| c != '0');
262        match pos {
263            Some(x) => Ok(&convert[x..convert.len()]),
264            None => {
265                if convert.starts_with('0') {
266                    Ok("0")
267                } else {
268                    Ok(convert)
269                }
270            }
271        }
272    }
273
274    /// Converts to decimal string, given a buffer. CAVEAT: This method removes any leading zeroes
275    pub fn to_radix_str<'a>(
276        &self,
277        result: &'a mut [u8],
278        radix: u8,
279    ) -> Result<&'a str, core::fmt::Error> {
280        type Error = core::fmt::Error;
281
282        if !(2..=16).contains(&radix) {
283            return Err(Error {}); // Radix out of supported range
284        }
285        for byte in result.iter_mut() {
286            *byte = b'0';
287        }
288        if Zero::is_zero(self) {
289            if !result.is_empty() {
290                result[0] = b'0';
291                return core::str::from_utf8(&result[0..1]).map_err(|_| Error {});
292            } else {
293                return Err(Error {});
294            }
295        }
296
297        let mut number = *self;
298        let mut idx = result.len();
299
300        let radix_t = Self::from(radix);
301
302        while !Zero::is_zero(&number) {
303            if idx == 0 {
304                return Err(Error {}); // not enough space in result...
305            }
306
307            idx -= 1;
308            let (quotient, remainder) = number.div_rem(&radix_t);
309
310            let digit = remainder.to_u8().unwrap();
311            result[idx] = match digit {
312                0..=9 => b'0' + digit,          // digits
313                10..=16 => b'a' + (digit - 10), // alphabetic digits for bases > 10
314                _ => return Err(Error {}),
315            };
316
317            number = quotient;
318        }
319
320        let start = result[idx..].iter().position(|&c| c != b'0').unwrap_or(0);
321        let radix_str = core::str::from_utf8(&result[idx + start..]).map_err(|_| Error {})?;
322        Ok(radix_str)
323    }
324
325    /// Construct a new value with a different size.
326    ///
327    /// - If `N2 < N`, the most-significant (upper) words are truncated.
328    /// - If `N2 > N`, the additional most-significant words are filled with zeros.
329    #[must_use]
330    pub fn resize<const N2: usize>(&self) -> FixedUInt<T, N2> {
331        let mut array = [T::zero(); N2];
332        let min_size = N.min(N2);
333        array[..min_size].copy_from_slice(&self.array[..min_size]);
334        FixedUInt { array }
335    }
336
337    fn hex_fmt(
338        &self,
339        formatter: &mut core::fmt::Formatter<'_>,
340        uppercase: bool,
341    ) -> Result<(), core::fmt::Error>
342    where
343        u8: core::convert::TryFrom<T>,
344    {
345        type Err = core::fmt::Error;
346
347        fn to_casedigit(byte: u8, uppercase: bool) -> Result<char, core::fmt::Error> {
348            let digit = core::char::from_digit(byte as u32, 16).ok_or(Err {})?;
349            if uppercase {
350                digit.to_uppercase().next().ok_or(Err {})
351            } else {
352                digit.to_lowercase().next().ok_or(Err {})
353            }
354        }
355
356        let mut leading_zero: bool = true;
357
358        let mut maybe_write = |nibble: char| -> Result<(), core::fmt::Error> {
359            leading_zero &= nibble == '0';
360            if !leading_zero {
361                formatter.write_char(nibble)?;
362            }
363            Ok(())
364        };
365
366        for index in (0..N).rev() {
367            let val = self.array[index];
368            let mask: T = 0xff.into();
369            for j in (0..Self::WORD_SIZE as u32).rev() {
370                let masked = val & mask.shl((j * 8) as usize);
371
372                let byte = u8::try_from(masked.shr((j * 8) as usize)).map_err(|_| Err {})?;
373
374                maybe_write(to_casedigit((byte & 0xf0) >> 4, uppercase)?)?;
375                maybe_write(to_casedigit(byte & 0x0f, uppercase)?)?;
376            }
377        }
378        Ok(())
379    }
380}
381
382c0nst::c0nst! {
383    /// Const-compatible add implementation operating on raw arrays
384    pub(crate) c0nst fn add_impl<T: [c0nst] ConstMachineWord, const N: usize>(
385        target: &mut [T; N],
386        other: &[T; N]
387    ) -> bool {
388        let mut carry = T::zero();
389        let mut i = 0usize;
390        while i < N {
391            let (res, carry1) = target[i].overflowing_add(&other[i]);
392            let (res, carry2) = res.overflowing_add(&carry);
393            carry = if carry1 || carry2 {
394                T::one()
395            } else {
396                T::zero()
397            };
398            target[i] = res;
399            i += 1;
400        }
401        !carry.is_zero()
402    }
403}
404
405c0nst::c0nst! {
406    /// Const-compatible sub implementation operating on raw arrays
407    pub(crate) c0nst fn sub_impl<T: [c0nst] ConstMachineWord, const N: usize>(
408        target: &mut [T; N],
409        other: &[T; N]
410    ) -> bool {
411        let mut borrow = T::zero();
412        let mut i = 0usize;
413        while i < N {
414            let (res, borrow1) = target[i].overflowing_sub(&other[i]);
415            let (res, borrow2) = res.overflowing_sub(&borrow);
416            borrow = if borrow1 || borrow2 {
417                T::one()
418            } else {
419                T::zero()
420            };
421            target[i] = res;
422            i += 1;
423        }
424        !borrow.is_zero()
425    }
426}
427
428c0nst::c0nst! {
429    /// Const-compatible left shift implementation
430    pub(crate) c0nst fn const_shl_impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
431        target: &mut FixedUInt<T, N>,
432        bits: usize,
433    ) {
434        if N == 0 {
435            return;
436        }
437        let word_bits = FixedUInt::<T, N>::WORD_BITS;
438        let nwords = bits / word_bits;
439        let nbits = bits - nwords * word_bits;
440
441        // If shift >= total bits, result is zero
442        if nwords >= N {
443            let mut i = 0;
444            while i < N {
445                target.array[i] = T::zero();
446                i += 1;
447            }
448            return;
449        }
450
451        // Move words (backwards)
452        let mut i = N;
453        while i > nwords {
454            i -= 1;
455            target.array[i] = target.array[i - nwords];
456        }
457        // Zero out the lower words
458        let mut i = 0;
459        while i < nwords {
460            target.array[i] = T::zero();
461            i += 1;
462        }
463
464        if nbits != 0 {
465            // Shift remaining bits (backwards)
466            let mut i = N;
467            while i > 1 {
468                i -= 1;
469                let right = target.array[i] << nbits;
470                let left = target.array[i - 1] >> (word_bits - nbits);
471                target.array[i] = right | left;
472            }
473            target.array[0] <<= nbits;
474        }
475    }
476
477    /// Const-compatible right shift implementation
478    pub(crate) c0nst fn const_shr_impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
479        target: &mut FixedUInt<T, N>,
480        bits: usize,
481    ) {
482        if N == 0 {
483            return;
484        }
485        let word_bits = FixedUInt::<T, N>::WORD_BITS;
486        let nwords = bits / word_bits;
487        let nbits = bits - nwords * word_bits;
488
489        // If shift >= total bits, result is zero
490        if nwords >= N {
491            let mut i = 0;
492            while i < N {
493                target.array[i] = T::zero();
494                i += 1;
495            }
496            return;
497        }
498
499        let last_index = N - 1;
500        let last_word = N - nwords;
501
502        // Move words (forwards)
503        let mut i = 0;
504        while i < last_word {
505            target.array[i] = target.array[i + nwords];
506            i += 1;
507        }
508
509        // Zero out the upper words
510        let mut i = last_word;
511        while i < N {
512            target.array[i] = T::zero();
513            i += 1;
514        }
515
516        if nbits != 0 {
517            // Shift remaining bits (forwards)
518            let mut i = 0;
519            while i < last_index {
520                let left = target.array[i] >> nbits;
521                let right = target.array[i + 1] << (word_bits - nbits);
522                target.array[i] = left | right;
523                i += 1;
524            }
525            target.array[last_index] >>= nbits;
526        }
527    }
528
529    /// Standalone const-compatible array multiplication (no FixedUInt dependency)
530    /// Returns (result_array, overflowed)
531    pub(crate) c0nst fn const_mul<T: [c0nst] ConstMachineWord, const N: usize, const CHECK_OVERFLOW: bool>(
532        op1: &[T; N],
533        op2: &[T; N],
534        word_bits: usize,
535    ) -> ([T; N], bool) {
536        let mut result: [T; N] = [<T as ConstZero>::zero(); N];
537        let mut overflowed = false;
538        let t_max = <T as ConstMachineWord>::to_double(<T as ConstBounded>::max_value());
539        // Zero for double word type
540        let dw_zero = <<T as ConstMachineWord>::ConstDoubleWord as ConstZero>::zero();
541
542        let mut i = 0;
543        while i < N {
544            let mut carry = dw_zero;
545            let mut j = 0;
546            while j < N {
547                let round = i + j;
548                let op1_dw = <T as ConstMachineWord>::to_double(op1[i]);
549                let op2_dw = <T as ConstMachineWord>::to_double(op2[j]);
550                let mul_res = op1_dw * op2_dw;
551                let mut accumulator = if round < N {
552                    <T as ConstMachineWord>::to_double(result[round])
553                } else {
554                    dw_zero
555                };
556                accumulator = accumulator + mul_res + carry;
557
558                if accumulator > t_max {
559                    carry = accumulator >> word_bits;
560                    accumulator &= t_max;
561                } else {
562                    carry = dw_zero;
563                }
564                if round < N {
565                    result[round] = <T as ConstMachineWord>::from_double(accumulator);
566                } else if CHECK_OVERFLOW {
567                    overflowed = overflowed || accumulator != dw_zero;
568                }
569                j += 1;
570            }
571            if carry != dw_zero && CHECK_OVERFLOW {
572                overflowed = true;
573            }
574            i += 1;
575        }
576        (result, overflowed)
577    }
578
579    /// Get the bit width of a word type.
580    pub(crate) c0nst fn const_word_bits<T>() -> usize {
581        core::mem::size_of::<T>() * 8
582    }
583
584    /// Compare two words, returning Some(ordering) if not equal, None if equal.
585    pub(crate) c0nst fn const_cmp_words<T: [c0nst] ConstMachineWord>(a: T, b: T) -> Option<core::cmp::Ordering> {
586        if a > b {
587            Some(core::cmp::Ordering::Greater)
588        } else if a < b {
589            Some(core::cmp::Ordering::Less)
590        } else {
591            None
592        }
593    }
594
595    /// Count leading zeros in a const-compatible way
596    pub(crate) c0nst fn const_leading_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
597        array: &[T; N],
598    ) -> u32 {
599        let mut ret = 0u32;
600        let mut index = N;
601        while index > 0 {
602            index -= 1;
603            let v = array[index];
604            ret += <T as ConstPrimInt>::leading_zeros(v);
605            if !<T as ConstZero>::is_zero(&v) {
606                break;
607            }
608        }
609        ret
610    }
611
612    /// Count trailing zeros in a const-compatible way
613    pub(crate) c0nst fn const_trailing_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
614        array: &[T; N],
615    ) -> u32 {
616        let mut ret = 0u32;
617        let mut index = 0;
618        while index < N {
619            let v = array[index];
620            ret += <T as ConstPrimInt>::trailing_zeros(v);
621            if !<T as ConstZero>::is_zero(&v) {
622                break;
623            }
624            index += 1;
625        }
626        ret
627    }
628
629    /// Get bit length of array (total bits - leading zeros)
630    pub(crate) c0nst fn const_bit_length<T: [c0nst] ConstMachineWord, const N: usize>(
631        array: &[T; N],
632    ) -> usize {
633        let word_bits = const_word_bits::<T>();
634        let bit_size = N * word_bits;
635        bit_size - const_leading_zeros::<T, N>(array) as usize
636    }
637
638    /// Check if array is zero
639    pub(crate) c0nst fn const_is_zero<T: [c0nst] ConstMachineWord, const N: usize>(
640        array: &[T; N],
641    ) -> bool {
642        let mut index = 0;
643        while index < N {
644            if !<T as ConstZero>::is_zero(&array[index]) {
645                return false;
646            }
647            index += 1;
648        }
649        true
650    }
651
652    /// Set a specific bit in the array.
653    ///
654    /// The array uses little-endian representation where index 0 contains
655    /// the least significant word, and bit 0 is the least significant bit
656    /// of the entire integer.
657    pub(crate) c0nst fn const_set_bit<T: [c0nst] ConstMachineWord, const N: usize>(
658        array: &mut [T; N],
659        pos: usize,
660    ) {
661        let word_bits = const_word_bits::<T>();
662        let word_idx = pos / word_bits;
663        if word_idx >= N {
664            return;
665        }
666        let bit_idx = pos % word_bits;
667        array[word_idx] |= <T as ConstOne>::one() << bit_idx;
668    }
669
670    /// Compare two arrays in a const-compatible way.
671    ///
672    /// Arrays use little-endian representation where index 0 contains
673    /// the least significant word.
674    pub(crate) c0nst fn const_cmp<T: [c0nst] ConstMachineWord, const N: usize>(
675        a: &[T; N],
676        b: &[T; N],
677    ) -> core::cmp::Ordering {
678        let mut index = N;
679        while index > 0 {
680            index -= 1;
681            if let Some(ord) = const_cmp_words(a[index], b[index]) {
682                return ord;
683            }
684        }
685        core::cmp::Ordering::Equal
686    }
687
688    /// Get the value of array's word at position `word_idx` when logically shifted left.
689    ///
690    /// This helper computes what value would be at `word_idx` if the array
691    /// were shifted left by `word_shift` words plus `bit_shift` bits.
692    pub(crate) c0nst fn const_get_shifted_word<T: [c0nst] ConstMachineWord, const N: usize>(
693        array: &[T; N],
694        word_idx: usize,
695        word_shift: usize,
696        bit_shift: usize,
697    ) -> T {
698        let word_bits = const_word_bits::<T>();
699
700        // Guard against invalid bit_shift that would cause UB
701        if bit_shift >= word_bits {
702            return <T as ConstZero>::zero();
703        }
704
705        if word_idx < word_shift {
706            return <T as ConstZero>::zero();
707        }
708
709        let source_idx = word_idx - word_shift;
710
711        if bit_shift == 0 {
712            if source_idx < N {
713                array[source_idx]
714            } else {
715                <T as ConstZero>::zero()
716            }
717        } else {
718            let mut result = <T as ConstZero>::zero();
719
720            // Get bits from the primary source word
721            if source_idx < N {
722                result |= array[source_idx] << bit_shift;
723            }
724
725            // Get high bits from the next lower word (if it exists)
726            if source_idx > 0 && source_idx - 1 < N {
727                let high_bits = array[source_idx - 1] >> (word_bits - bit_shift);
728                result |= high_bits;
729            }
730
731            result
732        }
733    }
734
735    /// Compare array vs (other << shift_bits) in a const-compatible way.
736    ///
737    /// This is useful for division algorithms where we need to compare
738    /// the dividend against a shifted divisor without allocating.
739    pub(crate) c0nst fn const_cmp_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
740        array: &[T; N],
741        other: &[T; N],
742        shift_bits: usize,
743    ) -> core::cmp::Ordering {
744        let word_bits = const_word_bits::<T>();
745
746        if shift_bits == 0 {
747            return const_cmp::<T, N>(array, other);
748        }
749
750        let word_shift = shift_bits / word_bits;
751        if word_shift >= N {
752            // other << shift_bits would overflow to 0
753            if const_is_zero::<T, N>(array) {
754                return core::cmp::Ordering::Equal;
755            } else {
756                return core::cmp::Ordering::Greater;
757            }
758        }
759
760        let bit_shift = shift_bits % word_bits;
761
762        // Compare from most significant words down
763        let mut index = N;
764        while index > 0 {
765            index -= 1;
766            let self_word = array[index];
767            let other_shifted_word = const_get_shifted_word::<T, N>(
768                other, index, word_shift, bit_shift
769            );
770
771            if let Some(ord) = const_cmp_words(self_word, other_shifted_word) {
772                return ord;
773            }
774        }
775
776        core::cmp::Ordering::Equal
777    }
778
779    /// Subtract (other << shift_bits) from array in-place.
780    ///
781    /// This is used in division algorithms to subtract shifted divisor
782    /// from the remainder without allocating.
783    pub(crate) c0nst fn const_sub_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
784        array: &mut [T; N],
785        other: &[T; N],
786        shift_bits: usize,
787    ) {
788        let word_bits = const_word_bits::<T>();
789
790        if shift_bits == 0 {
791            sub_impl::<T, N>(array, other);
792            return;
793        }
794
795        let word_shift = shift_bits / word_bits;
796        if word_shift >= N {
797            return;
798        }
799
800        let bit_shift = shift_bits % word_bits;
801        let mut borrow = T::zero();
802        let mut index = 0;
803        while index < N {
804            let other_word = const_get_shifted_word::<T, N>(other, index, word_shift, bit_shift);
805            let (res, borrow1) = array[index].overflowing_sub(&other_word);
806            let (res, borrow2) = res.overflowing_sub(&borrow);
807            borrow = if borrow1 || borrow2 { T::one() } else { T::zero() };
808            array[index] = res;
809            index += 1;
810        }
811    }
812
813    /// In-place division: dividend becomes quotient, returns remainder.
814    ///
815    /// Low-level const-compatible division on arrays.
816    pub(crate) c0nst fn const_div<T: [c0nst] ConstMachineWord, const N: usize>(
817        dividend: &mut [T; N],
818        divisor: &[T; N],
819    ) -> [T; N] {
820        use core::cmp::Ordering;
821
822        match const_cmp::<T, N>(dividend, divisor) {
823            // dividend < divisor: quotient = 0, remainder = dividend
824            Ordering::Less => {
825                let remainder = *dividend;
826                let mut i = 0;
827                while i < N {
828                    dividend[i] = <T as ConstZero>::zero();
829                    i += 1;
830                }
831                return remainder;
832            }
833            // dividend == divisor: quotient = 1, remainder = 0
834            Ordering::Equal => {
835                let mut i = 0;
836                while i < N {
837                    dividend[i] = <T as ConstZero>::zero();
838                    i += 1;
839                }
840                if N > 0 {
841                    dividend[0] = <T as ConstOne>::one();
842                }
843                return [<T as ConstZero>::zero(); N];
844            }
845            Ordering::Greater => {}
846        }
847
848        let mut quotient = [<T as ConstZero>::zero(); N];
849
850        // Calculate initial bit position
851        let dividend_bits = const_bit_length::<T, N>(dividend);
852        let divisor_bits = const_bit_length::<T, N>(divisor);
853
854        let mut bit_pos = if dividend_bits >= divisor_bits {
855            dividend_bits - divisor_bits
856        } else {
857            0
858        };
859
860        // Adjust bit position to find the first position where divisor can be subtracted
861        while bit_pos > 0 {
862            let cmp = const_cmp_shifted::<T, N>(dividend, divisor, bit_pos);
863            if !matches!(cmp, Ordering::Less) {
864                break;
865            }
866            bit_pos -= 1;
867        }
868
869        // Main division loop
870        loop {
871            let cmp = const_cmp_shifted::<T, N>(dividend, divisor, bit_pos);
872            if !matches!(cmp, Ordering::Less) {
873                const_sub_shifted::<T, N>(dividend, divisor, bit_pos);
874                const_set_bit::<T, N>(&mut quotient, bit_pos);
875            }
876
877            if bit_pos == 0 {
878                break;
879            }
880            bit_pos -= 1;
881        }
882
883        let remainder = *dividend;
884        *dividend = quotient;
885        remainder
886    }
887
888    /// Const-compatible div_rem: returns (quotient, remainder).
889    ///
890    /// Panics on divide by zero.
891    pub(crate) c0nst fn const_div_rem<T: [c0nst] ConstMachineWord, const N: usize>(
892        dividend: &[T; N],
893        divisor: &[T; N],
894    ) -> ([T; N], [T; N]) {
895        if const_is_zero(divisor) {
896            maybe_panic(PanicReason::DivByZero)
897        }
898        let mut quotient = *dividend;
899        let remainder = const_div(&mut quotient, divisor);
900        (quotient, remainder)
901    }
902}
903
904c0nst::c0nst! {
905    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst Default for FixedUInt<T, N> {
906        fn default() -> Self {
907            <Self as ConstZero>::zero()
908        }
909    }
910
911    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst Clone for FixedUInt<T, N> {
912        fn clone(&self) -> Self {
913            *self
914        }
915    }
916}
917
918impl<T: MachineWord, const N: usize> num_traits::Unsigned for FixedUInt<T, N> {}
919
920// #region Equality and Ordering
921
922c0nst::c0nst! {
923    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::cmp::PartialEq for FixedUInt<T, N> {
924        fn eq(&self, other: &Self) -> bool {
925            self.array == other.array
926        }
927    }
928
929    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::cmp::Eq for FixedUInt<T, N> {}
930
931    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::cmp::Ord for FixedUInt<T, N> {
932        fn cmp(&self, other: &Self) -> core::cmp::Ordering {
933            const_cmp(&self.array, &other.array)
934        }
935    }
936
937    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::cmp::PartialOrd for FixedUInt<T, N> {
938        fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
939            Some(self.cmp(other))
940        }
941    }
942}
943
944// #endregion Equality and Ordering
945
946// #region core::convert::From<primitive>
947
948c0nst::c0nst! {
949    /// Const-compatible conversion from little-endian bytes to array of words.
950    /// Delegates to impl_from_le_bytes_slice to avoid code duplication.
951    c0nst fn const_from_le_bytes<T: [c0nst] ConstMachineWord, const N: usize, const B: usize>(
952        bytes: [u8; B],
953    ) -> [T; N] {
954        impl_from_le_bytes_slice::<T, N>(&bytes)
955    }
956
957    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::convert::From<u8> for FixedUInt<T, N> {
958        fn from(x: u8) -> Self {
959            Self { array: const_from_le_bytes(x.to_le_bytes()) }
960        }
961    }
962
963    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::convert::From<u16> for FixedUInt<T, N> {
964        fn from(x: u16) -> Self {
965            Self { array: const_from_le_bytes(x.to_le_bytes()) }
966        }
967    }
968
969    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::convert::From<u32> for FixedUInt<T, N> {
970        fn from(x: u32) -> Self {
971            Self { array: const_from_le_bytes(x.to_le_bytes()) }
972        }
973    }
974
975    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::convert::From<u64> for FixedUInt<T, N> {
976        fn from(x: u64) -> Self {
977            Self { array: const_from_le_bytes(x.to_le_bytes()) }
978        }
979    }
980}
981
982// #endregion core::convert::From<primitive>
983
984// #region helpers
985
986// This is slightly less than ideal, but PIE isn't directly constructible
987// due to unstable members.
988fn make_parse_int_err() -> core::num::ParseIntError {
989    <u8>::from_str_radix("-", 2).err().unwrap()
990}
991fn make_overflow_err() -> core::num::ParseIntError {
992    <u8>::from_str_radix("101", 16).err().unwrap()
993}
994fn make_empty_error() -> core::num::ParseIntError {
995    <u8>::from_str_radix("", 8).err().unwrap()
996}
997
998fn to_slice_hex<T: AsRef<[u8]>>(
999    input: T,
1000    output: &mut [u8],
1001) -> Result<(), core::num::ParseIntError> {
1002    fn from_digit(byte: u8) -> Option<char> {
1003        core::char::from_digit(byte as u32, 16)
1004    }
1005    let r = input.as_ref();
1006    if r.len() * 2 != output.len() {
1007        return Err(make_parse_int_err());
1008    }
1009    for i in 0..r.len() {
1010        let byte = r[i];
1011        output[i * 2] = from_digit((byte & 0xf0) >> 4).ok_or_else(make_parse_int_err)? as u8;
1012        output[i * 2 + 1] = from_digit(byte & 0x0f).ok_or_else(make_parse_int_err)? as u8;
1013    }
1014
1015    Ok(())
1016}
1017
1018pub(super) enum PanicReason {
1019    Add,
1020    Sub,
1021    Mul,
1022    DivByZero,
1023}
1024
1025c0nst::c0nst! {
1026    pub(super) c0nst fn maybe_panic(r: PanicReason) {
1027        match r {
1028            PanicReason::Add => panic!("attempt to add with overflow"),
1029            PanicReason::Sub => panic!("attempt to subtract with overflow"),
1030            PanicReason::Mul => panic!("attempt to multiply with overflow"),
1031            PanicReason::DivByZero => panic!("attempt to divide by zero"),
1032        }
1033    }
1034}
1035
1036// #endregion helpers
1037
1038#[cfg(test)]
1039mod tests {
1040    use super::FixedUInt as Bn;
1041    use super::*;
1042    use num_traits::One;
1043
1044    type Bn8 = Bn<u8, 8>;
1045    type Bn16 = Bn<u16, 4>;
1046    type Bn32 = Bn<u32, 2>;
1047
1048    c0nst::c0nst! {
1049        pub c0nst fn test_add<T: [c0nst] ConstMachineWord, const N: usize>(
1050            a: &mut [T; N],
1051            b: &[T; N]
1052        ) -> bool {
1053            add_impl(a, b)
1054        }
1055
1056        pub c0nst fn test_sub<T: [c0nst] ConstMachineWord, const N: usize>(
1057            a: &mut [T; N],
1058            b: &[T; N]
1059        ) -> bool {
1060            sub_impl(a, b)
1061        }
1062
1063        pub c0nst fn test_mul<T: [c0nst] ConstMachineWord, const N: usize>(
1064            a: &[T; N],
1065            b: &[T; N],
1066            word_bits: usize,
1067        ) -> ([T; N], bool) {
1068            const_mul::<T, N, true>(a, b, word_bits)
1069        }
1070
1071        pub c0nst fn arr_leading_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1072            a: &[T; N],
1073        ) -> u32 {
1074            const_leading_zeros::<T, N>(a)
1075        }
1076
1077        pub c0nst fn arr_trailing_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1078            a: &[T; N],
1079        ) -> u32 {
1080            const_trailing_zeros::<T, N>(a)
1081        }
1082
1083        pub c0nst fn arr_bit_length<T: [c0nst] ConstMachineWord, const N: usize>(
1084            a: &[T; N],
1085        ) -> usize {
1086            const_bit_length::<T, N>(a)
1087        }
1088
1089        pub c0nst fn arr_is_zero<T: [c0nst] ConstMachineWord, const N: usize>(
1090            a: &[T; N],
1091        ) -> bool {
1092            const_is_zero::<T, N>(a)
1093        }
1094
1095        pub c0nst fn arr_set_bit<T: [c0nst] ConstMachineWord, const N: usize>(
1096            a: &mut [T; N],
1097            pos: usize,
1098        ) {
1099            const_set_bit::<T, N>(a, pos)
1100        }
1101
1102        pub c0nst fn arr_cmp<T: [c0nst] ConstMachineWord, const N: usize>(
1103            a: &[T; N],
1104            b: &[T; N],
1105        ) -> core::cmp::Ordering {
1106            const_cmp::<T, N>(a, b)
1107        }
1108
1109        pub c0nst fn arr_cmp_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
1110            a: &[T; N],
1111            b: &[T; N],
1112            shift_bits: usize,
1113        ) -> core::cmp::Ordering {
1114            const_cmp_shifted::<T, N>(a, b, shift_bits)
1115        }
1116
1117        pub c0nst fn arr_get_shifted_word<T: [c0nst] ConstMachineWord, const N: usize>(
1118            a: &[T; N],
1119            word_idx: usize,
1120            word_shift: usize,
1121            bit_shift: usize,
1122        ) -> T {
1123            const_get_shifted_word::<T, N>(a, word_idx, word_shift, bit_shift)
1124        }
1125    }
1126
1127    #[test]
1128    fn test_const_add_impl() {
1129        // Simple add, no overflow
1130        let mut a: [u8; 4] = [1, 0, 0, 0];
1131        let b: [u8; 4] = [2, 0, 0, 0];
1132        let overflow = test_add(&mut a, &b);
1133        assert_eq!(a, [3, 0, 0, 0]);
1134        assert!(!overflow);
1135
1136        // Add with carry propagation
1137        let mut a: [u8; 4] = [255, 0, 0, 0];
1138        let b: [u8; 4] = [1, 0, 0, 0];
1139        let overflow = test_add(&mut a, &b);
1140        assert_eq!(a, [0, 1, 0, 0]);
1141        assert!(!overflow);
1142
1143        // Add with overflow
1144        let mut a: [u8; 4] = [255, 255, 255, 255];
1145        let b: [u8; 4] = [1, 0, 0, 0];
1146        let overflow = test_add(&mut a, &b);
1147        assert_eq!(a, [0, 0, 0, 0]);
1148        assert!(overflow);
1149
1150        // Test with u32 words
1151        let mut a: [u32; 2] = [0xFFFFFFFF, 0];
1152        let b: [u32; 2] = [1, 0];
1153        let overflow = test_add(&mut a, &b);
1154        assert_eq!(a, [0, 1]);
1155        assert!(!overflow);
1156
1157        #[cfg(feature = "nightly")]
1158        {
1159            const ADD_RESULT: ([u8; 4], bool) = {
1160                let mut a = [1u8, 0, 0, 0];
1161                let b = [2u8, 0, 0, 0];
1162                let overflow = test_add(&mut a, &b);
1163                (a, overflow)
1164            };
1165            assert_eq!(ADD_RESULT, ([3, 0, 0, 0], false));
1166        }
1167    }
1168
1169    #[test]
1170    fn test_const_sub_impl() {
1171        // Simple sub, no overflow
1172        let mut a: [u8; 4] = [3, 0, 0, 0];
1173        let b: [u8; 4] = [1, 0, 0, 0];
1174        let overflow = test_sub(&mut a, &b);
1175        assert_eq!(a, [2, 0, 0, 0]);
1176        assert!(!overflow);
1177
1178        // Sub with borrow propagation
1179        let mut a: [u8; 4] = [0, 1, 0, 0];
1180        let b: [u8; 4] = [1, 0, 0, 0];
1181        let overflow = test_sub(&mut a, &b);
1182        assert_eq!(a, [255, 0, 0, 0]);
1183        assert!(!overflow);
1184
1185        // Sub with underflow
1186        let mut a: [u8; 4] = [0, 0, 0, 0];
1187        let b: [u8; 4] = [1, 0, 0, 0];
1188        let overflow = test_sub(&mut a, &b);
1189        assert_eq!(a, [255, 255, 255, 255]);
1190        assert!(overflow);
1191
1192        // Test with u32 words
1193        let mut a: [u32; 2] = [0, 1];
1194        let b: [u32; 2] = [1, 0];
1195        let overflow = test_sub(&mut a, &b);
1196        assert_eq!(a, [0xFFFFFFFF, 0]);
1197        assert!(!overflow);
1198
1199        #[cfg(feature = "nightly")]
1200        {
1201            const SUB_RESULT: ([u8; 4], bool) = {
1202                let mut a = [3u8, 0, 0, 0];
1203                let b = [1u8, 0, 0, 0];
1204                let overflow = test_sub(&mut a, &b);
1205                (a, overflow)
1206            };
1207            assert_eq!(SUB_RESULT, ([2, 0, 0, 0], false));
1208        }
1209    }
1210
1211    #[test]
1212    fn test_const_mul_impl() {
1213        // Simple mul: 3 * 4 = 12
1214        let a: [u8; 2] = [3, 0];
1215        let b: [u8; 2] = [4, 0];
1216        let (result, overflow) = test_mul(&a, &b, 8);
1217        assert_eq!(result, [12, 0]);
1218        assert!(!overflow);
1219
1220        // Mul with carry: 200 * 2 = 400 = 0x190 = [0x90, 0x01]
1221        let a: [u8; 2] = [200, 0];
1222        let b: [u8; 2] = [2, 0];
1223        let (result, overflow) = test_mul(&a, &b, 8);
1224        assert_eq!(result, [0x90, 0x01]);
1225        assert!(!overflow);
1226
1227        // Mul with overflow: 256 * 256 = 65536 which overflows 16 bits
1228        let a: [u8; 2] = [0, 1]; // 256
1229        let b: [u8; 2] = [0, 1]; // 256
1230        let (_result, overflow) = test_mul(&a, &b, 8);
1231        assert!(overflow);
1232
1233        // N=3 overflow at high position (round=4, i=2, j=2)
1234        // a = [0, 0, 1] = 65536, b = [0, 0, 1] = 65536
1235        // a * b = 65536^2 = 4294967296 which overflows 24 bits
1236        let a: [u8; 3] = [0, 0, 1];
1237        let b: [u8; 3] = [0, 0, 1];
1238        let (_result, overflow) = test_mul(&a, &b, 8);
1239        assert!(overflow, "N=3 high-position overflow not detected");
1240
1241        // N=3 overflow with larger high word values
1242        // a = [0, 0, 2] = 131072, b = [0, 0, 2] = 131072
1243        // a * b = 131072^2 = 17179869184 which overflows 24 bits
1244        let a: [u8; 3] = [0, 0, 2];
1245        let b: [u8; 3] = [0, 0, 2];
1246        let (_result, overflow) = test_mul(&a, &b, 8);
1247        assert!(
1248            overflow,
1249            "N=3 high-position overflow with larger values not detected"
1250        );
1251
1252        // N=3 non-overflow case: values that fit in 24 bits
1253        // a = [0, 1, 0] = 256, b = [0, 1, 0] = 256
1254        // a * b = 256 * 256 = 65536 = [0, 0, 1] which fits in 24 bits
1255        let a: [u8; 3] = [0, 1, 0];
1256        let b: [u8; 3] = [0, 1, 0];
1257        let (result, overflow) = test_mul(&a, &b, 8);
1258        assert_eq!(result, [0, 0, 1]);
1259        assert!(
1260            !overflow,
1261            "N=3 non-overflow incorrectly detected as overflow"
1262        );
1263
1264        // N=3 non-overflow with carry propagation
1265        // a = [255, 0, 0] = 255, b = [255, 0, 0] = 255
1266        // a * b = 255 * 255 = 65025 = 0xFE01 = [0x01, 0xFE, 0x00]
1267        let a: [u8; 3] = [255, 0, 0];
1268        let b: [u8; 3] = [255, 0, 0];
1269        let (result, overflow) = test_mul(&a, &b, 8);
1270        assert_eq!(result, [0x01, 0xFE, 0x00]);
1271        assert!(!overflow);
1272
1273        #[cfg(feature = "nightly")]
1274        {
1275            const MUL_RESULT: ([u8; 2], bool) = test_mul(&[3u8, 0], &[4u8, 0], 8);
1276            assert_eq!(MUL_RESULT, ([12, 0], false));
1277        }
1278    }
1279
1280    #[test]
1281    fn test_const_helpers() {
1282        // Test leading_zeros
1283        assert_eq!(arr_leading_zeros(&[0u8, 0, 0, 0]), 32); // all zeros
1284        assert_eq!(arr_leading_zeros(&[1u8, 0, 0, 0]), 31); // single bit
1285        assert_eq!(arr_leading_zeros(&[0u8, 0, 0, 1]), 7); // high byte has 1
1286        assert_eq!(arr_leading_zeros(&[0u8, 0, 0, 0x80]), 0); // MSB set
1287        assert_eq!(arr_leading_zeros(&[255u8, 255, 255, 255]), 0); // all ones
1288
1289        // Test trailing_zeros
1290        assert_eq!(arr_trailing_zeros(&[0u8, 0, 0, 0]), 32); // all zeros
1291        assert_eq!(arr_trailing_zeros(&[1u8, 0, 0, 0]), 0); // LSB set
1292        assert_eq!(arr_trailing_zeros(&[0u8, 1, 0, 0]), 8); // second byte
1293        assert_eq!(arr_trailing_zeros(&[0u8, 0, 0, 1]), 24); // fourth byte
1294        assert_eq!(arr_trailing_zeros(&[0x80u8, 0, 0, 0]), 7); // bit 7 of first byte
1295
1296        // Test bit_length
1297        assert_eq!(arr_bit_length(&[0u8, 0, 0, 0]), 0); // zero
1298        assert_eq!(arr_bit_length(&[1u8, 0, 0, 0]), 1); // 1
1299        assert_eq!(arr_bit_length(&[2u8, 0, 0, 0]), 2); // 2
1300        assert_eq!(arr_bit_length(&[3u8, 0, 0, 0]), 2); // 3
1301        assert_eq!(arr_bit_length(&[0u8, 1, 0, 0]), 9); // 256
1302        assert_eq!(arr_bit_length(&[0xF0u8, 0, 0, 0]), 8); // 240 (0xF0)
1303        assert_eq!(arr_bit_length(&[255u8, 255, 255, 255]), 32); // max
1304
1305        // Test is_zero
1306        assert!(arr_is_zero(&[0u8, 0, 0, 0]));
1307        assert!(!arr_is_zero(&[1u8, 0, 0, 0]));
1308        assert!(!arr_is_zero(&[0u8, 0, 0, 1]));
1309        assert!(!arr_is_zero(&[0u8, 1, 0, 0]));
1310
1311        // Test set_bit
1312        let mut arr: [u8; 4] = [0, 0, 0, 0];
1313        arr_set_bit(&mut arr, 0);
1314        assert_eq!(arr, [1, 0, 0, 0]);
1315
1316        let mut arr: [u8; 4] = [0, 0, 0, 0];
1317        arr_set_bit(&mut arr, 8);
1318        assert_eq!(arr, [0, 1, 0, 0]);
1319
1320        let mut arr: [u8; 4] = [0, 0, 0, 0];
1321        arr_set_bit(&mut arr, 31);
1322        assert_eq!(arr, [0, 0, 0, 0x80]);
1323
1324        // Set multiple bits
1325        let mut arr: [u8; 4] = [0, 0, 0, 0];
1326        arr_set_bit(&mut arr, 0);
1327        arr_set_bit(&mut arr, 3);
1328        arr_set_bit(&mut arr, 8);
1329        assert_eq!(arr, [0b00001001, 1, 0, 0]);
1330
1331        // Out of bounds should be no-op
1332        let mut arr: [u8; 4] = [0, 0, 0, 0];
1333        arr_set_bit(&mut arr, 32);
1334        assert_eq!(arr, [0, 0, 0, 0]);
1335
1336        // Test with u32 words
1337        assert_eq!(arr_leading_zeros(&[0u32, 0]), 64);
1338        assert_eq!(arr_leading_zeros(&[1u32, 0]), 63);
1339        assert_eq!(arr_leading_zeros(&[0u32, 1]), 31);
1340        assert_eq!(arr_trailing_zeros(&[0u32, 0]), 64);
1341        assert_eq!(arr_trailing_zeros(&[0u32, 1]), 32);
1342        assert_eq!(arr_bit_length(&[0u32, 0]), 0);
1343        assert_eq!(arr_bit_length(&[1u32, 0]), 1);
1344        assert_eq!(arr_bit_length(&[0u32, 1]), 33);
1345
1346        #[cfg(feature = "nightly")]
1347        {
1348            const LEADING: u32 = arr_leading_zeros(&[0u8, 0, 1, 0]);
1349            assert_eq!(LEADING, 15);
1350
1351            const TRAILING: u32 = arr_trailing_zeros(&[0u8, 0, 1, 0]);
1352            assert_eq!(TRAILING, 16);
1353
1354            const BIT_LEN: usize = arr_bit_length(&[0u8, 0, 1, 0]);
1355            assert_eq!(BIT_LEN, 17);
1356
1357            const IS_ZERO: bool = arr_is_zero(&[0u8, 0, 0, 0]);
1358            assert!(IS_ZERO);
1359
1360            const NOT_ZERO: bool = arr_is_zero(&[0u8, 1, 0, 0]);
1361            assert!(!NOT_ZERO);
1362
1363            const SET_BIT_RESULT: [u8; 4] = {
1364                let mut arr = [0u8, 0, 0, 0];
1365                arr_set_bit(&mut arr, 10);
1366                arr
1367            };
1368            assert_eq!(SET_BIT_RESULT, [0, 0b00000100, 0, 0]);
1369        }
1370    }
1371
1372    #[test]
1373    fn test_const_cmp() {
1374        use core::cmp::Ordering;
1375
1376        // Equal arrays
1377        assert_eq!(arr_cmp(&[1u8, 2, 3, 4], &[1u8, 2, 3, 4]), Ordering::Equal);
1378        assert_eq!(arr_cmp(&[0u8, 0, 0, 0], &[0u8, 0, 0, 0]), Ordering::Equal);
1379
1380        // Greater - high word differs
1381        assert_eq!(arr_cmp(&[0u8, 0, 0, 2], &[0u8, 0, 0, 1]), Ordering::Greater);
1382
1383        // Less - high word differs
1384        assert_eq!(arr_cmp(&[0u8, 0, 0, 1], &[0u8, 0, 0, 2]), Ordering::Less);
1385
1386        // Greater - low word differs (high words equal)
1387        assert_eq!(arr_cmp(&[2u8, 0, 0, 0], &[1u8, 0, 0, 0]), Ordering::Greater);
1388
1389        // Less - low word differs
1390        assert_eq!(arr_cmp(&[1u8, 0, 0, 0], &[2u8, 0, 0, 0]), Ordering::Less);
1391
1392        // Test with u32 words
1393        assert_eq!(arr_cmp(&[0u32, 1], &[0u32, 1]), Ordering::Equal);
1394        assert_eq!(arr_cmp(&[0u32, 2], &[0u32, 1]), Ordering::Greater);
1395        assert_eq!(arr_cmp(&[0u32, 1], &[0u32, 2]), Ordering::Less);
1396
1397        #[cfg(feature = "nightly")]
1398        {
1399            const CMP_EQ: Ordering = arr_cmp(&[1u8, 2, 3, 4], &[1u8, 2, 3, 4]);
1400            const CMP_GT: Ordering = arr_cmp(&[0u8, 0, 0, 2], &[0u8, 0, 0, 1]);
1401            const CMP_LT: Ordering = arr_cmp(&[0u8, 0, 0, 1], &[0u8, 0, 0, 2]);
1402            assert_eq!(CMP_EQ, Ordering::Equal);
1403            assert_eq!(CMP_GT, Ordering::Greater);
1404            assert_eq!(CMP_LT, Ordering::Less);
1405        }
1406    }
1407
1408    #[test]
1409    fn test_const_cmp_shifted() {
1410        use core::cmp::Ordering;
1411
1412        // No shift - same as regular cmp
1413        assert_eq!(
1414            arr_cmp_shifted(&[1u8, 0, 0, 0], &[1u8, 0, 0, 0], 0),
1415            Ordering::Equal
1416        );
1417
1418        // Compare [0, 1, 0, 0] (256) vs [1, 0, 0, 0] << 8 (256) = Equal
1419        assert_eq!(
1420            arr_cmp_shifted(&[0u8, 1, 0, 0], &[1u8, 0, 0, 0], 8),
1421            Ordering::Equal
1422        );
1423
1424        // Compare [0, 2, 0, 0] (512) vs [1, 0, 0, 0] << 8 (256) = Greater
1425        assert_eq!(
1426            arr_cmp_shifted(&[0u8, 2, 0, 0], &[1u8, 0, 0, 0], 8),
1427            Ordering::Greater
1428        );
1429
1430        // Compare [0, 0, 0, 0] (0) vs [1, 0, 0, 0] << 8 (256) = Less
1431        assert_eq!(
1432            arr_cmp_shifted(&[0u8, 0, 0, 0], &[1u8, 0, 0, 0], 8),
1433            Ordering::Less
1434        );
1435
1436        // Shift overflow: shift >= bit_size, other becomes 0
1437        // Compare [1, 0, 0, 0] vs [1, 0, 0, 0] << 32 (0) = Greater
1438        assert_eq!(
1439            arr_cmp_shifted(&[1u8, 0, 0, 0], &[1u8, 0, 0, 0], 32),
1440            Ordering::Greater
1441        );
1442
1443        // Compare [0, 0, 0, 0] vs anything << 32 (0) = Equal
1444        assert_eq!(
1445            arr_cmp_shifted(&[0u8, 0, 0, 0], &[255u8, 255, 255, 255], 32),
1446            Ordering::Equal
1447        );
1448
1449        // Test get_shifted_word helper with bit_shift == 0
1450        // [1, 2, 3, 4] shifted left by 1 word (8 bits for u8)
1451        // word 0 should be 0, word 1 should be 1, word 2 should be 2, etc.
1452        assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 0, 1, 0), 0);
1453        assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 1, 1, 0), 1);
1454        assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 2, 1, 0), 2);
1455
1456        // Test get_shifted_word with bit_shift != 0 (cross-word bit combination)
1457        // [0x0F, 0xF0, 0, 0] with word_shift=0, bit_shift=4
1458        // word 0: 0x0F << 4 = 0xF0 (no lower word to borrow from)
1459        assert_eq!(arr_get_shifted_word(&[0x0Fu8, 0xF0, 0, 0], 0, 0, 4), 0xF0);
1460        // word 1: (0xF0 << 4) | (0x0F >> 4) = 0x00 | 0x00 = 0x00
1461        assert_eq!(arr_get_shifted_word(&[0x0Fu8, 0xF0, 0, 0], 1, 0, 4), 0x00);
1462
1463        // [0xFF, 0x00, 0, 0] with bit_shift=4
1464        // word 0: 0xFF << 4 = 0xF0
1465        assert_eq!(arr_get_shifted_word(&[0xFFu8, 0x00, 0, 0], 0, 0, 4), 0xF0);
1466        // word 1: (0x00 << 4) | (0xFF >> 4) = 0x00 | 0x0F = 0x0F
1467        assert_eq!(arr_get_shifted_word(&[0xFFu8, 0x00, 0, 0], 1, 0, 4), 0x0F);
1468
1469        // Combined word_shift and bit_shift
1470        // [0xAB, 0xCD, 0, 0] with word_shift=1, bit_shift=4
1471        // word 0: below word_shift, returns 0
1472        assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 0, 1, 4), 0);
1473        // word 1: source_idx=0, 0xAB << 4 = 0xB0 (no lower word)
1474        assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 1, 1, 4), 0xB0);
1475        // word 2: source_idx=1, (0xCD << 4) | (0xAB >> 4) = 0xD0 | 0x0A = 0xDA
1476        assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 2, 1, 4), 0xDA);
1477
1478        #[cfg(feature = "nightly")]
1479        {
1480            const CMP_SHIFTED_EQ: Ordering = arr_cmp_shifted(&[0u8, 1, 0, 0], &[1u8, 0, 0, 0], 8);
1481            const CMP_SHIFTED_GT: Ordering = arr_cmp_shifted(&[0u8, 2, 0, 0], &[1u8, 0, 0, 0], 8);
1482            assert_eq!(CMP_SHIFTED_EQ, Ordering::Equal);
1483            assert_eq!(CMP_SHIFTED_GT, Ordering::Greater);
1484        }
1485    }
1486
1487    #[test]
1488    fn test_core_convert_u8() {
1489        let f = Bn::<u8, 1>::from(1u8);
1490        assert_eq!(f.array, [1]);
1491        let f = Bn::<u8, 2>::from(1u8);
1492        assert_eq!(f.array, [1, 0]);
1493
1494        let f = Bn::<u16, 1>::from(1u8);
1495        assert_eq!(f.array, [1]);
1496        let f = Bn::<u16, 2>::from(1u8);
1497        assert_eq!(f.array, [1, 0]);
1498
1499        #[cfg(feature = "nightly")]
1500        {
1501            const F1: Bn<u8, 2> = Bn::<u8, 2>::from(42u8);
1502            assert_eq!(F1.array, [42, 0]);
1503        }
1504    }
1505
1506    #[test]
1507    fn test_core_convert_u16() {
1508        let f = Bn::<u8, 1>::from(1u16);
1509        assert_eq!(f.array, [1]);
1510        let f = Bn::<u8, 2>::from(1u16);
1511        assert_eq!(f.array, [1, 0]);
1512
1513        let f = Bn::<u8, 1>::from(256u16);
1514        assert_eq!(f.array, [0]);
1515        let f = Bn::<u8, 2>::from(257u16);
1516        assert_eq!(f.array, [1, 1]);
1517        let f = Bn::<u8, 2>::from(65535u16);
1518        assert_eq!(f.array, [255, 255]);
1519
1520        let f = Bn::<u16, 1>::from(1u16);
1521        assert_eq!(f.array, [1]);
1522        let f = Bn::<u16, 2>::from(1u16);
1523        assert_eq!(f.array, [1, 0]);
1524
1525        let f = Bn::<u16, 1>::from(65535u16);
1526        assert_eq!(f.array, [65535]);
1527
1528        #[cfg(feature = "nightly")]
1529        {
1530            const F1: Bn<u8, 2> = Bn::<u8, 2>::from(0x0102u16);
1531            assert_eq!(F1.array, [0x02, 0x01]);
1532        }
1533    }
1534
1535    #[test]
1536    fn test_core_convert_u32() {
1537        let f = Bn::<u8, 1>::from(1u32);
1538        assert_eq!(f.array, [1]);
1539        let f = Bn::<u8, 1>::from(256u32);
1540        assert_eq!(f.array, [0]);
1541
1542        let f = Bn::<u8, 2>::from(1u32);
1543        assert_eq!(f.array, [1, 0]);
1544        let f = Bn::<u8, 2>::from(257u32);
1545        assert_eq!(f.array, [1, 1]);
1546        let f = Bn::<u8, 2>::from(65535u32);
1547        assert_eq!(f.array, [255, 255]);
1548
1549        let f = Bn::<u8, 4>::from(1u32);
1550        assert_eq!(f.array, [1, 0, 0, 0]);
1551        let f = Bn::<u8, 4>::from(257u32);
1552        assert_eq!(f.array, [1, 1, 0, 0]);
1553        let f = Bn::<u8, 4>::from(u32::max_value());
1554        assert_eq!(f.array, [255, 255, 255, 255]);
1555
1556        let f = Bn::<u8, 1>::from(1u32);
1557        assert_eq!(f.array, [1]);
1558        let f = Bn::<u8, 1>::from(256u32);
1559        assert_eq!(f.array, [0]);
1560
1561        let f = Bn::<u16, 2>::from(65537u32);
1562        assert_eq!(f.array, [1, 1]);
1563
1564        let f = Bn::<u32, 1>::from(1u32);
1565        assert_eq!(f.array, [1]);
1566        let f = Bn::<u32, 2>::from(1u32);
1567        assert_eq!(f.array, [1, 0]);
1568
1569        let f = Bn::<u32, 1>::from(65537u32);
1570        assert_eq!(f.array, [65537]);
1571
1572        let f = Bn::<u32, 1>::from(u32::max_value());
1573        assert_eq!(f.array, [4294967295]);
1574
1575        #[cfg(feature = "nightly")]
1576        {
1577            const F1: Bn<u8, 4> = Bn::<u8, 4>::from(0x01020304u32);
1578            assert_eq!(F1.array, [0x04, 0x03, 0x02, 0x01]);
1579        }
1580    }
1581
1582    #[test]
1583    fn test_core_convert_u64() {
1584        let f = Bn::<u8, 8>::from(0x0102030405060708u64);
1585        assert_eq!(f.array, [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
1586
1587        let f = Bn::<u16, 4>::from(0x0102030405060708u64);
1588        assert_eq!(f.array, [0x0708, 0x0506, 0x0304, 0x0102]);
1589
1590        let f = Bn::<u32, 2>::from(0x0102030405060708u64);
1591        assert_eq!(f.array, [0x05060708, 0x01020304]);
1592
1593        let f = Bn::<u64, 1>::from(0x0102030405060708u64);
1594        assert_eq!(f.array, [0x0102030405060708]);
1595
1596        #[cfg(feature = "nightly")]
1597        {
1598            const F1: Bn<u8, 8> = Bn::<u8, 8>::from(0x0102030405060708u64);
1599            assert_eq!(F1.array, [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
1600        }
1601    }
1602
1603    #[test]
1604    fn testsimple() {
1605        assert_eq!(Bn::<u8, 8>::new(), Bn::<u8, 8>::new());
1606
1607        assert_eq!(Bn::<u8, 8>::from_u8(3).unwrap().to_u32(), Some(3));
1608        assert_eq!(Bn::<u16, 4>::from_u8(3).unwrap().to_u32(), Some(3));
1609        assert_eq!(Bn::<u32, 2>::from_u8(3).unwrap().to_u32(), Some(3));
1610        assert_eq!(Bn::<u32, 2>::from_u64(3).unwrap().to_u32(), Some(3));
1611        assert_eq!(Bn::<u8, 8>::from_u64(255).unwrap().to_u32(), Some(255));
1612        assert_eq!(Bn::<u8, 8>::from_u64(256).unwrap().to_u32(), Some(256));
1613        assert_eq!(Bn::<u8, 8>::from_u64(65536).unwrap().to_u32(), Some(65536));
1614    }
1615    #[test]
1616    fn testfrom() {
1617        let mut n1 = Bn::<u8, 8>::new();
1618        n1.array[0] = 1;
1619        assert_eq!(Some(1), n1.to_u32());
1620        n1.array[1] = 1;
1621        assert_eq!(Some(257), n1.to_u32());
1622
1623        let mut n2 = Bn::<u16, 8>::new();
1624        n2.array[0] = 0xffff;
1625        assert_eq!(Some(65535), n2.to_u32());
1626        n2.array[0] = 0x0;
1627        n2.array[2] = 0x1;
1628        // Overflow
1629        assert_eq!(None, n2.to_u32());
1630        assert_eq!(Some(0x100000000), n2.to_u64());
1631    }
1632
1633    #[test]
1634    fn test_from_str_bitlengths() {
1635        let test_s64 = "81906f5e4d3c2c01";
1636        let test_u64: u64 = 0x81906f5e4d3c2c01;
1637        let bb = Bn8::from_str_radix(test_s64, 16).unwrap();
1638        let cc = Bn8::from_u64(test_u64).unwrap();
1639        assert_eq!(cc.array, [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81]);
1640        assert_eq!(bb.array, [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81]);
1641        let dd = Bn16::from_u64(test_u64).unwrap();
1642        let ff = Bn16::from_str_radix(test_s64, 16).unwrap();
1643        assert_eq!(dd.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190]);
1644        assert_eq!(ff.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190]);
1645        let ee = Bn32::from_u64(test_u64).unwrap();
1646        let gg = Bn32::from_str_radix(test_s64, 16).unwrap();
1647        assert_eq!(ee.array, [0x4d3c2c01, 0x81906f5e]);
1648        assert_eq!(gg.array, [0x4d3c2c01, 0x81906f5e]);
1649    }
1650
1651    #[test]
1652    fn test_from_str_stringlengths() {
1653        let ab = Bn::<u8, 9>::from_str_radix("2281906f5e4d3c2c01", 16).unwrap();
1654        assert_eq!(
1655            ab.array,
1656            [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81, 0x22]
1657        );
1658        assert_eq!(
1659            [0x2c01, 0x4d3c, 0x6f5e, 0],
1660            Bn::<u16, 4>::from_str_radix("6f5e4d3c2c01", 16)
1661                .unwrap()
1662                .array
1663        );
1664        assert_eq!(
1665            [0x2c01, 0x4d3c, 0x6f5e, 0x190],
1666            Bn::<u16, 4>::from_str_radix("1906f5e4d3c2c01", 16)
1667                .unwrap()
1668                .array
1669        );
1670        assert_eq!(
1671            Err(make_overflow_err()),
1672            Bn::<u16, 4>::from_str_radix("f81906f5e4d3c2c01", 16)
1673        );
1674        assert_eq!(
1675            Err(make_overflow_err()),
1676            Bn::<u16, 4>::from_str_radix("af81906f5e4d3c2c01", 16)
1677        );
1678        assert_eq!(
1679            Err(make_overflow_err()),
1680            Bn::<u16, 4>::from_str_radix("baaf81906f5e4d3c2c01", 16)
1681        );
1682        let ac = Bn::<u16, 5>::from_str_radix("baaf81906f5e4d3c2c01", 16).unwrap();
1683        assert_eq!(ac.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190, 0xbaaf]);
1684    }
1685
1686    #[test]
1687    fn test_resize() {
1688        type TestInt1 = FixedUInt<u32, 1>;
1689        type TestInt2 = FixedUInt<u32, 2>;
1690
1691        let a = TestInt1::from(u32::MAX);
1692        let b: TestInt2 = a.resize();
1693        assert_eq!(b, TestInt2::from([u32::MAX, 0]));
1694
1695        let a = TestInt2::from([u32::MAX, u32::MAX]);
1696        let b: TestInt1 = a.resize();
1697        assert_eq!(b, TestInt1::from(u32::MAX));
1698    }
1699
1700    #[test]
1701    fn test_bit_length() {
1702        assert_eq!(0, Bn8::from_u8(0).unwrap().bit_length());
1703        assert_eq!(1, Bn8::from_u8(1).unwrap().bit_length());
1704        assert_eq!(2, Bn8::from_u8(2).unwrap().bit_length());
1705        assert_eq!(2, Bn8::from_u8(3).unwrap().bit_length());
1706        assert_eq!(7, Bn8::from_u8(0x70).unwrap().bit_length());
1707        assert_eq!(8, Bn8::from_u8(0xF0).unwrap().bit_length());
1708        assert_eq!(9, Bn8::from_u16(0x1F0).unwrap().bit_length());
1709
1710        assert_eq!(20, Bn8::from_u64(990223).unwrap().bit_length());
1711        assert_eq!(32, Bn8::from_u64(0xefffffff).unwrap().bit_length());
1712        assert_eq!(32, Bn8::from_u64(0x8fffffff).unwrap().bit_length());
1713        assert_eq!(31, Bn8::from_u64(0x7fffffff).unwrap().bit_length());
1714        assert_eq!(34, Bn8::from_u64(0x3ffffffff).unwrap().bit_length());
1715
1716        assert_eq!(0, Bn32::from_u8(0).unwrap().bit_length());
1717        assert_eq!(1, Bn32::from_u8(1).unwrap().bit_length());
1718        assert_eq!(2, Bn32::from_u8(2).unwrap().bit_length());
1719        assert_eq!(2, Bn32::from_u8(3).unwrap().bit_length());
1720        assert_eq!(7, Bn32::from_u8(0x70).unwrap().bit_length());
1721        assert_eq!(8, Bn32::from_u8(0xF0).unwrap().bit_length());
1722        assert_eq!(9, Bn32::from_u16(0x1F0).unwrap().bit_length());
1723
1724        assert_eq!(20, Bn32::from_u64(990223).unwrap().bit_length());
1725        assert_eq!(32, Bn32::from_u64(0xefffffff).unwrap().bit_length());
1726        assert_eq!(32, Bn32::from_u64(0x8fffffff).unwrap().bit_length());
1727        assert_eq!(31, Bn32::from_u64(0x7fffffff).unwrap().bit_length());
1728        assert_eq!(34, Bn32::from_u64(0x3ffffffff).unwrap().bit_length());
1729    }
1730
1731    #[test]
1732    fn test_bit_length_1000() {
1733        // Test bit_length with value 1000
1734        let value = Bn32::from_u16(1000).unwrap();
1735
1736        // 1000 in binary is 1111101000, which has 10 bits
1737        // Let's verify the implementation is working correctly
1738        assert_eq!(value.to_u32().unwrap(), 1000);
1739        assert_eq!(value.bit_length(), 10);
1740
1741        // Test some edge cases around 1000
1742        assert_eq!(Bn32::from_u16(512).unwrap().bit_length(), 10); // 2^9 = 512
1743        assert_eq!(Bn32::from_u16(1023).unwrap().bit_length(), 10); // 2^10 - 1 = 1023
1744        assert_eq!(Bn32::from_u16(1024).unwrap().bit_length(), 11); // 2^10 = 1024
1745
1746        // Test with different word sizes to see if this makes a difference
1747        assert_eq!(Bn8::from_u16(1000).unwrap().bit_length(), 10);
1748        assert_eq!(Bn16::from_u16(1000).unwrap().bit_length(), 10);
1749
1750        // Test with different initialization methods
1751        let value_from_str = Bn32::from_str_radix("1000", 10).unwrap();
1752        assert_eq!(value_from_str.bit_length(), 10);
1753
1754        // This is the problematic case - let's debug it
1755        let value_from_bytes = Bn32::from_le_bytes(&1000u16.to_le_bytes());
1756        // Let's see what the actual value is
1757        assert_eq!(
1758            value_from_bytes.to_u32().unwrap_or(0),
1759            1000,
1760            "from_le_bytes didn't create the correct value"
1761        );
1762        assert_eq!(value_from_bytes.bit_length(), 10);
1763    }
1764    #[test]
1765    fn test_cmp() {
1766        let f0 = <Bn8 as Zero>::zero();
1767        let f1 = <Bn8 as Zero>::zero();
1768        let f2 = <Bn8 as One>::one();
1769        assert_eq!(f0, f1);
1770        assert!(f2 > f0);
1771        assert!(f0 < f2);
1772        let f3 = Bn32::from_u64(990223).unwrap();
1773        assert_eq!(f3, Bn32::from_u64(990223).unwrap());
1774        let f4 = Bn32::from_u64(990224).unwrap();
1775        assert!(f4 > Bn32::from_u64(990223).unwrap());
1776
1777        let f3 = Bn8::from_u64(990223).unwrap();
1778        assert_eq!(f3, Bn8::from_u64(990223).unwrap());
1779        let f4 = Bn8::from_u64(990224).unwrap();
1780        assert!(f4 > Bn8::from_u64(990223).unwrap());
1781
1782        #[cfg(feature = "nightly")]
1783        {
1784            use core::cmp::Ordering;
1785
1786            const A: FixedUInt<u8, 2> = FixedUInt { array: [10, 0] };
1787            const B: FixedUInt<u8, 2> = FixedUInt { array: [20, 0] };
1788            const C: FixedUInt<u8, 2> = FixedUInt { array: [10, 0] };
1789
1790            const CMP_LT: Ordering = A.cmp(&B);
1791            const CMP_GT: Ordering = B.cmp(&A);
1792            const CMP_EQ: Ordering = A.cmp(&C);
1793            const EQ_TRUE: bool = A.eq(&C);
1794            const EQ_FALSE: bool = A.eq(&B);
1795
1796            assert_eq!(CMP_LT, Ordering::Less);
1797            assert_eq!(CMP_GT, Ordering::Greater);
1798            assert_eq!(CMP_EQ, Ordering::Equal);
1799            assert!(EQ_TRUE);
1800            assert!(!EQ_FALSE);
1801        }
1802    }
1803
1804    #[test]
1805    fn test_default() {
1806        let d: Bn8 = Default::default();
1807        assert!(Zero::is_zero(&d));
1808
1809        #[cfg(feature = "nightly")]
1810        {
1811            const D: FixedUInt<u8, 2> = <FixedUInt<u8, 2> as Default>::default();
1812            assert!(Zero::is_zero(&D));
1813        }
1814    }
1815
1816    #[test]
1817    fn test_clone() {
1818        let a: Bn8 = 42u8.into();
1819        let b = a.clone();
1820        assert_eq!(a, b);
1821
1822        #[cfg(feature = "nightly")]
1823        {
1824            const A: FixedUInt<u8, 2> = FixedUInt { array: [42, 0] };
1825            const B: FixedUInt<u8, 2> = A.clone();
1826            assert_eq!(A.array, B.array);
1827        }
1828    }
1829
1830    #[test]
1831    fn test_le_be_bytes() {
1832        let le_bytes = [1, 2, 3, 4];
1833        let be_bytes = [4, 3, 2, 1];
1834        let u8_ver = FixedUInt::<u8, 4>::from_le_bytes(&le_bytes);
1835        let u16_ver = FixedUInt::<u16, 2>::from_le_bytes(&le_bytes);
1836        let u32_ver = FixedUInt::<u32, 1>::from_le_bytes(&le_bytes);
1837        let u8_ver_be = FixedUInt::<u8, 4>::from_be_bytes(&be_bytes);
1838        let u16_ver_be = FixedUInt::<u16, 2>::from_be_bytes(&be_bytes);
1839        let u32_ver_be = FixedUInt::<u32, 1>::from_be_bytes(&be_bytes);
1840
1841        assert_eq!(u8_ver.array, [1, 2, 3, 4]);
1842        assert_eq!(u16_ver.array, [0x0201, 0x0403]);
1843        assert_eq!(u32_ver.array, [0x04030201]);
1844        assert_eq!(u8_ver_be.array, [1, 2, 3, 4]);
1845        assert_eq!(u16_ver_be.array, [0x0201, 0x0403]);
1846        assert_eq!(u32_ver_be.array, [0x04030201]);
1847
1848        let mut output_buffer = [0u8; 16];
1849        assert_eq!(u8_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
1850        assert_eq!(u8_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
1851        assert_eq!(u16_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
1852        assert_eq!(u16_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
1853        assert_eq!(u32_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
1854        assert_eq!(u32_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
1855    }
1856
1857    // Test suite for division implementation
1858    #[test]
1859    fn test_div_small() {
1860        type TestInt = FixedUInt<u8, 2>;
1861
1862        // Test small values
1863        let test_cases = [
1864            (20u16, 3u16, 6u16),        // 20 / 3 = 6
1865            (100u16, 7u16, 14u16),      // 100 / 7 = 14
1866            (255u16, 5u16, 51u16),      // 255 / 5 = 51
1867            (65535u16, 256u16, 255u16), // max u16 / 256 = 255
1868        ];
1869
1870        for (dividend_val, divisor_val, expected) in test_cases {
1871            let dividend = TestInt::from(dividend_val);
1872            let divisor = TestInt::from(divisor_val);
1873            let expected_result = TestInt::from(expected);
1874
1875            assert_eq!(
1876                dividend / divisor,
1877                expected_result,
1878                "Division failed for {} / {} = {}",
1879                dividend_val,
1880                divisor_val,
1881                expected
1882            );
1883        }
1884    }
1885
1886    #[test]
1887    fn test_div_edge_cases() {
1888        type TestInt = FixedUInt<u16, 2>;
1889
1890        // Division by 1
1891        let dividend = TestInt::from(1000u16);
1892        let divisor = TestInt::from(1u16);
1893        assert_eq!(dividend / divisor, TestInt::from(1000u16));
1894
1895        // Equal values
1896        let dividend = TestInt::from(42u16);
1897        let divisor = TestInt::from(42u16);
1898        assert_eq!(dividend / divisor, TestInt::from(1u16));
1899
1900        // Dividend < divisor
1901        let dividend = TestInt::from(5u16);
1902        let divisor = TestInt::from(10u16);
1903        assert_eq!(dividend / divisor, TestInt::from(0u16));
1904
1905        // Powers of 2
1906        let dividend = TestInt::from(1024u16);
1907        let divisor = TestInt::from(4u16);
1908        assert_eq!(dividend / divisor, TestInt::from(256u16));
1909    }
1910
1911    #[test]
1912    fn test_helper_methods() {
1913        type TestInt = FixedUInt<u8, 2>;
1914
1915        // Test const_set_bit
1916        let mut val = <TestInt as Zero>::zero();
1917        const_set_bit(&mut val.array, 0);
1918        assert_eq!(val, TestInt::from(1u8));
1919
1920        const_set_bit(&mut val.array, 8);
1921        assert_eq!(val, TestInt::from(257u16)); // bit 0 + bit 8 = 1 + 256 = 257
1922
1923        // Test const_cmp_shifted
1924        let a = TestInt::from(8u8); // 1000 in binary
1925        let b = TestInt::from(1u8); // 0001 in binary
1926
1927        // b << 3 = 8, so a == (b << 3)
1928        assert_eq!(
1929            const_cmp_shifted(&a.array, &b.array, 3),
1930            core::cmp::Ordering::Equal
1931        );
1932
1933        // a > (b << 2) because b << 2 = 4
1934        assert_eq!(
1935            const_cmp_shifted(&a.array, &b.array, 2),
1936            core::cmp::Ordering::Greater
1937        );
1938
1939        // a < (b << 4) because b << 4 = 16
1940        assert_eq!(
1941            const_cmp_shifted(&a.array, &b.array, 4),
1942            core::cmp::Ordering::Less
1943        );
1944
1945        // Test const_sub_shifted
1946        let mut val = TestInt::from(10u8);
1947        let one = TestInt::from(1u8);
1948        const_sub_shifted(&mut val.array, &one.array, 2); // subtract 1 << 2 = 4
1949        assert_eq!(val, TestInt::from(6u8)); // 10 - 4 = 6
1950    }
1951
1952    #[test]
1953    fn test_shifted_operations_comprehensive() {
1954        type TestInt = FixedUInt<u32, 2>;
1955
1956        // Test cmp_shifted with various word boundary cases
1957        let a = TestInt::from(0x12345678u32);
1958        let b = TestInt::from(0x12345678u32);
1959
1960        // Equal comparison
1961        assert_eq!(
1962            const_cmp_shifted(&a.array, &b.array, 0),
1963            core::cmp::Ordering::Equal
1964        );
1965
1966        // Test shifts that cross word boundaries (assuming 32-bit words)
1967        let c = TestInt::from(0x123u32); // Small number
1968        let d = TestInt::from(0x48d159e2u32); // c << 16 + some bits
1969
1970        // c << 16 should be less than d
1971        assert_eq!(
1972            const_cmp_shifted(&d.array, &c.array, 16),
1973            core::cmp::Ordering::Greater
1974        );
1975
1976        // Test large shifts (beyond bit size, so shifted value becomes 0)
1977        let e = TestInt::from(1u32);
1978        let zero = TestInt::from(0u32);
1979        assert_eq!(
1980            const_cmp_shifted(&e.array, &zero.array, 100),
1981            core::cmp::Ordering::Greater
1982        );
1983        // When shift is beyond bit size, 1 << 100 becomes 0, so 0 == 0
1984        assert_eq!(
1985            const_cmp_shifted(&zero.array, &e.array, 100),
1986            core::cmp::Ordering::Equal
1987        );
1988
1989        // Test sub_shifted with word boundary crossing
1990        let mut val = TestInt::from(0x10000u32); // 65536
1991        let one = TestInt::from(1u32);
1992        const_sub_shifted(&mut val.array, &one.array, 15); // subtract 1 << 15 = 32768
1993        assert_eq!(val, TestInt::from(0x8000u32)); // 65536 - 32768 = 32768
1994
1995        // Test sub_shifted with multi-word operations
1996        let mut big_val = TestInt::from(0x100000000u64); // 2^32
1997        const_sub_shifted(&mut big_val.array, &one.array, 31); // subtract 1 << 31 = 2^31
1998        assert_eq!(big_val, TestInt::from(0x80000000u64)); // 2^32 - 2^31 = 2^31
1999    }
2000
2001    #[test]
2002    fn test_shifted_operations_edge_cases() {
2003        type TestInt = FixedUInt<u32, 2>;
2004
2005        // Test zero shifts
2006        let a = TestInt::from(42u32);
2007        let a2 = TestInt::from(42u32);
2008        assert_eq!(
2009            const_cmp_shifted(&a.array, &a2.array, 0),
2010            core::cmp::Ordering::Equal
2011        );
2012
2013        let mut b = TestInt::from(42u32);
2014        let ten = TestInt::from(10u32);
2015        const_sub_shifted(&mut b.array, &ten.array, 0);
2016        assert_eq!(b, TestInt::from(32u32));
2017
2018        // Test massive shifts (beyond bit size)
2019        let c = TestInt::from(123u32);
2020        let large = TestInt::from(456u32);
2021        assert_eq!(
2022            const_cmp_shifted(&c.array, &large.array, 200),
2023            core::cmp::Ordering::Greater
2024        );
2025
2026        let mut d = TestInt::from(123u32);
2027        const_sub_shifted(&mut d.array, &large.array, 200); // Should be no-op
2028        assert_eq!(d, TestInt::from(123u32));
2029
2030        // Test with zero values
2031        let zero = TestInt::from(0u32);
2032        let one = TestInt::from(1u32);
2033        assert_eq!(
2034            const_cmp_shifted(&zero.array, &zero.array, 10),
2035            core::cmp::Ordering::Equal
2036        );
2037        assert_eq!(
2038            const_cmp_shifted(&one.array, &zero.array, 10),
2039            core::cmp::Ordering::Greater
2040        );
2041    }
2042
2043    #[test]
2044    fn test_shifted_operations_equivalence() {
2045        type TestInt = FixedUInt<u32, 2>;
2046
2047        // Test that optimized operations give same results as naive shift+op
2048        let test_cases = [
2049            (0x12345u32, 0x678u32, 4),
2050            (0x1000u32, 0x10u32, 8),
2051            (0xABCDu32, 0x1u32, 16),
2052            (0x80000000u32, 0x1u32, 1),
2053        ];
2054
2055        for (a_val, b_val, shift) in test_cases {
2056            let a = TestInt::from(a_val);
2057            let b = TestInt::from(b_val);
2058
2059            // Test cmp_shifted equivalence
2060            let optimized_cmp = const_cmp_shifted(&a.array, &b.array, shift);
2061            let naive_cmp = a.cmp(&(b << shift));
2062            assert_eq!(
2063                optimized_cmp, naive_cmp,
2064                "cmp_shifted mismatch: {} vs ({} << {})",
2065                a_val, b_val, shift
2066            );
2067
2068            // Test sub_shifted equivalence (if subtraction won't underflow)
2069            if a >= (b << shift) {
2070                let mut optimized_result = a;
2071                const_sub_shifted(&mut optimized_result.array, &b.array, shift);
2072
2073                let naive_result = a - (b << shift);
2074                assert_eq!(
2075                    optimized_result, naive_result,
2076                    "sub_shifted mismatch: {} - ({} << {})",
2077                    a_val, b_val, shift
2078                );
2079            }
2080        }
2081    }
2082
2083    #[test]
2084    fn test_div_assign_in_place_optimization() {
2085        type TestInt = FixedUInt<u32, 2>;
2086
2087        // Test that div_assign uses the optimized in-place algorithm
2088        let test_cases = [
2089            (100u32, 10u32, 10u32, 0u32),     // 100 / 10 = 10 remainder 0
2090            (123u32, 7u32, 17u32, 4u32),      // 123 / 7 = 17 remainder 4
2091            (1000u32, 13u32, 76u32, 12u32),   // 1000 / 13 = 76 remainder 12
2092            (65535u32, 255u32, 257u32, 0u32), // 65535 / 255 = 257 remainder 0
2093        ];
2094
2095        for (dividend_val, divisor_val, expected_quotient, expected_remainder) in test_cases {
2096            // Test div_assign
2097            let mut dividend = TestInt::from(dividend_val);
2098            let divisor = TestInt::from(divisor_val);
2099
2100            dividend /= divisor;
2101            assert_eq!(
2102                dividend,
2103                TestInt::from(expected_quotient),
2104                "div_assign: {} / {} should be {}",
2105                dividend_val,
2106                divisor_val,
2107                expected_quotient
2108            );
2109
2110            // Test div_rem directly
2111            let dividend2 = TestInt::from(dividend_val);
2112            let (quotient, remainder) = dividend2.div_rem(&divisor);
2113            assert_eq!(
2114                quotient,
2115                TestInt::from(expected_quotient),
2116                "div_rem quotient: {} / {} should be {}",
2117                dividend_val,
2118                divisor_val,
2119                expected_quotient
2120            );
2121            assert_eq!(
2122                remainder,
2123                TestInt::from(expected_remainder),
2124                "div_rem remainder: {} % {} should be {}",
2125                dividend_val,
2126                divisor_val,
2127                expected_remainder
2128            );
2129
2130            // Verify: quotient * divisor + remainder == original dividend
2131            assert_eq!(
2132                quotient * divisor + remainder,
2133                TestInt::from(dividend_val),
2134                "Property check failed for {}",
2135                dividend_val
2136            );
2137        }
2138    }
2139
2140    #[test]
2141    fn test_div_assign_stack_efficiency() {
2142        type TestInt = FixedUInt<u32, 4>; // 16 bytes each
2143
2144        // Create test values
2145        let mut dividend = TestInt::from(0x123456789ABCDEFu64);
2146        let divisor = TestInt::from(0x12345u32);
2147        let original_dividend = dividend;
2148
2149        // Perform in-place division
2150        dividend /= divisor;
2151
2152        // Verify correctness
2153        let remainder = original_dividend % divisor;
2154        assert_eq!(dividend * divisor + remainder, original_dividend);
2155    }
2156
2157    #[test]
2158    fn test_rem_assign_optimization() {
2159        type TestInt = FixedUInt<u32, 2>;
2160
2161        let test_cases = [
2162            (100u32, 10u32, 0u32),    // 100 % 10 = 0
2163            (123u32, 7u32, 4u32),     // 123 % 7 = 4
2164            (1000u32, 13u32, 12u32),  // 1000 % 13 = 12
2165            (65535u32, 255u32, 0u32), // 65535 % 255 = 0
2166        ];
2167
2168        for (dividend_val, divisor_val, expected_remainder) in test_cases {
2169            let mut dividend = TestInt::from(dividend_val);
2170            let divisor = TestInt::from(divisor_val);
2171
2172            dividend %= divisor;
2173            assert_eq!(
2174                dividend,
2175                TestInt::from(expected_remainder),
2176                "rem_assign: {} % {} should be {}",
2177                dividend_val,
2178                divisor_val,
2179                expected_remainder
2180            );
2181        }
2182    }
2183
2184    #[test]
2185    fn test_div_with_remainder_property() {
2186        type TestInt = FixedUInt<u32, 2>;
2187
2188        // Test division with remainder property verification
2189        let test_cases = [
2190            (100u32, 10u32, 10u32),     // 100 / 10 = 10
2191            (123u32, 7u32, 17u32),      // 123 / 7 = 17
2192            (1000u32, 13u32, 76u32),    // 1000 / 13 = 76
2193            (65535u32, 255u32, 257u32), // 65535 / 255 = 257
2194        ];
2195
2196        for (dividend_val, divisor_val, expected_quotient) in test_cases {
2197            let dividend = TestInt::from(dividend_val);
2198            let divisor = TestInt::from(divisor_val);
2199
2200            // Test that div operator (which uses div_impl) works correctly
2201            let quotient = dividend / divisor;
2202            assert_eq!(
2203                quotient,
2204                TestInt::from(expected_quotient),
2205                "Division: {} / {} should be {}",
2206                dividend_val,
2207                divisor_val,
2208                expected_quotient
2209            );
2210
2211            // Verify the division property still holds
2212            let remainder = dividend % divisor;
2213            assert_eq!(
2214                quotient * divisor + remainder,
2215                dividend,
2216                "Division property check failed for {}",
2217                dividend_val
2218            );
2219        }
2220    }
2221
2222    #[test]
2223    fn test_code_simplification_benefits() {
2224        type TestInt = FixedUInt<u32, 2>;
2225
2226        // Verify division property holds
2227        let dividend = TestInt::from(12345u32);
2228        let divisor = TestInt::from(67u32);
2229        let quotient = dividend / divisor;
2230        let remainder = dividend % divisor;
2231
2232        // The division property should still hold
2233        assert_eq!(quotient * divisor + remainder, dividend);
2234    }
2235
2236    #[test]
2237    fn test_rem_assign_correctness_after_fix() {
2238        type TestInt = FixedUInt<u32, 2>;
2239
2240        // Test specific case: 17 % 5 = 2
2241        let mut a = TestInt::from(17u32);
2242        let b = TestInt::from(5u32);
2243
2244        // Historical note: an old bug caused quotient corruption during remainder calculation
2245        // Now const_div_rem properly computes both without corrupting intermediate state
2246        a %= b;
2247        assert_eq!(a, TestInt::from(2u32), "17 % 5 should be 2");
2248
2249        // Test that the original RemAssign bug would have failed this
2250        let mut test_val = TestInt::from(100u32);
2251        test_val %= TestInt::from(7u32);
2252        assert_eq!(
2253            test_val,
2254            TestInt::from(2u32),
2255            "100 % 7 should be 2 (not 14, the quotient)"
2256        );
2257    }
2258
2259    #[test]
2260    fn test_div_property_based() {
2261        type TestInt = FixedUInt<u16, 2>;
2262
2263        // Property: quotient * divisor + remainder == dividend
2264        let test_pairs = [
2265            (12345u16, 67u16),
2266            (1000u16, 13u16),
2267            (65535u16, 255u16),
2268            (5000u16, 7u16),
2269        ];
2270
2271        for (dividend_val, divisor_val) in test_pairs {
2272            let dividend = TestInt::from(dividend_val);
2273            let divisor = TestInt::from(divisor_val);
2274
2275            let quotient = dividend / divisor;
2276
2277            // Property verification: quotient * divisor + remainder == dividend
2278            let remainder = dividend - (quotient * divisor);
2279            let reconstructed = quotient * divisor + remainder;
2280
2281            assert_eq!(
2282                reconstructed,
2283                dividend,
2284                "Property failed for {} / {}: {} * {} + {} != {}",
2285                dividend_val,
2286                divisor_val,
2287                quotient.to_u32().unwrap_or(0),
2288                divisor_val,
2289                remainder.to_u32().unwrap_or(0),
2290                dividend_val
2291            );
2292
2293            // Remainder should be less than divisor
2294            assert!(
2295                remainder < divisor,
2296                "Remainder {} >= divisor {} for {} / {}",
2297                remainder.to_u32().unwrap_or(0),
2298                divisor_val,
2299                dividend_val,
2300                divisor_val
2301            );
2302        }
2303    }
2304}