Skip to main content

decimal_scaled/int/types/
mod.rs

1// SPDX-FileCopyrightText: 2026 John Moxley
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Const-generic fixed-width integers.
5//!
6//! A single `Uint<const LIMBS: usize>` / `Int<const LIMBS: usize>` pair
7//! parameterised by the number of 64-bit little-endian limbs, replacing
8//! the family of named `IntXXXX` / `UintXXXX` types. Bit width is
9//! derived (`BITS = LIMBS * 64`); every width this crate uses
10//! (256 … 16384 bits) is a clean multiple of 64, so the limb count is
11//! the natural single parameter — it sidesteps the
12//! `LIMBS = ceil(BITS / 64)` derivation that a `BITS`-parameterised type
13//! cannot express on stable Rust.
14//!
15//! Storage is `[u64; LIMBS]`, matching the limb representation the
16//! arithmetic primitives already use. Methods delegate to the
17//! width-matched limb algorithms; because `LIMBS` is a compile-time
18//! constant, the limb loops unroll and any `match LIMBS` arms const-fold
19//! per monomorphisation — no runtime dispatch.
20
21pub(crate) mod traits;
22pub(crate) mod compute_limbs;
23
24pub use traits::BigInt;
25
26use crate::int::algos::div::div_fixed::div_rem_mag_fixed;
27use crate::int::algos::div::div_rem::div_rem;
28use crate::int::algos::support::limbs::{
29    add_assign_fixed, bit_len_fixed, cmp_cross, cmp_fixed, is_zero_fixed, max_n_limbs, shl,
30    shl_fixed, shr_fixed, sub_assign_fixed,
31};
32use crate::int::algos::mul::mul_schoolbook::{mul_low_fixed, mul_schoolbook};
33use crate::int::policy::add::dispatch as add_dispatch;
34use crate::int::policy::cmp::dispatch as cmp_dispatch;
35use crate::int::policy::cube::dispatch as cube_dispatch;
36use crate::int::policy::div_rem::dispatch as div_rem_dispatch;
37use crate::int::policy::eq::dispatch as eq_dispatch;
38use crate::int::policy::icbrt::dispatch as icbrt_dispatch;
39use crate::int::policy::hypot::dispatch as hypot_dispatch;
40use crate::int::policy::isqrt::dispatch as isqrt_dispatch;
41use crate::int::policy::mul::dispatch as mul_fast;
42use crate::int::policy::neg::dispatch as neg_dispatch;
43use crate::int::policy::sum_sq::dispatch as sum_sq_dispatch;
44use crate::int::policy::pow::dispatch as pow_dispatch;
45use crate::int::policy::rem::dispatch as rem_dispatch;
46use crate::int::policy::sqr::dispatch as sqr_dispatch;
47use crate::int::policy::sub::dispatch as sub_dispatch;
48use crate::support::int_fmt::fmt_into;
49use core::cmp::Ordering;
50use core::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Rem, Shl, Shr, Sub};
51
52/// Unsigned fixed-width integer of `N` little-endian 64-bit limbs.
53#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
54pub struct Uint<const N: usize> {
55    limbs: [u64; N],
56}
57
58/// Signed (two's-complement) fixed-width integer of `N` little-endian
59/// 64-bit limbs.
60//
61// `PartialEq` / `PartialOrd` / `Ord` are NOT derived: a single generic
62// `impl<N, M>` cross-width surface (below) covers same-width comparison
63// too, so a derived same-width impl would collide. `Eq` is still derived
64// (it only needs the `PartialEq<Self>` the generic impl provides).
65#[derive(Clone, Copy, Eq, Hash, Debug)]
66pub struct Int<const N: usize> {
67    limbs: [u64; N],
68}
69
70impl<const N: usize> Uint<N> {
71    /// Number of 64-bit limbs.
72    pub const LIMBS: usize = N;
73    /// Bit width (`LIMBS * 64`). `u32` so it composes directly with the
74    /// `leading_zeros` / `count_ones` `u32` surface and matches the
75    /// historic named-type `BITS` constant.
76    pub const BITS: u32 = (N as u32) * 64;
77
78    /// Additive identity.
79    pub const ZERO: Self = Self { limbs: [0; N] };
80    /// Multiplicative identity.
81    pub const ONE: Self = {
82        let mut limbs = [0u64; N];
83        limbs[0] = 1;
84        Self { limbs }
85    };
86    /// Largest representable value (all limbs set).
87    pub const MAX: Self = Self {
88        limbs: [u64::MAX; N],
89    };
90
91    /// Constructs from raw little-endian limbs.
92    #[inline]
93    pub const fn from_limbs(limbs: [u64; N]) -> Self {
94        Self { limbs }
95    }
96
97    /// Borrows the raw little-endian limbs.
98    #[inline]
99    pub const fn as_limbs(&self) -> &[u64; N] {
100        &self.limbs
101    }
102
103    /// Wrapping addition (modulo `2^BITS`).
104    #[inline]
105    pub fn wrapping_add(mut self, rhs: Self) -> Self {
106        add_assign_fixed(&mut self.limbs, &rhs.limbs);
107        self
108    }
109
110    /// Wrapping subtraction (modulo `2^BITS`).
111    #[inline]
112    pub fn wrapping_sub(mut self, rhs: Self) -> Self {
113        sub_assign_fixed(&mut self.limbs, &rhs.limbs);
114        self
115    }
116
117    /// Wrapping multiplication (modulo `2^BITS`).
118    ///
119    /// Schoolbook multiply truncated to the low `N` limbs. Only the
120    /// product limbs that land below `2^BITS` are kept, so no
121    /// `[u64; 2*N]` output buffer is needed — the higher partial
122    /// products are simply never written. Carries that would land at or
123    /// above limb `N` are discarded, which is exactly the modular
124    /// reduction.
125    #[inline]
126    pub fn wrapping_mul(self, rhs: Self) -> Self {
127        let mut out = [0u64; N];
128        mul_low_fixed(&self.limbs, &rhs.limbs, &mut out);
129        Self { limbs: out }
130    }
131
132    /// Wrapping square (`self²` modulo `2^BITS`). Named entry point for
133    /// the open-coded `x * x` pattern. Thin delegator DOWN to the sqr
134    /// policy (`sqr_dispatch`): half-product squaring via the
135    /// `sqr_low_fixed` kernel — each cross term is formed once and doubled,
136    /// so the limb-multiply count is `N(N+1)/2` rather than the `N²` of a
137    /// general multiply.
138    #[inline]
139    pub const fn wrapping_sqr(self) -> Self {
140        sqr_dispatch(self)
141    }
142
143    /// Wrapping cube (`self³` modulo `2^BITS`). Named entry point for the
144    /// open-coded `x * x * x` pattern. Thin delegator DOWN to the cube
145    /// policy (`cube_dispatch`): `sqr` then one multiply via the const
146    /// kernels — no cheaper form exists below two multiplies.
147    #[inline]
148    pub const fn wrapping_cube(self) -> Self {
149        cube_dispatch(self)
150    }
151
152    /// Checked addition: `None` on overflow past `2^BITS`.
153    #[inline]
154    pub fn checked_add(mut self, rhs: Self) -> Option<Self> {
155        let carry = add_assign_fixed(&mut self.limbs, &rhs.limbs);
156        if carry { None } else { Some(self) }
157    }
158
159    /// Checked subtraction: `None` if the result would be negative.
160    #[inline]
161    pub fn checked_sub(mut self, rhs: Self) -> Option<Self> {
162        let borrow = sub_assign_fixed(&mut self.limbs, &rhs.limbs);
163        if borrow { None } else { Some(self) }
164    }
165
166    /// Checked multiplication: `None` if the true product does not fit
167    /// `2^BITS`.
168    #[inline]
169    pub fn checked_mul(self, rhs: Self) -> Option<Self> {
170        let (lhs_limbs, rhs_limbs) = (&self.limbs, &rhs.limbs);
171        let mut out = [0u64; N];
172        let mut overflow = false;
173        let mut i = 0;
174        while i < N {
175            let lhs_limb = lhs_limbs[i];
176            if lhs_limb != 0 {
177                let mut carry: u64 = 0;
178                let mut j = 0;
179                while j < N {
180                    let prod = (lhs_limb as u128) * (rhs_limbs[j] as u128);
181                    if i + j < N {
182                        let column = prod + (out[i + j] as u128) + (carry as u128);
183                        out[i + j] = column as u64;
184                        carry = (column >> 64) as u64;
185                    } else if prod != 0 || carry != 0 {
186                        // Any product or carry landing at/above limb `N`
187                        // means the true product exceeds the width.
188                        overflow = true;
189                        carry = 0;
190                    }
191                    j += 1;
192                }
193                if carry != 0 {
194                    // Row carry would spill into limb `i + N >= N`.
195                    overflow = true;
196                }
197            }
198            i += 1;
199        }
200        if overflow {
201            None
202        } else {
203            Some(Self { limbs: out })
204        }
205    }
206
207    /// Wrapping division. Panics on a zero divisor, matching the
208    /// behaviour of the primitive unsigned integer types.
209    #[inline]
210    pub fn wrapping_div(self, rhs: Self) -> Self {
211        assert!(!rhs.is_zero(), "attempt to divide by zero");
212        let mut quot = [0u64; N];
213        let mut rem = [0u64; N];
214        div_rem(&self.limbs, &rhs.limbs, &mut quot, &mut rem);
215        Self { limbs: quot }
216    }
217
218    /// Wrapping remainder. Panics on a zero divisor, matching the
219    /// behaviour of the primitive unsigned integer types.
220    #[inline]
221    pub fn wrapping_rem(self, rhs: Self) -> Self {
222        assert!(
223            !rhs.is_zero(),
224            "attempt to calculate the remainder with a divisor of zero"
225        );
226        let mut quot = [0u64; N];
227        let mut rem = [0u64; N];
228        div_rem(&self.limbs, &rhs.limbs, &mut quot, &mut rem);
229        Self { limbs: rem }
230    }
231
232    /// Bitwise AND.
233    // Operator-named inherent kernels (bitand/bitor/bitxor/not/shl/shr):
234    // deliberately NOT the std ops traits — const-usable, and `shl`/`shr` take
235    // a `u32` shift rather than the `Shl<Rhs>`/`Shr<Rhs>` shape (the std ops
236    // are implemented separately). Allow the look-alike-trait-method lint.
237    #[allow(clippy::should_implement_trait)]
238    #[inline]
239    pub fn bitand(self, rhs: Self) -> Self {
240        let mut out = [0u64; N];
241        let mut i = 0;
242        while i < N {
243            out[i] = self.limbs[i] & rhs.limbs[i];
244            i += 1;
245        }
246        Self { limbs: out }
247    }
248
249    /// Bitwise OR.
250    #[allow(clippy::should_implement_trait)]
251    #[inline]
252    pub fn bitor(self, rhs: Self) -> Self {
253        let mut out = [0u64; N];
254        let mut i = 0;
255        while i < N {
256            out[i] = self.limbs[i] | rhs.limbs[i];
257            i += 1;
258        }
259        Self { limbs: out }
260    }
261
262    /// Bitwise XOR.
263    #[allow(clippy::should_implement_trait)]
264    #[inline]
265    pub fn bitxor(self, rhs: Self) -> Self {
266        let mut out = [0u64; N];
267        let mut i = 0;
268        while i < N {
269            out[i] = self.limbs[i] ^ rhs.limbs[i];
270            i += 1;
271        }
272        Self { limbs: out }
273    }
274
275    /// Bitwise NOT (ones' complement).
276    #[allow(clippy::should_implement_trait)]
277    #[inline]
278    pub fn not(self) -> Self {
279        let mut out = [0u64; N];
280        let mut i = 0;
281        while i < N {
282            out[i] = !self.limbs[i];
283            i += 1;
284        }
285        Self { limbs: out }
286    }
287
288    /// Logical left shift by `shift` bits (modulo `2^BITS`).
289    #[allow(clippy::should_implement_trait)]
290    #[inline]
291    pub fn shl(self, shift: u32) -> Self {
292        let mut out = [0u64; N];
293        shl_fixed(&self.limbs, shift, &mut out);
294        Self { limbs: out }
295    }
296
297    /// Logical right shift by `shift` bits.
298    #[allow(clippy::should_implement_trait)]
299    #[inline]
300    pub fn shr(self, shift: u32) -> Self {
301        let mut out = [0u64; N];
302        shr_fixed(&self.limbs, shift, &mut out);
303        Self { limbs: out }
304    }
305
306    /// `true` when every limb is zero.
307    #[inline]
308    pub fn is_zero(&self) -> bool {
309        is_zero_fixed(&self.limbs)
310    }
311
312    /// Bit length (significant bits): `0` for zero, else
313    /// `floor(log2(self)) + 1`, equivalently `BITS - leading_zeros`.
314    ///
315    /// For an unsigned value there is no sign, so magnitude and stored
316    /// representation coincide — `bit_length` and the `uN`-contract
317    /// bit-count methods below agree by construction.
318    #[inline]
319    pub const fn bit_length(&self) -> u32 {
320        bit_len_fixed(&self.limbs)
321    }
322
323    /// Number of leading zero bits in the `BITS`-wide representation,
324    /// matching the primitive `uN::leading_zeros` contract:
325    /// `BITS - bit_length`, and `BITS` for zero.
326    #[inline]
327    pub const fn leading_zeros(&self) -> u32 {
328        Self::BITS - self.bit_length()
329    }
330
331    /// `true` when the value equals one.
332    #[inline]
333    pub fn is_one(&self) -> bool {
334        if N == 0 || self.limbs[0] != 1 {
335            return false;
336        }
337        let mut i = 1;
338        while i < N {
339            if self.limbs[i] != 0 {
340                return false;
341            }
342            i += 1;
343        }
344        true
345    }
346
347    /// Wrapping exponentiation by squaring (`self^exp` modulo `2^BITS`).
348    /// `self^0 == 1`. Thin delegator DOWN to the pow policy
349    /// (`pow_dispatch`): binary square-and-multiply over the const
350    /// kernels; optimal for the small fixed exponents the root iteration
351    /// needs (`k-1`, `k`).
352    #[inline]
353    pub const fn wrapping_pow(self, exp: u32) -> Self {
354        pow_dispatch(self, exp)
355    }
356
357    /// Exponentiation by squaring, returning `None` if the true power
358    /// overflows `2^BITS`. `self^0 == 1`.
359    #[inline]
360    pub fn checked_pow(self, mut exp: u32) -> Option<Self> {
361        let mut acc = Self::ONE;
362        let mut base = self;
363        loop {
364            if exp & 1 == 1 {
365                acc = acc.checked_mul(base)?;
366            }
367            exp >>= 1;
368            if exp == 0 {
369                break;
370            }
371            base = base.checked_mul(base)?;
372        }
373        Some(acc)
374    }
375
376    /// Exponentiation by squaring. Routes through the pow policy
377    /// (`pow_dispatch`): binary square-and-multiply at every `N`.
378    /// Result is `self^exp` modulo `2^BITS`; `self^0 == 1`.
379    #[inline]
380    pub fn pow(self, exp: u32) -> Self {
381        pow_dispatch(self, exp)
382    }
383
384    /// Integer square root: the largest `r` with `r² <= self`.
385    /// Routes through the isqrt policy (`isqrt_dispatch`):
386    /// `N ∈ {1, 2}` takes the hardware native path; `N >= 3` takes
387    /// the Newton limb kernel.
388    #[inline]
389    pub fn isqrt(self) -> Self {
390        isqrt_dispatch(self)
391    }
392
393    /// Integer square: `self²` modulo `2^BITS`. Routes through the sqr
394    /// policy (`sqr_dispatch`): half-product squaring kernel at every `N`.
395    #[inline]
396    pub fn sqr(self) -> Self {
397        sqr_dispatch(self)
398    }
399
400    /// Integer cube: `self³` modulo `2^BITS`. Routes through the cube
401    /// policy (`cube_dispatch`): sqr-then-multiply at every `N`.
402    #[inline]
403    pub fn cube(self) -> Self {
404        cube_dispatch(self)
405    }
406
407    /// Integer cube root: `floor(self^(1/3))`. Routes through the icbrt
408    /// policy (`icbrt_dispatch`):
409    /// `N ∈ {1, 2}` takes the narrow path; `N >= 3` takes the Newton
410    /// limb kernel with an `f64::cbrt` seed.
411    #[inline]
412    pub fn icbrt(self) -> Self {
413        icbrt_dispatch(self)
414    }
415
416    /// Integer `k`th root: returns `(root, exact)` where
417    /// `root = floor(self^(1/k))` and `exact` is `true` iff
418    /// `root^k == self`. `k` must be `>= 1`.
419    ///
420    /// Brent–Zimmermann RootInt (Modern Computer Arithmetic §1.5.2): the
421    /// integer projection of Newton's iteration
422    /// `u = ((k-1)·s + m / s^(k-1)) / k`, started from an upper bound on
423    /// the root and run until the monotone-decreasing sequence first
424    /// fails to decrease (`u >= s`), at which point `s` is the floor
425    /// root. The seed is the no_std-safe bit-length bound
426    /// `2^ceil(bit_length / k)` — a clean upper bound since
427    /// `(2^ceil(L/k))^k >= 2^L > m`. `k == 2` reuses the dedicated
428    /// [`Self::isqrt`]; `k == 3` is the cube root.
429    pub fn root_int(self, k: u32) -> (Self, bool) {
430        debug_assert!(k >= 1, "root_int requires k >= 1");
431        // Degenerate / trivial roots.
432        if k == 1 {
433            return (self, true);
434        }
435        if self.is_zero() {
436            return (Self::ZERO, true);
437        }
438        if self.is_one() {
439            return (Self::ONE, true);
440        }
441        if k == 2 {
442            let root = self.isqrt();
443            return (root, root.wrapping_sqr() == self);
444        }
445
446        // Seed: 2^ceil(bit_length / k) is an upper bound on the root.
447        let len = self.bit_length();
448        let seed_shift = len.div_ceil(k);
449        // ceil(len/k) <= len for k >= 2, so the seed fits the width.
450        let mut s = Self::ONE.shl(seed_shift);
451
452        // Newton: s decreases monotonically while above the root.
453        loop {
454            // t = (k-1)*s + m / s^(k-1)
455            let pow_km1 = s.wrapping_pow(k - 1);
456            // pow_km1 is non-zero (s >= 1), so the divide is defined.
457            let quot = self.wrapping_div(pow_km1);
458            let mut t = Self::ZERO;
459            let mut c = 0;
460            while c < k - 1 {
461                t = t.wrapping_add(s);
462                c += 1;
463            }
464            t = t.wrapping_add(quot);
465            // u = t / k
466            let u = t.wrapping_div(Self::from_u64(k as u64));
467            if u >= s {
468                break;
469            }
470            s = u;
471        }
472
473        let exact = s.checked_pow(k).is_some_and(|power| power == self);
474        (s, exact)
475    }
476
477    /// Constructs from a `u64`, zero-extending into the high limbs.
478    #[inline]
479    pub(crate) fn from_u64(value: u64) -> Self {
480        let mut limbs = [0u64; N];
481        if N > 0 {
482            limbs[0] = value;
483        }
484        Self { limbs }
485    }
486
487    /// Builds from an unsigned 128-bit value, zero-extending the upper
488    /// limbs. **Truncating** for `Uint<1>` (the high 64 bits of `value` are
489    /// discarded); use the checked `TryFrom` conversion when `value` may
490    /// not fit.
491    #[inline]
492    pub(crate) const fn from_u128(value: u128) -> Self {
493        let mut limbs = [0u64; N];
494        if N > 0 {
495            limbs[0] = value as u64;
496        }
497        if N > 1 {
498            limbs[1] = (value >> 64) as u64;
499        }
500        Self { limbs }
501    }
502
503    /// Exact value conversion from `u128`, or `None` if `value` does not fit
504    /// `Uint<N>` (only possible for `N < 2`). For `N >= 2` every `u128` fits.
505    #[inline]
506    pub(crate) const fn try_from_u128(value: u128) -> Option<Self> {
507        if N >= 2 || value <= u64::MAX as u128 {
508            Some(Self::from_u128(value))
509        } else {
510            None
511        }
512    }
513
514    /// Reinterprets the bit pattern as the signed sibling.
515    #[inline]
516    pub const fn cast_signed(self) -> Int<N> {
517        Int::from_limbs(self.limbs)
518    }
519
520    /// Approximate `f64` value (positive; truncated toward zero on
521    /// overflow).
522    pub fn as_f64(self) -> f64 {
523        let radix: f64 = 18_446_744_073_709_551_616.0; // 2^64
524        let mut acc = 0.0f64;
525        let mut i = N;
526        while i > 0 {
527            i -= 1;
528            acc = acc * radix + self.limbs[i] as f64;
529        }
530        acc
531    }
532
533    /// Set-bit count across all limbs, matching the primitive
534    /// `uN::count_ones` contract.
535    #[inline]
536    pub const fn count_ones(self) -> u32 {
537        let mut total = 0;
538        let mut i = 0;
539        while i < N {
540            total += self.limbs[i].count_ones();
541            i += 1;
542        }
543        total
544    }
545
546    /// `true` when exactly one bit is set.
547    #[inline]
548    pub const fn is_power_of_two(self) -> bool {
549        self.count_ones() == 1
550    }
551
552    /// Smallest power of two `>= self` (`1` for zero), wrapping on
553    /// overflow.
554    pub fn next_power_of_two(self) -> Self {
555        if self.is_zero() {
556            return Self::ONE;
557        }
558        if self.is_power_of_two() {
559            return self;
560        }
561        let bits = self.bit_length();
562        let mut out = [0u64; N];
563        if (bits as usize) < N * 64 {
564            out[(bits / 64) as usize] = 1u64 << (bits % 64);
565        }
566        Self { limbs: out }
567    }
568
569    /// Parses an unsigned decimal string. Only base 10 is supported.
570    pub const fn from_str_radix(text: &str, radix: u32) -> Result<Self, ()> {
571        if radix != 10 {
572            return Err(());
573        }
574        let bytes = text.as_bytes();
575        if bytes.is_empty() {
576            return Err(());
577        }
578        let mut acc = [0u64; N];
579        let mut k = 0;
580        while k < bytes.len() {
581            let ch = bytes[k];
582            if ch < b'0' || ch > b'9' {
583                return Err(());
584            }
585            let digit = (ch - b'0') as u64;
586            let mut carry: u64 = digit;
587            let mut j = 0;
588            while j < N {
589                let product = (acc[j] as u128) * 10u128 + (carry as u128);
590                acc[j] = product as u64;
591                carry = (product >> 64) as u64;
592                j += 1;
593            }
594            k += 1;
595        }
596        Ok(Self { limbs: acc })
597    }
598}
599
600impl<const N: usize> Add for Uint<N> {
601    type Output = Self;
602    #[inline]
603    fn add(self, rhs: Self) -> Self {
604        self.wrapping_add(rhs)
605    }
606}
607
608impl<const N: usize> Sub for Uint<N> {
609    type Output = Self;
610    #[inline]
611    fn sub(self, rhs: Self) -> Self {
612        self.wrapping_sub(rhs)
613    }
614}
615
616impl<const N: usize> Mul for Uint<N> {
617    type Output = Self;
618    #[inline]
619    fn mul(self, rhs: Self) -> Self {
620        self.wrapping_mul(rhs)
621    }
622}
623
624impl<const N: usize> BitAnd for Uint<N> {
625    type Output = Self;
626    #[inline]
627    fn bitand(self, rhs: Self) -> Self {
628        Uint::bitand(self, rhs)
629    }
630}
631
632impl<const N: usize> BitOr for Uint<N> {
633    type Output = Self;
634    #[inline]
635    fn bitor(self, rhs: Self) -> Self {
636        Uint::bitor(self, rhs)
637    }
638}
639
640impl<const N: usize> BitXor for Uint<N> {
641    type Output = Self;
642    #[inline]
643    fn bitxor(self, rhs: Self) -> Self {
644        Uint::bitxor(self, rhs)
645    }
646}
647
648impl<const N: usize> Not for Uint<N> {
649    type Output = Self;
650    #[inline]
651    fn not(self) -> Self {
652        Uint::not(self)
653    }
654}
655
656impl<const N: usize> Shl<u32> for Uint<N> {
657    type Output = Self;
658    #[inline]
659    fn shl(self, shift: u32) -> Self {
660        Uint::shl(self, shift)
661    }
662}
663
664impl<const N: usize> Shr<u32> for Uint<N> {
665    type Output = Self;
666    #[inline]
667    fn shr(self, shift: u32) -> Self {
668        Uint::shr(self, shift)
669    }
670}
671
672// Truncating unsigned division / remainder via the dispatching divmod
673// (Knuth / Burnikel–Ziegler), matching the macro `$U` operators so the
674// const-generic and named unsigned types share one divide algorithm.
675
676impl<const N: usize> Div for Uint<N> {
677    type Output = Self;
678    #[inline]
679    fn div(self, rhs: Self) -> Self {
680        let mut quotient = [0u64; N];
681        let mut remainder = [0u64; N];
682        div_rem_dispatch(&self.limbs, &rhs.limbs, &mut quotient, &mut remainder);
683        Self { limbs: quotient }
684    }
685}
686
687impl<const N: usize> Rem for Uint<N> {
688    type Output = Self;
689    #[inline]
690    fn rem(self, rhs: Self) -> Self {
691        let mut quotient = [0u64; N];
692        let mut remainder = [0u64; N];
693        div_rem_dispatch(&self.limbs, &rhs.limbs, &mut quotient, &mut remainder);
694        Self { limbs: remainder }
695    }
696}
697
698impl<const N: usize> PartialOrd for Uint<N> {
699    #[inline]
700    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
701        Some(self.cmp(other))
702    }
703}
704
705impl<const N: usize> Ord for Uint<N> {
706    #[inline]
707    fn cmp(&self, other: &Self) -> Ordering {
708        match cmp_fixed(&self.limbs, &other.limbs) {
709            -1 => Ordering::Less,
710            1 => Ordering::Greater,
711            _ => Ordering::Equal,
712        }
713    }
714}
715
716impl<const N: usize> Int<N> {
717    /// Number of 64-bit limbs.
718    pub const LIMBS: usize = N;
719    /// Bit width (`LIMBS * 64`). `u32` so it composes directly with the
720    /// `leading_zeros` / `count_ones` `u32` surface and matches the
721    /// historic named-type `BITS` constant.
722    pub const BITS: u32 = (N as u32) * 64;
723
724    /// Additive identity.
725    pub const ZERO: Self = Self { limbs: [0; N] };
726    /// Multiplicative identity.
727    pub const ONE: Self = {
728        let mut limbs = [0u64; N];
729        limbs[0] = 1;
730        Self { limbs }
731    };
732    /// Most positive representable value (`2^(BITS-1) - 1`).
733    pub const MAX: Self = {
734        let mut limbs = [u64::MAX; N];
735        limbs[N - 1] = i64::MAX as u64;
736        Self { limbs }
737    };
738    /// Most negative representable value (`-2^(BITS-1)`).
739    pub const MIN: Self = {
740        let mut limbs = [0u64; N];
741        limbs[N - 1] = 1u64 << 63;
742        Self { limbs }
743    };
744
745    /// Constructs from raw little-endian two's-complement limbs.
746    #[inline]
747    pub const fn from_limbs(limbs: [u64; N]) -> Self {
748        Self { limbs }
749    }
750
751    /// Borrows the raw little-endian limbs.
752    #[inline]
753    pub const fn as_limbs(&self) -> &[u64; N] {
754        &self.limbs
755    }
756
757    /// `true` when every limb is zero.
758    #[inline]
759    pub const fn is_zero(&self) -> bool {
760        is_zero_fixed(&self.limbs)
761    }
762
763    /// `true` when the value is strictly negative (top bit set).
764    #[inline]
765    pub const fn is_negative(&self) -> bool {
766        N > 0 && (self.limbs[N - 1] >> 63) == 1
767    }
768
769    /// `true` when the value is strictly positive (non-zero and the
770    /// sign bit clear).
771    #[inline]
772    pub const fn is_positive(&self) -> bool {
773        !self.is_negative() && !self.is_zero()
774    }
775
776    /// Two's-complement wrapping negation (`!self + 1`). `MIN` negates
777    /// to itself, as with the primitive signed integers.
778    #[inline]
779    pub const fn wrapping_neg(self) -> Self {
780        neg_dispatch(self)
781    }
782
783    /// Wrapping addition (modulo `2^BITS`). Identical bit pattern to the
784    /// unsigned add — two's-complement makes signed and unsigned
785    /// addition the same operation.
786    #[inline]
787    pub const fn wrapping_add(self, rhs: Self) -> Self {
788        add_dispatch(self, rhs)
789    }
790
791    /// Wrapping subtraction (modulo `2^BITS`).
792    #[inline]
793    pub const fn wrapping_sub(self, rhs: Self) -> Self {
794        sub_dispatch(self, rhs)
795    }
796
797    /// Wrapping multiplication (modulo `2^BITS`). The low `N` limbs of a
798    /// two's-complement product are independent of the operand signs, so
799    /// this is the same truncated schoolbook the unsigned type uses.
800    #[inline]
801    pub const fn wrapping_mul(self, rhs: Self) -> Self {
802        let mut out = [0u64; N];
803        mul_low_fixed(&self.limbs, &rhs.limbs, &mut out);
804        Self { limbs: out }
805    }
806
807    /// Absolute value (wrapping: `MIN.abs() == MIN`).
808    #[inline]
809    pub const fn abs(self) -> Self {
810        if self.is_negative() {
811            self.wrapping_neg()
812        } else {
813            self
814        }
815    }
816
817    /// Sign: `-1`, `0`, or `1` as the value is negative, zero, or
818    /// positive.
819    #[inline]
820    pub fn signum(&self) -> i32 {
821        if self.is_zero() {
822            0
823        } else if self.is_negative() {
824            -1
825        } else {
826            1
827        }
828    }
829
830    /// Constructs from an `i64`, sign-extending into the high limbs.
831    #[inline]
832    pub(crate) const fn from_i64(value: i64) -> Self {
833        // Negative values fill the upper limbs with all-ones so the
834        // two's-complement representation matches at every width.
835        let fill = if value < 0 { u64::MAX } else { 0 };
836        let mut limbs = [fill; N];
837        if N > 0 {
838            limbs[0] = value as u64;
839        }
840        Self { limbs }
841    }
842
843    /// Constructs from an `i8` (always representable; sign-extends).
844    #[inline]
845    pub(crate) const fn from_i8(value: i8) -> Self {
846        Self::from_i64(value as i64)
847    }
848
849    /// Constructs from an `i16` (always representable; sign-extends).
850    #[inline]
851    pub(crate) const fn from_i16(value: i16) -> Self {
852        Self::from_i64(value as i64)
853    }
854
855    /// Constructs from an `i32` (always representable; sign-extends).
856    #[inline]
857    pub(crate) const fn from_i32(value: i32) -> Self {
858        Self::from_i64(value as i64)
859    }
860
861    /// Constructs from a `u8` (always representable; zero-extends).
862    #[inline]
863    pub(crate) const fn from_u8(value: u8) -> Self {
864        Self::from_u64_unsigned(value as u64)
865    }
866
867    /// Constructs from a `u16` (always representable; zero-extends).
868    #[inline]
869    pub(crate) const fn from_u16(value: u16) -> Self {
870        Self::from_u64_unsigned(value as u64)
871    }
872
873    /// Constructs from a `u32` (always representable; zero-extends).
874    #[inline]
875    pub(crate) const fn from_u32(value: u32) -> Self {
876        Self::from_u64_unsigned(value as u64)
877    }
878
879    /// Zero-extends an unsigned 64-bit value into limb 0. Internal helper
880    /// for the unsigned `from_*` family; the public fitting check is in
881    /// [`Self::try_from_u64`].
882    #[inline]
883    const fn from_u64_unsigned(value: u64) -> Self {
884        let mut limbs = [0u64; N];
885        if N > 0 {
886            limbs[0] = value;
887        }
888        Self { limbs }
889    }
890
891    /// Exact conversion from a `u64`, or `None` if it does not fit
892    /// `Int<N>`. Only `N == 1` (the `i64` floor) can fail, when the value
893    /// exceeds `i64::MAX`; every wider tier holds all of `u64`.
894    #[inline]
895    pub(crate) const fn try_from_u64(value: u64) -> Option<Self> {
896        if N >= 2 || value <= i64::MAX as u64 {
897            Some(Self::from_u64_unsigned(value))
898        } else {
899            None
900        }
901    }
902
903    /// Exact conversion from an `i128`, or `None` if it does not fit
904    /// `Int<N>`. Only `N == 1` (64-bit storage) can fail; `N >= 2` holds
905    /// every `i128`.
906    #[inline]
907    pub(crate) const fn try_from_i128(value: i128) -> Option<Self> {
908        let mag = value.unsigned_abs();
909        let built = Self::from_mag_limbs(&[mag as u64, (mag >> 64) as u64], value < 0);
910        if N >= 2 || built.as_i128() == value {
911            Some(built)
912        } else {
913            None
914        }
915    }
916
917    /// Exact conversion from a `u128`, or `None` if it does not fit
918    /// `Int<N>`. `N == 1` fails above `i64::MAX`; `N == 2` fails above
919    /// `i128::MAX` (the sign bit); `N >= 3` holds every `u128`.
920    #[inline]
921    pub(crate) const fn try_from_u128(value: u128) -> Option<Self> {
922        let built = Self::from_mag_limbs(&[value as u64, (value >> 64) as u64], false);
923        if N >= 3 {
924            Some(built)
925        } else if built.is_negative() {
926            // Magnitude landed in the sign bit of the N-limb storage.
927            None
928        } else if N >= 2 || built.as_i128() as u128 == value {
929            Some(built)
930        } else {
931            None
932        }
933    }
934
935    /// Lossless `i64` value, valid on the `N == 1` tier where `Int<N>`
936    /// *is* an `i64`. The trait form is `From<Int<1>> for i64`.
937    #[inline]
938    pub(crate) const fn to_i64(self) -> i64 {
939        self.as_i128() as i64
940    }
941
942    /// Lossless `i128` value, valid on the `N <= 2` tiers where every
943    /// `Int<N>` fits an `i128`. The trait forms are `From<Int<1>>` /
944    /// `From<Int<2>> for i128`.
945    #[inline]
946    pub(crate) const fn to_i128(self) -> i128 {
947        self.as_i128()
948    }
949
950    /// Exact `i32` value, or `None` if out of range.
951    #[inline]
952    pub(crate) fn try_to_i32(self) -> Option<i32> {
953        match self.to_i128_checked() {
954            Some(wide) if wide >= i32::MIN as i128 && wide <= i32::MAX as i128 =>
955                Some(wide as i32),
956            _ => None,
957        }
958    }
959
960    /// Exact `u32` value, or `None` if negative or out of range.
961    #[inline]
962    pub(crate) fn try_to_u32(self) -> Option<u32> {
963        match self.to_u128_checked() {
964            Some(wide) if wide <= u32::MAX as u128 => Some(wide as u32),
965            _ => None,
966        }
967    }
968
969    /// Exact `i64` value, or `None` if out of range.
970    #[inline]
971    pub(crate) fn try_to_i64(self) -> Option<i64> {
972        match self.to_i128_checked() {
973            Some(wide) if wide >= i64::MIN as i128 && wide <= i64::MAX as i128 =>
974                Some(wide as i64),
975            _ => None,
976        }
977    }
978
979    /// Exact `u64` value, or `None` if negative or out of range.
980    #[inline]
981    pub(crate) fn try_to_u64(self) -> Option<u64> {
982        match self.to_u128_checked() {
983            Some(wide) if wide <= u64::MAX as u128 => Some(wide as u64),
984            _ => None,
985        }
986    }
987
988    /// Exact `i128` value, or `None` if out of range. Surface alias of
989    /// [`Self::to_i128_checked`].
990    #[inline]
991    pub(crate) fn try_to_i128(self) -> Option<i128> {
992        self.to_i128_checked()
993    }
994
995    /// Exact `u128` value, or `None` if negative or out of range. Surface
996    /// alias of [`Self::to_u128_checked`].
997    #[inline]
998    pub(crate) fn try_to_u128(self) -> Option<u128> {
999        self.to_u128_checked()
1000    }
1001
1002    /// `true` when the value equals one.
1003    #[inline]
1004    pub fn is_one(&self) -> bool {
1005        if N == 0 || self.limbs[0] != 1 {
1006            return false;
1007        }
1008        let mut i = 1;
1009        while i < N {
1010            if self.limbs[i] != 0 {
1011                return false;
1012            }
1013            i += 1;
1014        }
1015        true
1016    }
1017
1018    /// Most negative representable value (`-2^(BITS-1)`).
1019    #[inline]
1020    pub fn min_value() -> Self {
1021        let mut limbs = [0u64; N];
1022        if N > 0 {
1023            limbs[N - 1] = 1u64 << 63;
1024        }
1025        Self { limbs }
1026    }
1027
1028    /// Most positive representable value (`2^(BITS-1) - 1`).
1029    #[inline]
1030    pub fn max_value() -> Self {
1031        let mut limbs = [u64::MAX; N];
1032        if N > 0 {
1033            limbs[N - 1] = u64::MAX >> 1;
1034        }
1035        Self { limbs }
1036    }
1037
1038    /// Checked signed addition: `None` on two's-complement overflow.
1039    /// Overflow happens only when both operands share a sign and the
1040    /// result's sign differs from it. Routes through the add policy's
1041    /// checked door to the FUSED single-pass kernel (ripple + overflow
1042    /// verdict in one traversal) — the previous layered shape
1043    /// (`wrapping_add` then three sign reads then an `Option` rewrap)
1044    /// measured ≈2× the bare loop at 24 limbs in inter-layer moves.
1045    #[inline]
1046    pub const fn checked_add(self, rhs: Self) -> Option<Self> {
1047        crate::int::policy::add::dispatch_checked(self, rhs)
1048    }
1049
1050    /// Checked signed subtraction: `None` on two's-complement overflow
1051    /// (the operands' signs differ and the result takes the subtrahend's
1052    /// sign). Routes through the sub policy's checked door to the fused
1053    /// single-pass kernel — see [`Self::checked_add`].
1054    #[inline]
1055    pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
1056        crate::int::policy::sub::dispatch_checked(self, rhs)
1057    }
1058
1059    /// Checked signed multiplication: `None` if the true product does
1060    /// not fit the signed range. Computed via magnitudes so it reuses
1061    /// the unsigned overflow check, then re-signs.
1062    #[inline]
1063    pub fn checked_mul(self, rhs: Self) -> Option<Self> {
1064        if self.is_zero() || rhs.is_zero() {
1065            return Some(Self::ZERO);
1066        }
1067        let negative = self.is_negative() ^ rhs.is_negative();
1068        let lhs_mag = Uint::<N>::from_limbs(*self.abs().as_limbs());
1069        let rhs_mag = Uint::<N>::from_limbs(*rhs.abs().as_limbs());
1070        let prod = lhs_mag.checked_mul(rhs_mag)?;
1071        let signed = Self::from_limbs(*prod.as_limbs());
1072        if negative {
1073            let negated = signed.wrapping_neg();
1074            // Negative magnitude must not exceed |MIN|; the round-trip
1075            // through wrapping_neg detects the single MIN-magnitude case.
1076            if negated.is_negative() || negated.is_zero() {
1077                Some(negated)
1078            } else {
1079                None
1080            }
1081        } else if signed.is_negative() {
1082            // Positive magnitude landed in the sign bit → overflow.
1083            None
1084        } else {
1085            Some(signed)
1086        }
1087    }
1088
1089    /// Wrapping exponentiation by squaring (`self^exp` modulo `2^BITS`).
1090    /// `self^0 == 1`. Thin delegator DOWN to the pow policy
1091    /// (`pow_dispatch`) on the unsigned reinterpretation: binary
1092    /// square-and-multiply over the const kernels. The low `N` limbs of a
1093    /// power are sign-independent, so reinterpreting as `Uint<N>` and back
1094    /// preserves the two's-complement result.
1095    #[inline]
1096    pub const fn wrapping_pow(self, exp: u32) -> Self {
1097        Self::from_limbs(*pow_dispatch(self.cast_unsigned(), exp).as_limbs())
1098    }
1099
1100    /// Exponentiation by squaring, returning `None` on signed overflow.
1101    #[inline]
1102    pub fn checked_pow(self, mut exp: u32) -> Option<Self> {
1103        let mut acc = Self::ONE;
1104        let mut base = self;
1105        loop {
1106            if exp & 1 == 1 {
1107                acc = acc.checked_mul(base)?;
1108            }
1109            exp >>= 1;
1110            if exp == 0 {
1111                break;
1112            }
1113            base = base.checked_mul(base)?;
1114        }
1115        Some(acc)
1116    }
1117
1118    /// Wrapping square (`self²` modulo `2^BITS`). Thin delegator DOWN to
1119    /// the sqr policy (`sqr_dispatch`) on the unsigned reinterpretation:
1120    /// half-product squaring via the const kernel. The low `N` limbs of a
1121    /// square are sign-independent, so reinterpreting as `Uint<N>` and back
1122    /// preserves the two's-complement result.
1123    #[inline]
1124    pub const fn wrapping_sqr(self) -> Self {
1125        Self::from_limbs(*sqr_dispatch(self.cast_unsigned()).as_limbs())
1126    }
1127
1128    /// Wrapping cube (`self³` modulo `2^BITS`). Thin delegator DOWN to the
1129    /// cube policy (`cube_dispatch`) on the unsigned reinterpretation:
1130    /// `sqr` then one multiply via the const kernels. The low `N` limbs are
1131    /// sign-independent, so reinterpreting as `Uint<N>` and back preserves
1132    /// the two's-complement result.
1133    #[inline]
1134    pub const fn wrapping_cube(self) -> Self {
1135        Self::from_limbs(*cube_dispatch(self.cast_unsigned()).as_limbs())
1136    }
1137
1138    /// MAGNITUDE bit length: `0` for zero, else `floor(log2|self|) + 1`
1139    /// — the number of significant bits of the absolute value `|self|`.
1140    ///
1141    /// This is a *distinct concept* from the primitive bit-count methods
1142    /// ([`Self::leading_zeros`], [`Self::trailing_zeros`],
1143    /// [`Self::count_ones`], [`Self::count_zeros`]), which read the
1144    /// two's-complement representation. `bit_length` ignores the sign:
1145    /// `(-5).bit_length() == 5.bit_length() == 3`. It is kept public for
1146    /// internal normalisation use (root/reciprocal seeds, shift amounts).
1147    ///
1148    /// At `MIN` the magnitude is `2^(BITS-1)`, so
1149    /// `MIN.bit_length() == BITS` (one more than `MAX.bit_length()`,
1150    /// which is `BITS - 1`).
1151    #[inline]
1152    pub const fn bit_length(&self) -> u32 {
1153        bit_len_fixed(self.abs().as_limbs())
1154    }
1155
1156    /// Leading zero bits of the two's-complement representation, matching
1157    /// the primitive `iN::leading_zeros` contract. A negative value has its
1158    /// sign bit (the MSB) set, so it has zero leading zeros; a non-negative
1159    /// value's leading-zero count is `BITS - bit_length` (`BITS` for zero).
1160    ///
1161    /// `MIN` is negative, so `MIN.leading_zeros() == 0`. `MAX` is the
1162    /// largest non-negative value (`0b0111…1`), so `MAX.leading_zeros()
1163    /// == 1`. Note this is the two's-complement count, NOT a magnitude
1164    /// count — contrast [`Self::bit_length`].
1165    #[inline]
1166    pub const fn leading_zeros(&self) -> u32 {
1167        if self.is_negative() {
1168            0
1169        } else {
1170            Self::BITS - self.bit_length()
1171        }
1172    }
1173
1174    // ── Int<N> / Uint<N> parity surface ─────────────────────────
1175    //
1176    // The methods below give the const-generic `Int<N>` the surface the
1177    // kernel-facing `BigInt` trait and the public `IntXXXX` type aliases
1178    // expect. Most delegate to the existing inherent methods or the
1179    // `Uint<N>` twin.
1180
1181    /// Integer constant `10`, used by decimal-scale `10^scale`
1182    /// rescaling.
1183    pub const TEN: Self = {
1184        let mut limbs = [0u64; N];
1185        if N > 0 {
1186            limbs[0] = 10;
1187        }
1188        Self { limbs }
1189    };
1190
1191    /// `|self|` as the unsigned twin. `MIN` maps to `2^(BITS-1)`.
1192    #[inline]
1193    pub const fn unsigned_abs(self) -> Uint<N> {
1194        Uint::from_limbs(*self.abs().as_limbs())
1195    }
1196
1197    /// Two's-complement negation. Alias of [`Self::wrapping_neg`].
1198    #[inline]
1199    pub fn negate(self) -> Self {
1200        self.wrapping_neg()
1201    }
1202
1203    /// Truncating quotient and remainder `(self / rhs, self % rhs)` in a
1204    /// single divmod call. The quotient truncates toward zero and the
1205    /// remainder takes the sign of the dividend. Routes through the
1206    /// const-`N` fast-arm (`div_rem_mag_fixed`): native `u64` idiv at
1207    /// `N == 1`, native `u128` divide at `N == 2`, and the dispatching
1208    /// divmod (Knuth / Burnikel–Ziegler) for wider `N`. Panics on a zero
1209    /// divisor.
1210    #[inline]
1211    pub fn div_rem(self, rhs: Self) -> (Self, Self) {
1212        assert!(!rhs.is_zero(), "attempt to divide by zero");
1213        let neg_q = self.is_negative() ^ rhs.is_negative();
1214        let neg_r = self.is_negative();
1215        let mut quot = [0u64; N];
1216        let mut rem = [0u64; N];
1217        div_rem_mag_fixed::<N>(
1218            self.unsigned_abs().as_limbs(),
1219            rhs.unsigned_abs().as_limbs(),
1220            &mut quot,
1221            &mut rem,
1222        );
1223        let quotient = Self::from_mag_limbs(&quot, neg_q);
1224        let remainder = Self::from_mag_limbs(&rem, neg_r);
1225        (quotient, remainder)
1226    }
1227
1228    /// Builds a signed value from a non-negative magnitude limb slice
1229    /// and a sign, truncating the magnitude into `N` limbs.
1230    #[inline]
1231    pub(crate) const fn from_mag_limbs(mag: &[u64], negative: bool) -> Self {
1232        let mut out = [0u64; N];
1233        let copy_len = if mag.len() < N { mag.len() } else { N };
1234        let mut i = 0;
1235        while i < copy_len {
1236            out[i] = mag[i];
1237            i += 1;
1238        }
1239        let built = Self { limbs: out };
1240        // Inherent `const` zero-check (avoids the non-const `BigInt`
1241        // trait method that name-resolution would otherwise pick here).
1242        if negative && !is_zero_fixed(&built.limbs) {
1243            built.wrapping_neg()
1244        } else {
1245            built
1246        }
1247    }
1248
1249    /// `true` if bit `idx` of the two's-complement representation is set.
1250    #[inline]
1251    pub const fn bit(self, idx: u32) -> bool {
1252        let limb = (idx / 64) as usize;
1253        if limb >= N {
1254            return self.is_negative();
1255        }
1256        (self.limbs[limb] >> (idx % 64)) & 1 == 1
1257    }
1258
1259    /// Builds from a signed 128-bit value. **Truncating** for `Int<1>`
1260    /// (the high 64 bits of `value` are discarded); use the checked `TryFrom`
1261    /// conversion when `value` may not fit.
1262    #[inline]
1263    pub(crate) const fn from_i128(value: i128) -> Self {
1264        // Narrow non-limb fast path (const-folds): for N<=2 write the
1265        // two's-complement limbs directly (truncating for N==1), skipping the
1266        // magnitude split + `wrapping_neg` re-sign. Bit-identical to the
1267        // magnitude path (two's-complement negation commutes with low-64
1268        // truncation).
1269        if N <= 2 {
1270            let mut limbs = [0u64; N];
1271            limbs[0] = value as u64;
1272            if N == 2 {
1273                limbs[1] = (value >> 64) as u64;
1274            }
1275            return Self { limbs };
1276        }
1277        let mag = value.unsigned_abs();
1278        Self::from_mag_limbs(&[mag as u64, (mag >> 64) as u64], value < 0)
1279    }
1280
1281    /// Builds from an unsigned 128-bit value.
1282    #[inline]
1283    pub(crate) const fn from_u128(value: u128) -> Self {
1284        Self::from_mag_limbs(&[value as u64, (value >> 64) as u64], false)
1285    }
1286
1287    /// Builds directly from the little-endian u64 limb array. Alias of
1288    /// [`Self::from_limbs`] under the historic `from_limbs_le` public name.
1289    #[inline]
1290    pub const fn from_limbs_le(limbs: [u64; N]) -> Self {
1291        Self { limbs }
1292    }
1293
1294    /// Returns the little-endian u64 limbs by value. Symmetric with
1295    /// [`Self::from_limbs_le`].
1296    #[inline]
1297    pub const fn limbs_le(self) -> [u64; N] {
1298        self.limbs
1299    }
1300
1301    /// `self · (multiplier as Self)` with the sign of `self`, panicking on
1302    /// overflow (the default-form contract). Computes the n-by-1-word
1303    /// product (the same limb recurrence as `mul_schoolbook_into`) and
1304    /// rejects a non-zero top carry.
1305    #[inline]
1306    pub fn mul_u64(self, multiplier: u64) -> Self {
1307        let mag = *self.unsigned_abs().as_limbs();
1308        let mut prod = [0u64; N];
1309        let mut carry: u64 = 0;
1310        let mut i = 0;
1311        while i < N {
1312            let product = (mag[i] as u128) * (multiplier as u128) + (carry as u128);
1313            prod[i] = product as u64;
1314            carry = (product >> 64) as u64;
1315            i += 1;
1316        }
1317        if carry != 0 {
1318            panic!("Int: mul overflow");
1319        }
1320        let negative = self.is_negative();
1321        let scaled = Self::from_mag_limbs(&prod, negative);
1322        // `from_mag_limbs` only mishandles the `mag == 2^(BITS-1)` edge:
1323        // legal as MIN for `negative`, overflow otherwise.
1324        if !scaled.is_zero() && scaled.is_negative() != negative {
1325            panic!("Int: mul overflow");
1326        }
1327        scaled
1328    }
1329
1330    /// Exact `i128` value, or `None` if it does not fit.
1331    pub fn to_i128_checked(self) -> Option<i128> {
1332        let negative = self.is_negative();
1333        let mag = *self.unsigned_abs().as_limbs();
1334        // First two u64 limbs make up the low u128; the rest must be 0.
1335        let mut i = 2;
1336        while i < N {
1337            if mag[i] != 0 {
1338                return None;
1339            }
1340            i += 1;
1341        }
1342        let lo = if N > 0 { mag[0] as u128 } else { 0 };
1343        let hi = if N > 1 { mag[1] as u128 } else { 0 };
1344        let lo_u128 = lo | (hi << 64);
1345        if negative {
1346            if lo_u128 <= (i128::MAX as u128) + 1 {
1347                Some((lo_u128 as i128).wrapping_neg())
1348            } else {
1349                None
1350            }
1351        } else if lo_u128 <= i128::MAX as u128 {
1352            Some(lo_u128 as i128)
1353        } else {
1354            None
1355        }
1356    }
1357
1358    /// Exact `u128` value, or `None` if negative / too large.
1359    pub fn to_u128_checked(self) -> Option<u128> {
1360        if self.is_negative() {
1361            return None;
1362        }
1363        let mut i = 2;
1364        while i < N {
1365            if self.limbs[i] != 0 {
1366                return None;
1367            }
1368            i += 1;
1369        }
1370        let lo = if N > 0 { self.limbs[0] as u128 } else { 0 };
1371        let hi = if N > 1 { self.limbs[1] as u128 } else { 0 };
1372        Some(lo | (hi << 64))
1373    }
1374
1375    /// Approximate `f64` value of `self` (lossy above 53 significant
1376    /// bits).
1377    pub fn to_f64(self) -> f64 {
1378        let mag = *self.unsigned_abs().as_limbs();
1379        let radix: f64 = 18_446_744_073_709_551_616.0; // 2^64
1380        let mut acc = 0.0f64;
1381        let mut i = N;
1382        while i > 0 {
1383            i -= 1;
1384            acc = acc * radix + mag[i] as f64;
1385        }
1386        if self.is_negative() { -acc } else { acc }
1387    }
1388
1389    /// Approximate `f32` value of `self` (round-to-nearest; lossy above
1390    /// 24 significant bits). Routes through the `f64` accumulation to keep
1391    /// one summation path.
1392    pub fn to_f32(self) -> f32 {
1393        self.to_f64() as f32
1394    }
1395
1396    /// Exact conversion from an `f64`, or `None` when `value` is NaN, ±inf,
1397    /// has a fractional part, or lies outside the `Int<N>` range.
1398    ///
1399    /// `const`: classification and decomposition go through the const
1400    /// `f64::to_bits` plus integer/bit ops only — never the non-const
1401    /// `is_finite` / `fract` / float-`as` paths.
1402    pub(crate) const fn try_from_f64(value: f64) -> Option<Self> {
1403        let bits = value.to_bits();
1404        let negative = (bits >> 63) & 1 == 1;
1405        let exp = ((bits >> 52) & 0x7ff) as i32;
1406        let mant = bits & 0x000f_ffff_ffff_ffff;
1407        if exp == 0x7ff {
1408            // NaN / ±inf.
1409            return None;
1410        }
1411        if exp == 0 {
1412            // ±0 is exact zero; a subnormal is a non-zero |value| < 1, i.e.
1413            // never an integer.
1414            return if mant == 0 { Some(Self::ZERO) } else { None };
1415        }
1416        let significand = (1u64 << 52) | mant; // 53-bit normalized value
1417        let shift = exp - 1075; // (exp - 1023) - 52
1418        Self::from_significand_shift(significand, shift, negative)
1419    }
1420
1421    /// Exact conversion from an `f32`, or `None` on NaN, ±inf, a
1422    /// fractional part, or out-of-range. Decomposes via the const
1423    /// `f32::to_bits` (8-bit exponent, 23-bit mantissa, bias 127, 24-bit
1424    /// significand) with integer/bit ops only — `const` for the same
1425    /// reason as [`Self::try_from_f64`].
1426    pub(crate) const fn try_from_f32(value: f32) -> Option<Self> {
1427        let bits = value.to_bits();
1428        let negative = (bits >> 31) & 1 == 1;
1429        let exp = ((bits >> 23) & 0xff) as i32;
1430        let mant = (bits & 0x007f_ffff) as u64;
1431        if exp == 0xff {
1432            // NaN / ±inf.
1433            return None;
1434        }
1435        if exp == 0 {
1436            return if mant == 0 { Some(Self::ZERO) } else { None };
1437        }
1438        let significand = (1u64 << 23) | mant; // 24-bit normalized value
1439        let shift = exp - 150; // (exp - 127) - 23
1440        Self::from_significand_shift(significand, shift, negative)
1441    }
1442
1443    /// Builds `Int<N>` from `(-1)^sign * significand * 2^shift`, where
1444    /// `significand` fits in 64 bits. Returns `None` when the value has a
1445    /// fractional part (`shift < 0` with low bits set) or does not fit the
1446    /// signed `Int<N>` range. Pure const integer/bit arithmetic — no float
1447    /// ops — so both float entry points are `const`.
1448    const fn from_significand_shift(significand: u64, shift: i32, negative: bool) -> Option<Self> {
1449        if shift < 0 {
1450            let right_shift = (-shift) as u32;
1451            if right_shift >= 64 {
1452                // Whole value is fractional (|value| < 1, non-zero) — not an
1453                // integer.
1454                return None;
1455            }
1456            if significand & ((1u64 << right_shift) - 1) != 0 {
1457                // Fractional bits set — not an integer.
1458                return None;
1459            }
1460            let mag = significand >> right_shift; // integral, fits a single limb
1461            let built = Self::from_mag_limbs(&[mag], negative);
1462            Self::accept_signed(built, negative)
1463        } else {
1464            // Place `significand` left-shifted by `shift` into an N-limb
1465            // magnitude, rejecting any bit that lands beyond limb N-1.
1466            let left_shift = shift as u32;
1467            let limb_off = (left_shift / 64) as usize;
1468            let bit_off = left_shift % 64;
1469            let mut mag = [0u64; N];
1470            // Low chunk into limb `limb_off`; carry into the next limb.
1471            let lo = significand << bit_off;
1472            // The part of `significand` pushed past this limb's top.
1473            let hi = if bit_off == 0 {
1474                0
1475            } else {
1476                significand >> (64 - bit_off)
1477            };
1478            if limb_off < N {
1479                mag[limb_off] = lo;
1480            } else if lo != 0 {
1481                return None;
1482            }
1483            if hi != 0 {
1484                if limb_off + 1 < N {
1485                    mag[limb_off + 1] = hi;
1486                } else {
1487                    return None;
1488                }
1489            }
1490            let built = Self::from_mag_limbs(&mag, negative);
1491            Self::accept_signed(built, negative)
1492        }
1493    }
1494
1495    /// Sign-overflow gate shared by the float builders: a zero is always
1496    /// fine; otherwise the built sign must match the requested one, which
1497    /// rejects a positive magnitude that wrapped into the sign bit while
1498    /// still admitting the exact `MIN` edge (magnitude `2^(bits-1)` with
1499    /// the sign set, which `wrapping_neg` reproduces as itself).
1500    #[inline]
1501    const fn accept_signed(built: Self, negative: bool) -> Option<Self> {
1502        // Inherent const zero-check (the `BigInt::is_zero` trait method
1503        // name-resolution would otherwise pick is not const).
1504        if is_zero_fixed(&built.limbs) {
1505            Some(built)
1506        } else if built.is_negative() != negative {
1507            None
1508        } else {
1509            Some(built)
1510        }
1511    }
1512
1513    /// Builds from an `f64`, truncating toward zero. Saturates to
1514    /// `MIN` / `MAX` on out-of-range; non-finite maps to `ZERO`.
1515    pub fn from_f64(value: f64) -> Self {
1516        if !value.is_finite() {
1517            return Self::ZERO;
1518        }
1519        let negative = value < 0.0;
1520        let mut magnitude = if negative { -value } else { value };
1521        let radix: f64 = 18_446_744_073_709_551_616.0; // 2^64
1522        let mut limbs = [0u64; N];
1523        let mut i = 0;
1524        while magnitude >= 1.0 && i < N {
1525            let rem = magnitude % radix;
1526            limbs[i] = rem as u64;
1527            magnitude = (magnitude - rem) / radix;
1528            i += 1;
1529        }
1530        if magnitude >= 1.0 {
1531            return if negative {
1532                Self::min_value()
1533            } else {
1534                Self::max_value()
1535            };
1536        }
1537        Self::from_mag_limbs(&limbs, negative)
1538    }
1539
1540    /// Parses a signed decimal magnitude from `text`. Accepts an optional
1541    /// leading `-`, then ASCII digits. Only `radix == 10` is supported;
1542    /// any other value returns `Err(())`.
1543    pub const fn from_str_radix(text: &str, radix: u32) -> Result<Self, ()> {
1544        if radix != 10 {
1545            return Err(());
1546        }
1547        let bytes = text.as_bytes();
1548        let (negative, start): (bool, usize) = if !bytes.is_empty() && bytes[0] == b'-' {
1549            (true, 1)
1550        } else {
1551            (false, 0)
1552        };
1553        if start >= bytes.len() {
1554            return Err(());
1555        }
1556        // acc = acc * 10 + digit per digit, truncating into N limbs — the
1557        // same Horner recurrence the macro runs through `mul_schoolbook`
1558        // + `add_assign`, but the low-N-limb multiply-by-10 is
1559        // folded into one n-by-1-word pass (no `2*N` staging buffer).
1560        let mut acc = [0u64; N];
1561        let mut k = start;
1562        while k < bytes.len() {
1563            let ch = bytes[k];
1564            if ch < b'0' || ch > b'9' {
1565                return Err(());
1566            }
1567            let digit = (ch - b'0') as u64;
1568            let mut carry: u64 = digit;
1569            let mut j = 0;
1570            while j < N {
1571                let product = (acc[j] as u128) * 10u128 + (carry as u128);
1572                acc[j] = product as u64;
1573                carry = (product >> 64) as u64;
1574                j += 1;
1575            }
1576            k += 1;
1577        }
1578        Ok(Self::from_mag_limbs(&acc, negative))
1579    }
1580
1581    // ── Named-type API parity (the `IntXXXX` alias surface) ─────
1582    //
1583    // The methods below complete the inherent surface the `IntXXXX = Int<N>`
1584    // type aliases expose, so every named-type call site keeps resolving.
1585
1586    /// Integer power: `self^exp` (wrapping on overflow). Alias of
1587    /// [`Self::wrapping_pow`] under the `pow` name.
1588    #[inline]
1589    pub const fn pow(self, exp: u32) -> Self {
1590        self.wrapping_pow(exp)
1591    }
1592
1593    /// Integer square root of the magnitude (`floor(sqrt(|self|))`),
1594    /// returned non-negative. Delegates to the unsigned sibling which
1595    /// routes through `isqrt_dispatch`.
1596    #[inline]
1597    pub fn isqrt(self) -> Self {
1598        Self::from_limbs(*self.unsigned_abs().isqrt().as_limbs())
1599    }
1600
1601    /// Integer square: `self²` modulo `2^BITS`. Routes through the sqr
1602    /// policy (`sqr_dispatch`) on the unsigned reinterpretation, then
1603    /// reinterprets back as signed. Equivalent to `wrapping_sqr`.
1604    #[inline]
1605    pub fn sqr(self) -> Self {
1606        self.wrapping_sqr()
1607    }
1608
1609    /// Integer cube: `self³` modulo `2^BITS`. Routes through the cube
1610    /// policy (`cube_dispatch`) on the unsigned reinterpretation, then
1611    /// reinterprets back as signed. Equivalent to `wrapping_cube`.
1612    #[inline]
1613    pub fn cube(self) -> Self {
1614        self.wrapping_cube()
1615    }
1616
1617    /// Integer cube root of the magnitude (`floor(cbrt(|self|))`),
1618    /// returned non-negative. Delegates to the unsigned sibling which
1619    /// routes through `icbrt_dispatch`.
1620    #[inline]
1621    pub fn icbrt(self) -> Self {
1622        Self::from_limbs(*self.unsigned_abs().icbrt().as_limbs())
1623    }
1624
1625    /// Integer hypotenuse: `round(sqrt(self^2 + other^2))` without
1626    /// intermediate overflow of the radicand. Routes through the hypot
1627    /// policy (`hypot_dispatch`): the radicand `self^2 + other^2` is formed
1628    /// in a wider limb scratch buffer and the floor root is taken via the
1629    /// integer slice `isqrt`, then a single round step under `mode` lands
1630    /// the result. Returns [`None`] when the rounded result does not fit
1631    /// the signed range of `Int<N>` (true overflow). The sign of each
1632    /// operand drops out of the squaring; `hypot(0, 0) = 0` and
1633    /// `hypot(0, x) = |x|`.
1634    #[inline]
1635    #[must_use]
1636    pub(crate) fn hypot(
1637        self,
1638        other: Self,
1639        mode: crate::support::rounding::RoundingMode,
1640    ) -> Option<Self>
1641    where
1642        crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
1643    {
1644        hypot_dispatch::<N>(self, other, mode)
1645    }
1646
1647    /// Sum of squares: `self^2 + other^2`, the sqrt-free magnitude
1648    /// primitive. Routes through the sum-of-squares policy
1649    /// (`sum_sq_dispatch`): the radicand is formed in a wider limb scratch
1650    /// buffer (the same former [`Self::hypot`] roots), so there is no
1651    /// intermediate truncation. Returns [`None`] when `self^2 + other^2`
1652    /// exceeds the signed range of `Int<N>` (true overflow). The sign of
1653    /// each operand drops out of the squaring, so the result is always
1654    /// non-negative. Comparing two `sum_sq` values orders the same as
1655    /// comparing the corresponding [`Self::hypot`] values (the root is
1656    /// monotonic), which is why a distance comparison can skip the root.
1657    #[inline]
1658    #[must_use]
1659    pub(crate) fn sum_sq(self, other: Self) -> Option<Self>
1660    where
1661        crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
1662    {
1663        sum_sq_dispatch::<N>(self, other)
1664    }
1665
1666    /// Reinterprets the bit pattern as the unsigned sibling.
1667    #[inline]
1668    pub const fn cast_unsigned(self) -> Uint<N> {
1669        Uint::from_limbs(self.limbs)
1670    }
1671
1672    /// Approximate `f64` value. Alias of [`Self::to_f64`], matching the
1673    /// macro's `as_f64` name.
1674    #[inline]
1675    pub fn as_f64(self) -> f64 {
1676        self.to_f64()
1677    }
1678
1679    /// Count of set bits across the two's-complement representation,
1680    /// matching the primitive `iN::count_ones` contract — the limbs are
1681    /// read as stored, so negative values count their sign-extended
1682    /// one-bits. `(-1).count_ones() == BITS` (all-ones), and
1683    /// `MIN.count_ones() == 1` (only the sign bit). This is a
1684    /// two's-complement count, not a magnitude count.
1685    #[inline]
1686    pub const fn count_ones(self) -> u32 {
1687        let mut total = 0;
1688        let mut i = 0;
1689        while i < N {
1690            total += self.limbs[i].count_ones();
1691            i += 1;
1692        }
1693        total
1694    }
1695
1696    /// Count of clear bits across the two's-complement representation,
1697    /// matching the primitive `iN::count_zeros` contract
1698    /// (`BITS - count_ones`). `(-1).count_zeros() == 0` and
1699    /// `MIN.count_zeros() == BITS - 1`.
1700    #[inline]
1701    pub const fn count_zeros(self) -> u32 {
1702        Self::BITS - self.count_ones()
1703    }
1704
1705    /// Number of trailing zero bits of the two's-complement
1706    /// representation, matching the primitive `iN::trailing_zeros`
1707    /// contract; `BITS` for zero. `MIN` has only its sign bit set, so
1708    /// `MIN.trailing_zeros() == BITS - 1`. This reads the stored limbs,
1709    /// not the magnitude — contrast [`Self::bit_length`].
1710    #[inline]
1711    pub const fn trailing_zeros(self) -> u32 {
1712        let mut i = 0;
1713        while i < N {
1714            if self.limbs[i] != 0 {
1715                return i as u32 * 64 + self.limbs[i].trailing_zeros();
1716            }
1717            i += 1;
1718        }
1719        Self::BITS
1720    }
1721
1722    /// Checked negation: `None` exactly at `MIN` (whose negation
1723    /// overflows the signed range).
1724    #[inline]
1725    pub const fn checked_neg(self) -> Option<Self> {
1726        if eq_dispatch(self, Self::MIN) {
1727            None
1728        } else {
1729            Some(self.wrapping_neg())
1730        }
1731    }
1732
1733    /// Checked division: `None` on a zero divisor.
1734    #[inline]
1735    pub const fn checked_div(self, rhs: Self) -> Option<Self> {
1736        if is_zero_fixed(&rhs.limbs) {
1737            None
1738        } else {
1739            Some(self.wrapping_div(rhs))
1740        }
1741    }
1742
1743    /// Checked remainder: `None` on a zero divisor, and `None` for the
1744    /// `MIN % -1` overflow case (the paired division `MIN / -1` overflows
1745    /// the signed range), matching the primitive integer contract.
1746    #[inline]
1747    pub const fn checked_rem(self, rhs: Self) -> Option<Self> {
1748        if is_zero_fixed(&rhs.limbs) || self.is_min_neg_one(rhs) {
1749            // Zero divisor, or the `MIN % -1` overflow case (its paired
1750            // `MIN / -1` division overflows the signed range).
1751            None
1752        } else {
1753            Some(self.wrapping_rem(rhs))
1754        }
1755    }
1756
1757    /// `true` when `self == MIN` and `rhs == -1` — the remainder/division
1758    /// overflow case where `MIN / -1` exceeds the signed range.
1759    #[inline]
1760    const fn is_min_neg_one(self, rhs: Self) -> bool {
1761        cmp_fixed(&self.limbs, &Self::MIN.limbs) == 0
1762            && cmp_fixed(&rhs.wrapping_neg().limbs, &Self::ONE.limbs) == 0
1763    }
1764
1765    /// Euclidean division: the quotient that leaves a non-negative
1766    /// remainder.
1767    #[inline]
1768    pub const fn div_euclid(self, rhs: Self) -> Self {
1769        let quotient = self.wrapping_div(rhs);
1770        let remainder = self.wrapping_rem(rhs);
1771        if remainder.is_negative() {
1772            if rhs.is_negative() {
1773                quotient.wrapping_add(Self::ONE)
1774            } else {
1775                quotient.wrapping_sub(Self::ONE)
1776            }
1777        } else {
1778            quotient
1779        }
1780    }
1781
1782    /// Euclidean remainder — always non-negative.
1783    #[inline]
1784    pub const fn rem_euclid(self, rhs: Self) -> Self {
1785        let remainder = self.wrapping_rem(rhs);
1786        if remainder.is_negative() {
1787            remainder.wrapping_add(rhs.abs())
1788        } else {
1789            remainder
1790        }
1791    }
1792
1793    /// Wrapping addition paired with the two's-complement overflow flag.
1794    #[inline]
1795    pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) {
1796        let sum = self.wrapping_add(rhs);
1797        let lhs_negative = self.is_negative();
1798        let rhs_negative = rhs.is_negative();
1799        let sum_negative = sum.is_negative();
1800        (sum, lhs_negative == rhs_negative && sum_negative != lhs_negative)
1801    }
1802
1803    /// Wrapping subtraction paired with the two's-complement overflow
1804    /// flag.
1805    #[inline]
1806    pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) {
1807        let difference = self.wrapping_sub(rhs);
1808        let lhs_negative = self.is_negative();
1809        let rhs_negative = rhs.is_negative();
1810        let difference_negative = difference.is_negative();
1811        (difference, lhs_negative != rhs_negative && difference_negative != lhs_negative)
1812    }
1813
1814    /// Wrapping negation paired with the overflow flag (`true` only at
1815    /// `MIN`).
1816    #[inline]
1817    pub const fn overflowing_neg(self) -> (Self, bool) {
1818        let overflow = cmp_fixed(&self.limbs, &Self::MIN.limbs) == 0;
1819        (self.wrapping_neg(), overflow)
1820    }
1821
1822    /// Wrapping remainder paired with an overflow flag. The flag is `true`
1823    /// only for `MIN % -1` (whose paired division overflows the signed
1824    /// range), in which case the remainder is `0`; otherwise `false`.
1825    #[inline]
1826    pub const fn overflowing_rem(self, rhs: Self) -> (Self, bool) {
1827        if self.is_min_neg_one(rhs) {
1828            (Self::ZERO, true)
1829        } else {
1830            (self.wrapping_rem(rhs), false)
1831        }
1832    }
1833
1834    /// Saturating addition: clamps to `MIN` / `MAX` on overflow.
1835    #[inline]
1836    pub const fn saturating_add(self, rhs: Self) -> Self {
1837        match self.checked_add(rhs) {
1838            Some(exact) => exact,
1839            None => {
1840                if self.is_negative() {
1841                    Self::MIN
1842                } else {
1843                    Self::MAX
1844                }
1845            }
1846        }
1847    }
1848
1849    /// Saturating subtraction: clamps to `MIN` / `MAX` on overflow.
1850    #[inline]
1851    pub const fn saturating_sub(self, rhs: Self) -> Self {
1852        match self.checked_sub(rhs) {
1853            Some(exact) => exact,
1854            None => {
1855                if self.is_negative() {
1856                    Self::MIN
1857                } else {
1858                    Self::MAX
1859                }
1860            }
1861        }
1862    }
1863
1864    /// Saturating negation: `MIN` saturates to `MAX`.
1865    #[inline]
1866    pub const fn saturating_neg(self) -> Self {
1867        match self.checked_neg() {
1868            Some(exact) => exact,
1869            None => Self::MAX,
1870        }
1871    }
1872
1873    /// Rotates the bits left by `rotation` (modulo `BITS`).
1874    #[inline]
1875    pub fn rotate_left(self, rotation: u32) -> Self {
1876        let bits = Self::BITS;
1877        let rotation = rotation % bits;
1878        if rotation == 0 {
1879            return self;
1880        }
1881        let unsigned = self.cast_unsigned();
1882        Self::from_limbs(((unsigned.shl(rotation)) | (unsigned.shr(bits - rotation))).limbs)
1883    }
1884
1885    /// Rotates the bits right by `rotation` (modulo `BITS`).
1886    #[inline]
1887    pub fn rotate_right(self, rotation: u32) -> Self {
1888        self.rotate_left(Self::BITS - (rotation % Self::BITS))
1889    }
1890
1891    /// Truncating cast to `u128` (low 128 magnitude bits, sign ignored).
1892    /// **Truncating** — discards any higher limbs and the sign; use
1893    /// [`Self::to_u128_checked`] (or `TryFrom`) when the value may not
1894    /// fit or may be negative.
1895    #[inline]
1896    pub(crate) const fn as_u128(self) -> u128 {
1897        let mag = *self.unsigned_abs().as_limbs();
1898        let lo = if N > 0 { mag[0] as u128 } else { 0 };
1899        let hi = if N > 1 { mag[1] as u128 } else { 0 };
1900        lo | (hi << 64)
1901    }
1902
1903    /// Truncating cast to `i128` (low 128 bits, sign-applied).
1904    /// **Truncating** — for `Int<3+>` any value outside the `i128` range
1905    /// loses its high limbs; use [`Self::to_i128_checked`] (or `TryFrom`)
1906    /// when the value may not fit.
1907    #[inline]
1908    pub(crate) const fn as_i128(self) -> i128 {
1909        // Narrow non-limb fast path (const-folds per monomorphisation): for
1910        // N<=2 the limbs ARE the i64/i128 two's-complement value, so skip the
1911        // `unsigned_abs`/`wrapping_neg` sign-magnitude round trip (which costs
1912        // two `neg_dispatch` calls on a negative operand). Always fits i128
1913        // for N<=2; bit-identical to the magnitude path below.
1914        if N <= 2 {
1915            if N == 1 {
1916                return (self.limbs[0] as i64) as i128;
1917            }
1918            let lo = self.limbs[0] as u128;
1919            let hi = self.limbs[1] as u128;
1920            return (lo | (hi << 64)) as i128;
1921        }
1922        let mag = *self.unsigned_abs().as_limbs();
1923        let lo = if N > 0 { mag[0] as u128 } else { 0 };
1924        let hi = if N > 1 { mag[1] as u128 } else { 0 };
1925        let combined = lo | (hi << 64);
1926        if self.is_negative() {
1927            (combined as i128).wrapping_neg()
1928        } else {
1929            combined as i128
1930        }
1931    }
1932
1933    /// Widening / narrowing cast to any other [`BigInt`] type, via the
1934    /// shared magnitude + sign bridge. Matches the macro's
1935    /// `resize::<T>()` signature so the decimal-tier code that calls
1936    /// `storage.resize::<$Wider>()` resolves against `Int<N>`.
1937    #[inline]
1938    pub(crate) fn resize<T: crate::int::types::traits::BigInt>(self) -> T {
1939        use crate::int::types::traits::BigInt as _;
1940        self.resize_to::<T>()
1941    }
1942
1943    /// Truncating division toward zero. Panics on a zero divisor.
1944    /// Matches the macro's `wrapping_div` (single-limb-aware
1945    /// `div_rem`, not the dispatching `div_rem`).
1946    #[inline]
1947    pub const fn wrapping_div(self, rhs: Self) -> Self {
1948        if is_zero_fixed(&rhs.limbs) {
1949            panic!("attempt to divide by zero");
1950        }
1951        let negative = self.is_negative() ^ rhs.is_negative();
1952        let mut quotient = [0u64; N];
1953        let mut remainder = [0u64; N];
1954        div_rem(
1955            self.unsigned_abs().as_limbs(),
1956            rhs.unsigned_abs().as_limbs(),
1957            &mut quotient,
1958            &mut remainder,
1959        );
1960        Self::from_mag_limbs(&quotient, negative)
1961    }
1962
1963    /// Truncating remainder; result carries the sign of `self`. Panics
1964    /// on a zero divisor. Matches the macro's `wrapping_rem`.
1965    #[inline]
1966    pub const fn wrapping_rem(self, rhs: Self) -> Self {
1967        if is_zero_fixed(&rhs.limbs) {
1968            panic!("attempt to calculate the remainder with a divisor of zero");
1969        }
1970        let mut quotient = [0u64; N];
1971        let mut remainder = [0u64; N];
1972        div_rem(
1973            self.unsigned_abs().as_limbs(),
1974            rhs.unsigned_abs().as_limbs(),
1975            &mut quotient,
1976            &mut remainder,
1977        );
1978        Self::from_mag_limbs(&remainder, self.is_negative())
1979    }
1980
1981    /// Full `self · rhs` product widened into a `W: BigInt`, in one
1982    /// step (no double trip through the magnitude staging buffer). Used
1983    /// by the wide-tier `Mul` operator to compute
1984    /// `Storage * Storage → Wider`. Matches the macro's `widen_mul`.
1985    #[inline]
1986    pub(crate) fn widen_mul<W>(self, rhs: Self) -> W
1987    where
1988        W: crate::int::types::traits::BigInt,
1989        W::Scratch: crate::int::types::compute_limbs::ComputeLimbs,
1990        crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
1991    {
1992        use crate::int::types::compute_limbs::{ComputeLimbs, Limbs};
1993        let negative = self.is_negative() ^ rhs.is_negative();
1994        let lhs_mag = *self.unsigned_abs().as_limbs();
1995        let rhs_mag = *rhs.unsigned_abs().as_limbs();
1996        // Full product spans 2·N u64 limbs — sized exactly by the source's
1997        // `ComputeLimbs::double_buffered_u64()` (no build-max blanket). Route through
1998        // the equal-length multiply dispatcher: both operands are `[u64; N]`,
1999        // so this is the single site every wide tier's full product flows
2000        // through. The dispatcher base-cases to schoolbook below
2001        // `KARATSUBA_THRESHOLD_U64` (every shipped tier) and engages the
2002        // non-allocating Karatsuba kernel at or above it.
2003        let mut prod_buf = <Limbs<N> as ComputeLimbs>::double_buffered_u64();
2004        let prod = prod_buf.as_mut();
2005        mul_fast::<N>(&lhs_mag, &rhs_mag, &mut prod[..2 * N]);
2006        // Pack the `2·N`-u64 product into `N` u128 limbs for the kept
2007        // `BigInt::from_mag_sign_u128` bridge. The result `W` holds the
2008        // product, so its scratch carrier's `single_u128()` buffer (`≥ N`)
2009        // sizes the packed magnitude exactly.
2010        let mut u128_buf = <W::Scratch as ComputeLimbs>::single_u128();
2011        let u128_prod = u128_buf.as_mut();
2012        let mut i = 0;
2013        while i < N {
2014            u128_prod[i] = (prod[2 * i] as u128) | ((prod[2 * i + 1] as u128) << 64);
2015            i += 1;
2016        }
2017        W::from_mag_sign_u128(&u128_prod[..N], negative)
2018    }
2019}
2020
2021impl<const N: usize> Int<N> {
2022    /// Const cross-width signed comparison `Int<N>` vs `Int<M>`, returning
2023    /// `core::cmp::Ordering`. No widening copy is made: the sign is
2024    /// compared first (a negative value is always less than a non-negative
2025    /// one); when the signs agree the magnitudes are compared
2026    /// length-aware via [`cmp_cross`] over the `unsigned_abs`
2027    /// limbs (the longer magnitude's surplus high limbs must be zero for
2028    /// equality, else it is the larger). For two negatives the larger
2029    /// magnitude is the smaller value, so the magnitude order is flipped.
2030    #[inline]
2031    pub(crate) const fn cmp_cross<const M: usize>(self, other: Int<M>) -> Ordering {
2032        let self_negative = self.is_negative();
2033        let other_negative = other.is_negative();
2034        if self_negative && !other_negative {
2035            return Ordering::Less;
2036        }
2037        if !self_negative && other_negative {
2038            return Ordering::Greater;
2039        }
2040        // Same sign: compare magnitudes length-aware.
2041        let self_mag = self.unsigned_abs();
2042        let other_mag = other.unsigned_abs();
2043        let mag_cmp = cmp_cross(self_mag.as_limbs(), other_mag.as_limbs());
2044        // For two negatives the larger magnitude is the smaller value.
2045        let mag_cmp = if self_negative { -mag_cmp } else { mag_cmp };
2046        if mag_cmp < 0 {
2047            Ordering::Less
2048        } else if mag_cmp > 0 {
2049            Ordering::Greater
2050        } else {
2051            Ordering::Equal
2052        }
2053    }
2054}
2055
2056impl<const N: usize> Int<N> {
2057    /// Const cross-width, cross-*scale* signed comparison: compares
2058    /// `self` against `other · 10^scale_diff`, returning
2059    /// [`core::cmp::Ordering`]. Used by the decimal layer to compare two
2060    /// `D<Int<_>, S>` values at *different* `SCALE`s without materialising
2061    /// a widened product (no `generic_const_exprs`, no computed type).
2062    ///
2063    /// # Approach (scale-down-with-remainder, overflow-free)
2064    ///
2065    /// Rather than scale `other` *up* by `10^scale_diff` (which can
2066    /// overflow `M`'s width), this scales `self` *down* by the same
2067    /// factor — division can never overflow. With magnitudes
2068    /// `|self| = q · 10^scale_diff + r` (`0 ≤ r < 10^scale_diff`):
2069    ///
2070    /// * compare the quotient `q` against `|other|`;
2071    /// * on a magnitude tie, a nonzero remainder `r` means `|self|` is the
2072    ///   larger magnitude (it carries extra low digits `other` lacks).
2073    ///
2074    /// Signs are resolved first (a negative is always less than a
2075    /// non-negative); for two negatives the larger magnitude is the
2076    /// smaller value, so the magnitude order is flipped. `scale_diff == 0`
2077    /// degenerates to a plain cross-width magnitude compare.
2078    ///
2079    /// `const`, `core`-only, no allocation: the `10^scale_diff` divisor and
2080    /// the quotient/remainder are built in fixed staging buffers (the same
2081    /// `max_n_limbs(4)` width the wide tiers stage products through — `288`
2082    /// limbs at xx-wide, scaling down with `MAX_WORK_N`), and the division
2083    /// reuses [`div_rem`].
2084    pub(crate) const fn cmp_cross_scaled<const M: usize>(
2085        self,
2086        other: Int<M>,
2087        scale_diff: u32,
2088    ) -> Ordering {
2089        let self_negative = self.is_negative();
2090        let other_negative = other.is_negative();
2091        if self_negative && !other_negative {
2092            return Ordering::Less;
2093        }
2094        if !self_negative && other_negative {
2095            return Ordering::Greater;
2096        }
2097
2098        // Same sign (or one/both zero): compare magnitudes. `mag_cmp` is
2099        // the i32 sign of `|self|` − `|other| · 10^scale_diff`.
2100        let self_mag = self.unsigned_abs();
2101        let other_mag = other.unsigned_abs();
2102
2103        let mag_cmp = if scale_diff == 0 {
2104            cmp_cross(self_mag.as_limbs(), other_mag.as_limbs())
2105        } else {
2106            // Build the divisor 10^scale_diff in a fixed staging buffer.
2107            // `max_n_limbs(4)` (= 288 at xx-wide) is the wide-tier staging
2108            // width, tracking MAX_WORK_N so a narrower build does not carry
2109            // the widest tier's buffer; it covers every shipped width/scale.
2110            let mut pow = [0u64; max_n_limbs(4)];
2111            pow[0] = 1;
2112            let mut step = 0;
2113            while step < scale_diff {
2114                // pow *= 10, propagating carry across limbs.
2115                let mut carry: u128 = 0;
2116                let mut i = 0;
2117                while i < pow.len() {
2118                    let prod = (pow[i] as u128) * 10u128 + carry;
2119                    pow[i] = prod as u64;
2120                    carry = prod >> 64;
2121                    i += 1;
2122                }
2123                step += 1;
2124            }
2125
2126            // |self| = quotient · 10^scale_diff + remainder.
2127            let mut quotient = [0u64; max_n_limbs(4)];
2128            let mut remainder = [0u64; max_n_limbs(4)];
2129            div_rem(self_mag.as_limbs(), &pow, &mut quotient, &mut remainder);
2130
2131            // Compare quotient against |other|; tie-break on remainder.
2132            let quotient_cmp = cmp_cross(&quotient, other_mag.as_limbs());
2133            if quotient_cmp != 0 {
2134                quotient_cmp
2135            } else {
2136                // Quotients equal: a nonzero remainder makes |self| larger.
2137                let mut limb_index = 0;
2138                let mut remainder_nonzero = false;
2139                while limb_index < remainder.len() {
2140                    if remainder[limb_index] != 0 {
2141                        remainder_nonzero = true;
2142                        break;
2143                    }
2144                    limb_index += 1;
2145                }
2146                if remainder_nonzero {
2147                    1
2148                } else {
2149                    0
2150                }
2151            }
2152        };
2153
2154        // For two negatives, the larger magnitude is the smaller value.
2155        let signed_cmp = if self_negative { -mag_cmp } else { mag_cmp };
2156        if signed_cmp < 0 {
2157            Ordering::Less
2158        } else if signed_cmp > 0 {
2159            Ordering::Greater
2160        } else {
2161            Ordering::Equal
2162        }
2163    }
2164}
2165
2166impl<const N: usize> Int<N> {
2167    /// Const EXACT comparison of the decimal value `self / 10^scale`
2168    /// against the exact dyadic value of an `f64`, returning
2169    /// [`core::cmp::Ordering`]. This is *value* equality, NOT a lossy
2170    /// round-trip: an `f64` has an exact rational value `m · 2^e`, and
2171    /// this compares `self / 10^scale` against it by cross-multiplying to
2172    /// integers (overflow-free in fixed staging buffers, riding
2173    /// [`cmp_cross`]).
2174    ///
2175    /// `value` MUST be finite — the caller rejects `NaN` / `±inf` before
2176    /// calling (those compare unequal to every decimal).
2177    ///
2178    /// # Approach
2179    ///
2180    /// Decompose `value = (-1)^sign · m · 2^e` from its IEEE-754 bits
2181    /// (`m` the 53-bit-or-subnormal integer mantissa, `e` the unbiased
2182    /// power-of-two exponent). The magnitudes satisfy
2183    /// `|self| / 10^scale  ==  m · 2^e` iff:
2184    ///
2185    /// * `e ≥ 0`:  `|self|          ==  m · 2^e · 10^scale`
2186    /// * `e < 0`:  `|self| · 2^(−e)  ==  m · 10^scale`
2187    ///
2188    /// both sides being non-negative integers. Signs are resolved first
2189    /// (a negative is always less than a non-negative; for two negatives
2190    /// the magnitude order is flipped). `m == 0` is the float zero.
2191    pub(crate) const fn cmp_f64_exact(self, scale: u32, value: f64) -> Ordering {
2192        let bits = value.to_bits();
2193        let float_negative = (bits >> 63) != 0;
2194        let exp_field = ((bits >> 52) & 0x7ff) as i32;
2195        let frac = bits & 0x000f_ffff_ffff_ffff;
2196        // Mantissa and unbiased power-of-two exponent.
2197        let (mantissa, exponent): (u64, i32) = if exp_field == 0 {
2198            // Subnormal (or zero): no implicit leading bit.
2199            (frac, -1074)
2200        } else {
2201            (frac | 0x0010_0000_0000_0000, exp_field - 1075)
2202        };
2203
2204        let self_negative = self.is_negative();
2205        let float_is_zero = mantissa == 0;
2206        // Resolve signs. The float's zero has no sign for ordering.
2207        if float_is_zero {
2208            // value == 0: compare against self's sign / zeroness.
2209            let self_limbs = self.as_limbs();
2210            let mut self_nonzero = false;
2211            let mut k = 0;
2212            while k < self_limbs.len() {
2213                if self_limbs[k] != 0 {
2214                    self_nonzero = true;
2215                    break;
2216                }
2217                k += 1;
2218            }
2219            if !self_nonzero {
2220                return Ordering::Equal;
2221            }
2222            return if self_negative { Ordering::Less } else { Ordering::Greater };
2223        }
2224        if self_negative && !float_negative {
2225            return Ordering::Less;
2226        }
2227        if !self_negative && float_negative {
2228            return Ordering::Greater;
2229        }
2230
2231        // Same sign, both nonzero: compare magnitudes.
2232        // Build the two non-negative integer sides in fixed staging
2233        // buffers (288 u64 limbs covers every shipped width / scale /
2234        // f64 exponent: D307 magnitude ≤ 16 limbs, 10^scale ≤ 16 limbs,
2235        // 2^1074 ≤ 17 limbs — products stay well under 288).
2236        let self_mag = self.unsigned_abs();
2237        let self_mag_limbs = self_mag.as_limbs();
2238
2239        // 10^scale in a buffer.
2240        let mut pow10 = [0u64; 288];
2241        pow10[0] = 1;
2242        let mut step = 0;
2243        while step < scale {
2244            let mut carry: u128 = 0;
2245            let mut i = 0;
2246            while i < pow10.len() {
2247                let prod = (pow10[i] as u128) * 10u128 + carry;
2248                pow10[i] = prod as u64;
2249                carry = prod >> 64;
2250                i += 1;
2251            }
2252            step += 1;
2253        }
2254
2255        let mantissa_limbs = [mantissa];
2256
2257        let mut lhs = [0u64; 288];
2258        let mut rhs = [0u64; 288];
2259
2260        if exponent >= 0 {
2261            // lhs = |self|; rhs = mantissa · 2^exponent · 10^scale.
2262            let mut i = 0;
2263            while i < self_mag_limbs.len() {
2264                lhs[i] = self_mag_limbs[i];
2265                i += 1;
2266            }
2267            // scaled_mantissa = mantissa · 10^scale
2268            let mut scaled_mantissa = [0u64; 288];
2269            mul_schoolbook(&mantissa_limbs, &pow10, &mut scaled_mantissa);
2270            // rhs = scaled_mantissa << exponent
2271            shl(&scaled_mantissa, exponent as u32, &mut rhs);
2272        } else {
2273            // lhs = |self| · 2^(−exponent); rhs = mantissa · 10^scale.
2274            shl(self_mag_limbs, (-exponent) as u32, &mut lhs);
2275            mul_schoolbook(&mantissa_limbs, &pow10, &mut rhs);
2276        }
2277
2278        let mag_cmp = cmp_cross(&lhs, &rhs);
2279        // For two negatives the larger magnitude is the smaller value.
2280        let signed_cmp = if self_negative { -mag_cmp } else { mag_cmp };
2281        if signed_cmp < 0 {
2282            Ordering::Less
2283        } else if signed_cmp > 0 {
2284            Ordering::Greater
2285        } else {
2286            Ordering::Equal
2287        }
2288    }
2289}
2290
2291// One generic comparison surface across widths. The `N == M` case is
2292// covered here too, so `Int<N>` carries no separate same-width
2293// `PartialEq` / `PartialOrd` / `Ord` impl (a derived or hand-written
2294// same-width comparison would collide — E0119). `Eq` is still derived:
2295// it only requires the `PartialEq<Self>` this generic impl provides.
2296impl<const N: usize, const M: usize> PartialEq<Int<M>> for Int<N> {
2297    #[inline]
2298    fn eq(&self, other: &Int<M>) -> bool {
2299        self.cmp_cross(*other) == Ordering::Equal
2300    }
2301}
2302
2303impl<const N: usize, const M: usize> PartialOrd<Int<M>> for Int<N> {
2304    #[inline]
2305    fn partial_cmp(&self, other: &Int<M>) -> Option<Ordering> {
2306        Some(self.cmp_cross(*other))
2307    }
2308}
2309
2310// `i128` interop for the 128-bit storage `Int<2>` (D38's backend).
2311// `Int<2>` *is* an `i128`, so the comparison is the direct `as_i128`
2312// value — no widening round-trip. This lets `to_bits()` results compare
2313// against `i128` literals without an explicit conversion at the call
2314// site. Deliberately `Int<2>`-only: for wider tiers an `i128` comparison
2315// would be a lossy narrowing and is not offered.
2316// `Int<2>` *is* a 128-bit integer, so the conversion to `i128` is exact
2317// (the trait form of `as_i128`). Enables `i128::from(int2)` / `.into()`.
2318impl From<Int<2>> for i128 {
2319    #[inline]
2320    fn from(value: Int<2>) -> i128 {
2321        value.as_i128()
2322    }
2323}
2324
2325impl PartialEq<i128> for Int<2> {
2326    #[inline]
2327    fn eq(&self, other: &i128) -> bool {
2328        self.as_i128() == *other
2329    }
2330}
2331impl PartialEq<Int<2>> for i128 {
2332    #[inline]
2333    fn eq(&self, other: &Int<2>) -> bool {
2334        *self == other.as_i128()
2335    }
2336}
2337impl PartialOrd<i128> for Int<2> {
2338    #[inline]
2339    fn partial_cmp(&self, other: &i128) -> Option<Ordering> {
2340        self.as_i128().partial_cmp(other)
2341    }
2342}
2343impl PartialOrd<Int<2>> for i128 {
2344    #[inline]
2345    fn partial_cmp(&self, other: &Int<2>) -> Option<Ordering> {
2346        self.partial_cmp(&other.as_i128())
2347    }
2348}
2349
2350// `i64` interop for the 64-bit storage `Int<1>` (D18's backend). `Int<1>`
2351// *is* an `i64`, so the bridge is the direct value (its `as_i128()` is the
2352// sign-extended `i64`) — letting `to_bits()` results compare against `i64`
2353// literals without an explicit conversion. Deliberately `Int<1>`-only.
2354impl From<Int<1>> for i64 {
2355    #[inline]
2356    fn from(value: Int<1>) -> i64 {
2357        value.as_i128() as i64
2358    }
2359}
2360// `Int<1>` is 64-bit, so widening to `i128` is exact.
2361impl From<Int<1>> for i128 {
2362    #[inline]
2363    fn from(value: Int<1>) -> i128 {
2364        value.as_i128()
2365    }
2366}
2367impl PartialEq<i64> for Int<1> {
2368    #[inline]
2369    fn eq(&self, other: &i64) -> bool {
2370        self.as_i128() == *other as i128
2371    }
2372}
2373impl PartialEq<Int<1>> for i64 {
2374    #[inline]
2375    fn eq(&self, other: &Int<1>) -> bool {
2376        *self as i128 == other.as_i128()
2377    }
2378}
2379impl PartialOrd<i64> for Int<1> {
2380    #[inline]
2381    fn partial_cmp(&self, other: &i64) -> Option<Ordering> {
2382        self.as_i128().partial_cmp(&(i128::from(*other)))
2383    }
2384}
2385impl PartialOrd<Int<1>> for i64 {
2386    #[inline]
2387    fn partial_cmp(&self, other: &Int<1>) -> Option<Ordering> {
2388        i128::from(*self).partial_cmp(&other.as_i128())
2389    }
2390}
2391
2392impl<const N: usize> Ord for Int<N> {
2393    #[inline]
2394    fn cmp(&self, other: &Self) -> Ordering {
2395        // Same-width total order, routing through the cmp policy dispatcher
2396        // so the algorithm seam exists at a single point and the `const {
2397        // select::<N>() }` block folds the choice per monomorphisation.
2398        cmp_dispatch(*self, *other)
2399    }
2400}
2401
2402impl<const N: usize> Add for Int<N> {
2403    type Output = Self;
2404    #[inline]
2405    fn add(self, rhs: Self) -> Self {
2406        add_dispatch(self, rhs)
2407    }
2408}
2409
2410impl<const N: usize> Sub for Int<N> {
2411    type Output = Self;
2412    #[inline]
2413    fn sub(self, rhs: Self) -> Self {
2414        self.wrapping_sub(rhs)
2415    }
2416}
2417
2418impl<const N: usize> Mul for Int<N> {
2419    type Output = Self;
2420    #[inline]
2421    fn mul(self, rhs: Self) -> Self {
2422        self.wrapping_mul(rhs)
2423    }
2424}
2425
2426impl<const N: usize> BitAnd for Int<N> {
2427    type Output = Self;
2428    #[inline]
2429    fn bitand(self, rhs: Self) -> Self {
2430        let mut out = [0u64; N];
2431        let mut i = 0;
2432        while i < N {
2433            out[i] = self.limbs[i] & rhs.limbs[i];
2434            i += 1;
2435        }
2436        Self { limbs: out }
2437    }
2438}
2439
2440impl<const N: usize> BitOr for Int<N> {
2441    type Output = Self;
2442    #[inline]
2443    fn bitor(self, rhs: Self) -> Self {
2444        let mut out = [0u64; N];
2445        let mut i = 0;
2446        while i < N {
2447            out[i] = self.limbs[i] | rhs.limbs[i];
2448            i += 1;
2449        }
2450        Self { limbs: out }
2451    }
2452}
2453
2454impl<const N: usize> BitXor for Int<N> {
2455    type Output = Self;
2456    #[inline]
2457    fn bitxor(self, rhs: Self) -> Self {
2458        let mut out = [0u64; N];
2459        let mut i = 0;
2460        while i < N {
2461            out[i] = self.limbs[i] ^ rhs.limbs[i];
2462            i += 1;
2463        }
2464        Self { limbs: out }
2465    }
2466}
2467
2468impl<const N: usize> Not for Int<N> {
2469    type Output = Self;
2470    #[inline]
2471    fn not(self) -> Self {
2472        let mut out = [0u64; N];
2473        let mut i = 0;
2474        while i < N {
2475            out[i] = !self.limbs[i];
2476            i += 1;
2477        }
2478        Self { limbs: out }
2479    }
2480}
2481
2482impl<const N: usize> Shl<u32> for Int<N> {
2483    type Output = Self;
2484    #[inline]
2485    fn shl(self, shift: u32) -> Self {
2486        let mut out = [0u64; N];
2487        shl_fixed(&self.limbs, shift, &mut out);
2488        Self { limbs: out }
2489    }
2490}
2491
2492impl<const N: usize> Shr<u32> for Int<N> {
2493    type Output = Self;
2494    #[inline]
2495    fn shr(self, shift: u32) -> Self {
2496        // Arithmetic (sign-preserving) right shift — matches Rust's signed
2497        // `>>` and the prior macro `Int*` types the transcendental range
2498        // reduction relies on. Two's-complement: x >> s == !((!x) >> s) for x < 0.
2499        let neg = self.is_negative();
2500        let src = if neg { !self } else { self };
2501        let mut out = [0u64; N];
2502        shr_fixed(&src.limbs, shift, &mut out);
2503        let shifted = Self { limbs: out };
2504        if neg { !shifted } else { shifted }
2505    }
2506}
2507
2508impl<const N: usize> Neg for Int<N> {
2509    type Output = Self;
2510    #[inline]
2511    fn neg(self) -> Self {
2512        self.wrapping_neg()
2513    }
2514}
2515
2516// ── Div / Rem ───────────────────────────────────────────────────────
2517//
2518// Truncating signed division / remainder, delegating to the dispatching
2519// `div_rem` so the operators share the macro types' divide algorithm
2520// (`div_rem_dispatch`: Knuth / Burnikel–Ziegler for multi-limb
2521// divisors). These supertraits are what `BigInt` requires.
2522
2523impl<const N: usize> Div for Int<N> {
2524    type Output = Self;
2525    #[inline]
2526    fn div(self, rhs: Self) -> Self {
2527        self.div_rem(rhs).0
2528    }
2529}
2530
2531impl<const N: usize> Rem for Int<N> {
2532    type Output = Self;
2533    #[inline]
2534    fn rem(self, rhs: Self) -> Self {
2535        rem_dispatch(self, rhs)
2536    }
2537}
2538
2539// ── Display / FromStr ───────────────────────────────────────────────
2540//
2541// Delegate to the shared limb fmt / parse path, so the const-generic
2542// surface round-trips identically across every width.
2543
2544impl<const N: usize> core::fmt::Display for Uint<N>
2545where
2546    crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
2547{
2548    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2549        // Exact per-`N` decimal output buffer (`20N + 2` bytes), drawn from
2550        // the `Limbs<N>` scratch carrier; the formatter writes the base-10
2551        // digits from its tail.
2552        let mut buf =
2553            <crate::int::types::compute_limbs::Limbs<N> as crate::int::types::compute_limbs::ComputeLimbs>::digit_formatting_limbs_u8();
2554        let rendered = fmt_into::<N>(&self.limbs, 10, true, buf.as_mut());
2555        formatter.pad_integral(true, "", rendered)
2556    }
2557}
2558
2559impl<const N: usize> core::fmt::Display for Int<N>
2560where
2561    crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
2562{
2563    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2564        let mag = *self.unsigned_abs().as_limbs();
2565        // Exact per-`N` decimal output buffer (`20N + 2` bytes): an `Int<N>`
2566        // is `64N` bits, so its base-10 form is `⌈64·N·log10(2)⌉ ≈ 19.27·N`
2567        // digits, comfortably within `20N + 2`. Drawn from the `Limbs<N>`
2568        // scratch carrier; the formatter writes the digits from its tail.
2569        let mut buf =
2570            <crate::int::types::compute_limbs::Limbs<N> as crate::int::types::compute_limbs::ComputeLimbs>::digit_formatting_limbs_u8();
2571        let rendered = fmt_into::<N>(&mag, 10, true, buf.as_mut());
2572        formatter.pad_integral(!self.is_negative() || self.is_zero(), "", rendered)
2573    }
2574}
2575
2576impl<const N: usize> core::str::FromStr for Int<N> {
2577    type Err = ();
2578    #[inline]
2579    fn from_str(text: &str) -> Result<Self, ()> {
2580        Self::from_str_radix(text, 10)
2581    }
2582}
2583
2584// ── Radix formatting (raw two's-complement bit pattern) ─────────────
2585//
2586// `LowerHex` / `UpperHex` / `Octal` / `Binary` print the raw limb bit
2587// pattern (not a signed magnitude), matching the macro `$S` impls. Each
2588// draws its output buffer exact-per-`N` from the `Limbs<N>` scratch carrier:
2589// hex (`16N` digits) fits the `digit_formatting_limbs_u8` decimal buffer
2590// (`20N + 2`); octal (`⌈64N/3⌉`) and binary (`64N`, one byte per bit) take
2591// the wider `bit_formatting_limbs_u8` buffer (`64N + 2`).
2592
2593impl<const N: usize> core::fmt::LowerHex for Int<N>
2594where
2595    crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
2596{
2597    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2598        let mut buf =
2599            <crate::int::types::compute_limbs::Limbs<N> as crate::int::types::compute_limbs::ComputeLimbs>::digit_formatting_limbs_u8();
2600        let rendered = fmt_into::<N>(&self.limbs, 16, true, buf.as_mut());
2601        formatter.pad_integral(true, "0x", rendered)
2602    }
2603}
2604
2605impl<const N: usize> core::fmt::UpperHex for Int<N>
2606where
2607    crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
2608{
2609    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2610        let mut buf =
2611            <crate::int::types::compute_limbs::Limbs<N> as crate::int::types::compute_limbs::ComputeLimbs>::digit_formatting_limbs_u8();
2612        let rendered = fmt_into::<N>(&self.limbs, 16, false, buf.as_mut());
2613        formatter.pad_integral(true, "0x", rendered)
2614    }
2615}
2616
2617impl<const N: usize> core::fmt::Octal for Int<N>
2618where
2619    crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
2620{
2621    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2622        let mut buf =
2623            <crate::int::types::compute_limbs::Limbs<N> as crate::int::types::compute_limbs::ComputeLimbs>::bit_formatting_limbs_u8();
2624        let rendered = fmt_into::<N>(&self.limbs, 8, true, buf.as_mut());
2625        formatter.pad_integral(true, "0o", rendered)
2626    }
2627}
2628
2629impl<const N: usize> core::fmt::Binary for Int<N>
2630where
2631    crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
2632{
2633    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2634        let mut buf =
2635            <crate::int::types::compute_limbs::Limbs<N> as crate::int::types::compute_limbs::ComputeLimbs>::bit_formatting_limbs_u8();
2636        let rendered = fmt_into::<N>(&self.limbs, 2, true, buf.as_mut());
2637        formatter.pad_integral(true, "0b", rendered)
2638    }
2639}
2640
2641// ── Width conversion: widen (lossless) / narrow (fallible) ─────────
2642//
2643// `Uint<N>` and `Uint<M>` are different-sized stack types, so a value
2644// conversion builds a fresh `[u64; M]` — there is no heap allocation,
2645// and reinterpreting across widths via `transmute` would be unsound
2646// (different size and layout). `resize` writes each destination limb
2647// exactly once via `array::from_fn`; `widen` is the infallible extend
2648// and `narrow` the information-preserving truncation. Stable Rust
2649// cannot constrain `M >= N` / `M <= N` in the type system, so the
2650// direction is enforced by `debug_assert!` plus, for `narrow`, the
2651// `Option` return.
2652
2653impl<const N: usize> Uint<N> {
2654    /// Resizes to `M` limbs: zero-extends when widening, drops the high
2655    /// limbs when narrowing. Direction-agnostic and infallible.
2656    ///
2657    /// Named `resize_n` (not `resize`) so the const-generic width bridge
2658    /// does not collide with the type-generic `Int::resize` the named-
2659    /// type API expects.
2660    #[inline]
2661    pub const fn resize_n<const M: usize>(self) -> Uint<M> {
2662        let mut out = [0u64; M];
2663        let mut i = 0;
2664        while i < M {
2665            if i < N {
2666                out[i] = self.limbs[i];
2667            }
2668            i += 1;
2669        }
2670        Uint::from_limbs(out)
2671    }
2672
2673    /// Widens to a wider `Uint<M>` (`M >= N`), zero-extending the new
2674    /// high limbs. Lossless.
2675    #[inline]
2676    pub fn widen<const M: usize>(self) -> Uint<M> {
2677        debug_assert!(M >= N, "widen requires M >= N");
2678        self.resize_n::<M>()
2679    }
2680
2681    /// Narrows to a narrower `Uint<M>` (`M <= N`). Returns `None` if any
2682    /// discarded high limb is non-zero (the value does not fit `M`).
2683    #[inline]
2684    pub fn narrow<const M: usize>(self) -> Option<Uint<M>> {
2685        debug_assert!(M <= N, "narrow requires M <= N");
2686        let keep = if M < N { M } else { N };
2687        let mut i = keep;
2688        while i < N {
2689            if self.limbs[i] != 0 {
2690                return None;
2691            }
2692            i += 1;
2693        }
2694        Some(self.resize_n::<M>())
2695    }
2696}
2697
2698impl<const N: usize> Int<N> {
2699    /// Resizes to `M` limbs: sign-extends when widening, drops the high
2700    /// limbs when narrowing. Direction-agnostic and infallible
2701    /// (narrowing may change the represented value).
2702    ///
2703    /// Named `resize_n` (not `resize`) so the const-generic width bridge
2704    /// does not collide with the type-generic `Int::resize` the named-
2705    /// type API expects (the magnitude-bridge cast over any `BigInt`).
2706    ///
2707    /// Infallible two's-complement width conversion `Int<N> -> Int<M>`,
2708    /// const and direction-agnostic. Copies the overlapping limbs; when
2709    /// widening, fills the new high limbs with the sign (all-ones if the
2710    /// source is negative, else zero); when narrowing, drops the surplus
2711    /// high limbs (which may change the represented value). This is the
2712    /// canonical internal resize that the const fallible `try_narrow` and
2713    /// the `Int<->Int` magnitude bridge build on.
2714    #[inline]
2715    pub(crate) const fn resize_n<const M: usize>(self) -> Int<M> {
2716        let fill = if self.is_negative() { u64::MAX } else { 0 };
2717        let mut out = [0u64; M];
2718        let mut i = 0;
2719        while i < M {
2720            out[i] = if i < N { self.limbs[i] } else { fill };
2721            i += 1;
2722        }
2723        Int::from_limbs(out)
2724    }
2725
2726    /// Const fallible narrow `Int<N> -> Int<M>` (`1 <= M <= N`). Returns
2727    /// `Some` only when every discarded high limb is a pure sign-extension
2728    /// of the narrowed value's top bit — i.e. the value fits `M` limbs as
2729    /// two's complement — and `None` otherwise. The const base for the
2730    /// future public fallible narrow.
2731    #[inline]
2732    pub(crate) const fn try_narrow<const M: usize>(self) -> Option<Int<M>> {
2733        debug_assert!(M >= 1 && M <= N, "try_narrow requires 1 <= M <= N");
2734        let sign_fill = if (self.limbs[M - 1] >> 63) == 1 {
2735            u64::MAX
2736        } else {
2737            0
2738        };
2739        let mut i = M;
2740        while i < N {
2741            if self.limbs[i] != sign_fill {
2742                return None;
2743            }
2744            i += 1;
2745        }
2746        Some(self.resize_n::<M>())
2747    }
2748
2749    /// Widens to a wider `Int<M>` (`M >= N`), sign-extending. Lossless.
2750    #[inline]
2751    pub fn widen<const M: usize>(self) -> Int<M> {
2752        debug_assert!(M >= N, "widen requires M >= N");
2753        self.resize_n::<M>()
2754    }
2755
2756    /// Narrows to a narrower `Int<M>` (`1 <= M <= N`). Returns `None`
2757    /// unless every discarded high limb is a pure sign-extension of the
2758    /// narrowed value's top bit — i.e. the value fits `M` limbs as
2759    /// two's complement.
2760    #[inline]
2761    pub fn narrow<const M: usize>(self) -> Option<Int<M>> {
2762        self.try_narrow::<M>()
2763    }
2764}
2765
2766
2767
2768// ── std-aligned primitive conversions ───────────────────────────────
2769// Infallible widening (the source always fits `Int<N>` / `Uint<N>` for
2770// N >= 1, judged against the `N == 1` = `i64` floor) is `From`. Fallible
2771// narrowing (the value may exceed the target range) is `TryFrom`,
2772// returning [`ConvertError::Overflow`]. Each impl is a thin one-line
2773// delegate to the inherent const base. `Into` / `TryInto` come for free.
2774
2775// ── IN: primitive → Int<N> ───────────────────────────────────────────
2776macro_rules! int_from_signed {
2777    ($($prim:ty => $base:ident),+) => {$(
2778        impl<const N: usize> From<$prim> for Int<N> {
2779            #[inline]
2780            fn from(value: $prim) -> Self { Self::$base(value) }
2781        }
2782    )+};
2783}
2784int_from_signed!(i8 => from_i8, i16 => from_i16, i32 => from_i32, i64 => from_i64);
2785
2786macro_rules! int_from_unsigned {
2787    ($($prim:ty => $base:ident),+) => {$(
2788        impl<const N: usize> From<$prim> for Int<N> {
2789            #[inline]
2790            fn from(value: $prim) -> Self { Self::$base(value) }
2791        }
2792    )+};
2793}
2794int_from_unsigned!(u8 => from_u8, u16 => from_u16, u32 => from_u32);
2795
2796macro_rules! uint_from_unsigned {
2797    ($($prim:ty),+) => {$(
2798        impl<const N: usize> From<$prim> for Uint<N> {
2799            #[inline]
2800            fn from(value: $prim) -> Self { Self::from_u64(value as u64) }
2801        }
2802    )+};
2803}
2804uint_from_unsigned!(u8, u16, u32, u64);
2805
2806// Fallible IN: `u64` can overflow `Int<1>`; `i128` / `u128` can overflow
2807// the narrow tiers (see the inherent `try_from_*`).
2808impl<const N: usize> TryFrom<u64> for Int<N> {
2809    type Error = crate::support::error::ConvertError;
2810    #[inline]
2811    fn try_from(value: u64) -> Result<Self, Self::Error> {
2812        Self::try_from_u64(value).ok_or(crate::support::error::ConvertError::Overflow)
2813    }
2814}
2815
2816impl<const N: usize> TryFrom<i128> for Int<N> {
2817    type Error = crate::support::error::ConvertError;
2818    #[inline]
2819    fn try_from(value: i128) -> Result<Self, Self::Error> {
2820        Self::try_from_i128(value).ok_or(crate::support::error::ConvertError::Overflow)
2821    }
2822}
2823
2824impl<const N: usize> TryFrom<u128> for Int<N> {
2825    type Error = crate::support::error::ConvertError;
2826    #[inline]
2827    fn try_from(value: u128) -> Result<Self, Self::Error> {
2828        Self::try_from_u128(value).ok_or(crate::support::error::ConvertError::Overflow)
2829    }
2830}
2831
2832// ── OUT: Int<N> → primitive ──────────────────────────────────────────
2833// `i64` / `i128` are always representable on the fitting tiers, so those
2834// are infallible `From` (declared above: `From<Int<1>> for i64`,
2835// `From<Int<1>>` / `From<Int<2>> for i128`). The std blanket
2836// `impl<T,U: From<T>> TryFrom<T> for U` then derives the matching
2837// `TryFrom` for those tiers, so a generic `TryFrom<Int<N>> for i64`/`i128`
2838// is omitted to avoid the coherence conflict. The remaining widths /
2839// targets are genuinely fallible:
2840impl<const N: usize> TryFrom<Int<N>> for i32 {
2841    type Error = crate::support::error::ConvertError;
2842    #[inline]
2843    fn try_from(value: Int<N>) -> Result<Self, Self::Error> {
2844        value.try_to_i32().ok_or(crate::support::error::ConvertError::Overflow)
2845    }
2846}
2847
2848impl<const N: usize> TryFrom<Int<N>> for u32 {
2849    type Error = crate::support::error::ConvertError;
2850    #[inline]
2851    fn try_from(value: Int<N>) -> Result<Self, Self::Error> {
2852        value.try_to_u32().ok_or(crate::support::error::ConvertError::Overflow)
2853    }
2854}
2855
2856impl<const N: usize> TryFrom<Int<N>> for u64 {
2857    type Error = crate::support::error::ConvertError;
2858    #[inline]
2859    fn try_from(value: Int<N>) -> Result<Self, Self::Error> {
2860        value.try_to_u64().ok_or(crate::support::error::ConvertError::Overflow)
2861    }
2862}
2863
2864impl<const N: usize> TryFrom<Int<N>> for u128 {
2865    type Error = crate::support::error::ConvertError;
2866    #[inline]
2867    fn try_from(value: Int<N>) -> Result<Self, Self::Error> {
2868        value.try_to_u128().ok_or(crate::support::error::ConvertError::Overflow)
2869    }
2870}
2871
2872impl<const N: usize> TryFrom<u128> for Uint<N> {
2873    type Error = crate::support::error::ConvertError;
2874    #[inline]
2875    fn try_from(value: u128) -> Result<Self, Self::Error> {
2876        Self::try_from_u128(value).ok_or(crate::support::error::ConvertError::Overflow)
2877    }
2878}
2879
2880// ── Floats ───────────────────────────────────────────────────────────
2881// Float → Int<N> is fallible (NaN, ±inf, non-integer, out-of-range), so
2882// `TryFrom`. There is no `From<Int<N>> for f64/f32`: that direction is
2883// lossy above the mantissa width and is offered only as the inherent
2884// `to_f64` / `to_f32` round-to-nearest methods.
2885impl<const N: usize> TryFrom<f64> for Int<N> {
2886    type Error = crate::support::error::ConvertError;
2887    #[inline]
2888    fn try_from(value: f64) -> Result<Self, Self::Error> {
2889        Self::try_from_f64(value).ok_or(crate::support::error::ConvertError::Overflow)
2890    }
2891}
2892
2893impl<const N: usize> TryFrom<f32> for Int<N> {
2894    type Error = crate::support::error::ConvertError;
2895    #[inline]
2896    fn try_from(value: f32) -> Result<Self, Self::Error> {
2897        Self::try_from_f32(value).ok_or(crate::support::error::ConvertError::Overflow)
2898    }
2899}
2900
2901#[cfg(test)]
2902mod tests {
2903    use super::*;
2904    use crate::int::algos::mul::mul_schoolbook::{mul_low_fixed, mul_schoolbook_fixed};
2905
2906    #[test]
2907    fn try_from_i128_detects_narrow_overflow() {
2908        // Int<2> (128-bit) holds every i128.
2909        assert_eq!(Int::<2>::try_from_i128(i128::MAX), Some(Int::<2>::from_i128(i128::MAX)));
2910        assert_eq!(Int::<2>::try_from_i128(i128::MIN), Some(Int::<2>::from_i128(i128::MIN)));
2911        // Int<1> (64-bit) holds only the i64 range.
2912        assert_eq!(Int::<1>::try_from_i128(i64::MAX as i128 + 1), None);
2913        assert_eq!(Int::<1>::try_from_i128(i64::MIN as i128 - 1), None);
2914        assert_eq!(
2915            Int::<1>::try_from_i128(i64::MAX as i128),
2916            Some(Int::<1>::from_i64(i64::MAX))
2917        );
2918        // TryFrom mirrors it.
2919        assert!(<Int<1> as TryFrom<i128>>::try_from(i64::MAX as i128 + 1).is_err());
2920        assert_eq!(<Int<2> as TryFrom<i128>>::try_from(-5_i128), Ok(Int::<2>::from_i64(-5)));
2921    }
2922
2923    #[test]
2924    fn try_from_u128_detects_narrow_overflow() {
2925        // Uint twin: every u128 fits Uint<2+>; Uint<1> only holds u64.
2926        assert_eq!(Uint::<2>::try_from_u128(u128::MAX), Some(Uint::<2>::from_u128(u128::MAX)));
2927        assert_eq!(Uint::<1>::try_from_u128(u64::MAX as u128 + 1), None);
2928        assert_eq!(Uint::<1>::try_from_u128(u64::MAX as u128), Some(Uint::<1>::from_u64(u64::MAX)));
2929        assert!(<Uint<1> as TryFrom<u128>>::try_from(u64::MAX as u128 + 1).is_err());
2930        // Int<N> u128 entry: Int<1> fails above i64::MAX, Int<2> above i128::MAX.
2931        assert_eq!(Int::<1>::try_from_u128(u64::MAX as u128), None);
2932        assert_eq!(Int::<2>::try_from_u128(u128::MAX), None);
2933        assert_eq!(Int::<3>::try_from_u128(u128::MAX), Some(Int::<3>::from_u128(u128::MAX)));
2934        assert!(<Int<2> as TryFrom<u128>>::try_from(u128::MAX).is_err());
2935    }
2936
2937    #[test]
2938    fn try_from_u64_and_out_conversions_at_edges() {
2939        // u64 entry: Int<1> rejects above i64::MAX, wider tiers accept.
2940        assert!(<Int<1> as TryFrom<u64>>::try_from(u64::MAX).is_err());
2941        assert_eq!(<Int<2> as TryFrom<u64>>::try_from(u64::MAX), Ok(Int::<2>::from_u128(u64::MAX as u128)));
2942        // OUT lossless: Int<1> -> i64, Int<2> -> i128 are exact From.
2943        assert_eq!(i128::from(Int::<2>::MAX), i128::MAX);
2944        assert_eq!(i128::from(Int::<2>::MIN), i128::MIN);
2945        assert_eq!(i64::from(Int::<1>::from_i64(-123)), -123_i64);
2946        // OUT fallible: a wide value out of i128 range rejects (inherent
2947        // base; i128 has no generic TryFrom — it is From-derived on N<=2).
2948        let big = Int::<3>::from_u128(u128::MAX);
2949        assert_eq!(big.try_to_i128(), None);
2950        assert_eq!(<i32 as TryFrom<Int<4>>>::try_from(Int::<4>::from_i64(-7)), Ok(-7_i32));
2951        assert!(<u32 as TryFrom<Int<4>>>::try_from(Int::<4>::from_i64(-1)).is_err());
2952    }
2953
2954    #[test]
2955    fn float_conversions_reject_and_round_trip() {
2956        // Rejections.
2957        assert_eq!(Int::<2>::try_from_f64(f64::NAN), None);
2958        assert_eq!(Int::<2>::try_from_f64(f64::INFINITY), None);
2959        assert_eq!(Int::<2>::try_from_f64(f64::NEG_INFINITY), None);
2960        assert_eq!(Int::<2>::try_from_f64(0.5), None);
2961        assert_eq!(Int::<2>::try_from_f64(3.5), None);
2962        assert_eq!(Int::<2>::try_from_f32(f32::NAN), None);
2963        assert_eq!(Int::<2>::try_from_f32(f32::INFINITY), None);
2964        assert_eq!(Int::<2>::try_from_f32(0.5), None);
2965        assert_eq!(Int::<2>::try_from_f32(3.5), None);
2966        assert_eq!(Int::<1>::try_from_f64(1e30), None); // out of i64 range
2967        // ±0 maps to ZERO.
2968        assert_eq!(Int::<2>::try_from_f64(0.0), Some(Int::<2>::ZERO));
2969        assert_eq!(Int::<2>::try_from_f64(-0.0), Some(Int::<2>::ZERO));
2970        assert_eq!(Int::<2>::try_from_f32(0.0), Some(Int::<2>::ZERO));
2971        assert_eq!(Int::<2>::try_from_f32(-0.0), Some(Int::<2>::ZERO));
2972        // In-range integers round-trip exactly.
2973        assert_eq!(Int::<4>::try_from_f64(42.0), Some(Int::<4>::from_i64(42)));
2974        assert_eq!(Int::<4>::try_from_f64(-42.0), Some(Int::<4>::from_i64(-42)));
2975        assert_eq!(Int::<4>::try_from_f32(7.0), Some(Int::<4>::from_i64(7)));
2976        assert_eq!(Int::<4>::try_from_f32(-7.0), Some(Int::<4>::from_i64(-7)));
2977        // TryFrom mirrors the helper.
2978        assert!(<Int<2> as TryFrom<f64>>::try_from(f64::NAN).is_err());
2979        assert_eq!(<Int<2> as TryFrom<f64>>::try_from(-9.0), Ok(Int::<2>::from_i64(-9)));
2980        assert_eq!(<Int<2> as TryFrom<f32>>::try_from(-9.0), Ok(Int::<2>::from_i64(-9)));
2981        // OUT to_f64 / to_f32 round-trip small values.
2982        assert_eq!(Int::<4>::from_i64(123).to_f64(), 123.0);
2983        assert_eq!(Int::<4>::from_i64(-123).to_f64(), -123.0);
2984        assert_eq!(Int::<4>::from_i64(123).to_f32(), 123.0_f32);
2985    }
2986
2987    #[test]
2988    fn float_conversions_wide_and_boundaries() {
2989        // A large exact integer (2^64) needs two limbs: it overflows
2990        // Int<1> but fits Int<2>.
2991        let big = 2f64.powi(64); // exactly representable
2992        assert_eq!(Int::<1>::try_from_f64(big), None);
2993        assert_eq!(
2994            Int::<2>::try_from_f64(big),
2995            Some(Int::<2>::from_u128(1u128 << 64))
2996        );
2997
2998        // 2^63 is exactly i64::MAX + 1 — just over the Int<1> positive
2999        // range, so it must reject.
3000        let two_63 = 2f64.powi(63);
3001        assert_eq!(Int::<1>::try_from_f64(two_63), None);
3002        // i64::MAX itself (2^63 - 1) is not f64-exact, but 2^63 - 2^10 is;
3003        // confirm a representable value strictly below the cap fits.
3004        let near_max = (i64::MAX - 1023) as f64; // exact in f64
3005        assert_eq!(near_max as i64, i64::MAX - 1023);
3006        assert_eq!(
3007            Int::<1>::try_from_f64(near_max),
3008            Some(Int::<1>::from_i64(i64::MAX - 1023))
3009        );
3010
3011        // The MIN edge: -2^63 == i64::MIN fits Int<1> exactly.
3012        let min_edge = -(2f64.powi(63));
3013        assert_eq!(
3014            Int::<1>::try_from_f64(min_edge),
3015            Some(Int::<1>::from_i64(i64::MIN))
3016        );
3017        // One magnitude bit past the negative edge does not fit.
3018        assert_eq!(Int::<1>::try_from_f64(-(2f64.powi(64))), None);
3019
3020        // f32 analogues: 2^24 fits Int<1>; the round-trip is exact.
3021        let two_24 = 2f32.powi(24);
3022        assert_eq!(
3023            Int::<1>::try_from_f32(two_24),
3024            Some(Int::<1>::from_i64(1 << 24))
3025        );
3026    }
3027
3028    #[test]
3029    fn float_conversions_are_const() {
3030        // Proves the float→int path is usable in const context.
3031        const X: Option<Int<2>> = Int::<2>::try_from_f64(42.0);
3032        const Y: Option<Int<2>> = Int::<2>::try_from_f32(42.0);
3033        assert_eq!(X, Some(Int::<2>::from_i64(42)));
3034        assert_eq!(Y, Some(Int::<2>::from_i64(42)));
3035    }
3036
3037    #[test]
3038    fn from_traits_are_infallible_for_fitting_prims() {
3039        assert_eq!(Int::<1>::from(-7_i32), Int::<1>::from_i64(-7));
3040        assert_eq!(Int::<4>::from(42_i64), Int::<4>::from_i64(42));
3041        assert_eq!(Uint::<1>::from(7_u32), Uint::<1>::from_u64(7));
3042        assert_eq!(Uint::<4>::from(u64::MAX), Uint::<4>::from_u64(u64::MAX));
3043    }
3044
3045    /// The `BigInt` / `BigInt` surface must compile and
3046    /// behave through a fully generic bound, for both signed and
3047    /// unsigned fixed-width integers.
3048    #[test]
3049    fn fixed_int_trait_surface() {
3050        fn exercises<T: BigInt>(seven: T, three: T) {
3051            assert_eq!(T::LIMBS as u32 * 64, T::BITS);
3052            assert!(T::ZERO.is_zero());
3053            assert!(T::ONE.is_one());
3054            assert!(!T::ZERO.is_one());
3055
3056            // Operators via the supertrait bounds.
3057            let ten = seven + three;
3058            assert_eq!(ten - three, seven);
3059            assert_eq!(ten, seven.wrapping_add(three));
3060            assert_eq!(seven.wrapping_sub(three) + three, seven);
3061
3062            // Bitwise / shift surface.
3063            let _ = (seven & three) | (seven ^ three);
3064            let _ = !T::ZERO;
3065            assert_eq!((T::ONE << 4) >> 4, T::ONE);
3066
3067            // Checked arithmetic returns Some for in-range work.
3068            assert_eq!(seven.checked_add(three), Some(ten));
3069            assert!(seven.checked_mul(three).is_some());
3070
3071            // Powers and optimisable functions agree with each other.
3072            assert_eq!(three.sqr(), three * three);
3073            assert_eq!(three.cube(), three * three * three);
3074            assert_eq!(three.pow(2), three.sqr());
3075            assert_eq!(three.wrapping_pow(3), three.cube());
3076            assert_eq!(three.checked_pow(2), Some(three.sqr()));
3077
3078            // bit_length / leading_zeros consistency.
3079            assert_eq!(T::ONE.bit_length(), 1);
3080            assert_eq!(T::ZERO.bit_length(), 0);
3081            assert_eq!(T::ONE.leading_zeros(), T::BITS - 1);
3082
3083            // Reductions and the limb round-trip.
3084            let items = [T::ONE, T::ONE, T::ONE];
3085            assert_eq!(T::sum(items), three);
3086            assert_eq!(T::product([three, T::ONE]), three);
3087            assert_eq!(T::from_limbs(seven.to_limbs()), seven);
3088        }
3089
3090        exercises(Int::<4>::from_i64(7), Int::<4>::from_i64(3));
3091        exercises(Int::<6>::from_i64(7), Int::<6>::from_i64(3));
3092    }
3093
3094    /// The truncated low-`N` product must equal the low `N` limbs of
3095    /// the full `2N`-limb schoolbook product, across widths and edges.
3096    #[test]
3097    fn limbs_mul_low_matches_full_product_low_half() {
3098        fn check<const N: usize, const D: usize>(lhs: [u64; N], rhs: [u64; N]) {
3099            debug_assert!(D == 2 * N);
3100            let mut full = [0u64; D];
3101            mul_schoolbook_fixed::<N, D>(&lhs, &rhs, &mut full);
3102            let mut low = [0u64; N];
3103            mul_low_fixed::<N>(&lhs, &rhs, &mut low);
3104            let mut expected = [0u64; N];
3105            expected.copy_from_slice(&full[..N]);
3106            assert_eq!(low, expected, "low-half mismatch for {lhs:?} * {rhs:?}");
3107        }
3108
3109        // Width 4 (256-bit): zero, one, MAX, cross-limb spans.
3110        check::<4, 8>([0, 0, 0, 0], [0, 0, 0, 0]);
3111        check::<4, 8>([1, 0, 0, 0], [u64::MAX, u64::MAX, u64::MAX, u64::MAX]);
3112        check::<4, 8>([u64::MAX; 4], [u64::MAX; 4]);
3113        check::<4, 8>([0, 1, 0, 0], [0, 1, 0, 0]); // 2^64 * 2^64
3114        check::<4, 8>(
3115            [0xDEAD_BEEF, 0xCAFE_F00D, 0x1234, 0x5678_9ABC],
3116            [0xFEED_FACE, 0x0BAD_C0DE, 0x9999, 0x0000_0001],
3117        );
3118        // Width 2 and width 6 to exercise other monomorphisations.
3119        check::<2, 4>([u64::MAX, u64::MAX], [3, 0]);
3120        check::<6, 12>([7, 8, 9, 10, 11, 12], [1, 2, 3, 4, 5, 6]);
3121    }
3122
3123    /// The dedicated squaring kernel must be bit-exact against the
3124    /// general truncated product `x · x` at every width and edge case.
3125    #[test]
3126    fn dedicated_sqr_matches_general_mul() {
3127        fn check<const N: usize>(limbs: [u64; N]) {
3128            let value = Uint::<N>::from_limbs(limbs);
3129            assert_eq!(
3130                value.wrapping_sqr(),
3131                value.wrapping_mul(value),
3132                "sqr != mul(self,self) for {limbs:?}"
3133            );
3134        }
3135
3136        // Width 4: 0, 1, MAX, single-limb, cross-limb, full-width.
3137        check::<4>([0, 0, 0, 0]);
3138        check::<4>([1, 0, 0, 0]);
3139        check::<4>([u64::MAX; 4]);
3140        check::<4>([0x1234_5678, 0, 0, 0]);
3141        check::<4>([u64::MAX, u64::MAX, 0, 0]);
3142        check::<4>([0xDEAD_BEEF_CAFE_F00D, 0x0123_4567_89AB_CDEF, 0xFEDC, 0x99]);
3143        // Carry-heavy: every limb all-ones but the top.
3144        check::<4>([u64::MAX, u64::MAX, u64::MAX, 0]);
3145        // Other widths / monomorphisations.
3146        check::<1>([u64::MAX]);
3147        check::<2>([u64::MAX, u64::MAX]);
3148        check::<6>([7, 8, 9, 10, 11, 12]);
3149        check::<8>([u64::MAX, 1, u64::MAX, 2, u64::MAX, 3, u64::MAX, 4]);
3150        // A pseudo-random sweep across width 5.
3151        let mut state = 0x9E37_79B9_7F4A_7C15u64;
3152        let mut next = || {
3153            state ^= state << 13;
3154            state ^= state >> 7;
3155            state ^= state << 17;
3156            state
3157        };
3158        for _ in 0..64 {
3159            check::<5>([next(), next(), next(), next(), next()]);
3160        }
3161    }
3162
3163    #[test]
3164    fn uint_sqr_cube_match_naive() {
3165        let x = Uint::<4>::from_limbs([123_456_789, 0, 0, 0]);
3166        assert_eq!(x.wrapping_sqr(), x.wrapping_mul(x));
3167        assert_eq!(x.wrapping_cube(), x.wrapping_mul(x).wrapping_mul(x));
3168
3169        // A value spanning two limbs, to exercise cross-limb products.
3170        let y = Uint::<4>::from_limbs([0xDEAD_BEEF_CAFE, 0x1234_5678, 0, 0]);
3171        assert_eq!(y.wrapping_sqr(), y.wrapping_mul(y));
3172        assert_eq!(y.wrapping_cube(), y.wrapping_mul(y).wrapping_mul(y));
3173    }
3174
3175    /// `root_int(k)` must return `floor(m^(1/k))` with the correct
3176    /// exactness flag: `root^k <= m < (root+1)^k`, and `exact` iff
3177    /// `root^k == m`. Checked against a brute-force reference for small
3178    /// `m` and `k in {2,3,5}` across widths, plus cross-checking k=2
3179    /// against the shipped isqrt and large perfect-power exactness.
3180    // Exercises Int<8> roots (isqrt/icbrt work widths) beyond the narrow
3181    // build's int-kernel scratch; runs where the wide tiers are compiled.
3182    #[cfg(feature = "_wide-support")]
3183    #[test]
3184    fn root_int_floor_and_exactness() {
3185        // Brute-force floor kth root of a small u128.
3186        fn brute(m: u128, k: u32) -> (u128, bool) {
3187            if m == 0 {
3188                return (0, true);
3189            }
3190            let mut root: u128 = 0;
3191            // Smallest root with (root+1)^k > m, capped to avoid overflow.
3192            while {
3193                let next = root + 1;
3194                next.checked_pow(k).is_some_and(|power| power <= m)
3195            } {
3196                root += 1;
3197            }
3198            (root, root.pow(k) == m)
3199        }
3200
3201        fn check<const N: usize>(m: u128, k: u32) {
3202            let lo = (m & 0xFFFF_FFFF_FFFF_FFFF) as u64;
3203            let hi = (m >> 64) as u64;
3204            let mut limbs = [0u64; N];
3205            limbs[0] = lo;
3206            if N > 1 {
3207                limbs[1] = hi;
3208            }
3209            let value = Uint::<N>::from_limbs(limbs);
3210            let (root, exact) = value.root_int(k);
3211            let (expected_root, expected_exact) = brute(m, k);
3212            let root_lo = root.as_limbs()[0] as u128
3213                | ((if N > 1 { root.as_limbs()[1] as u128 } else { 0 }) << 64);
3214            assert_eq!(root_lo, expected_root, "root mismatch for m={m}, k={k}");
3215            assert_eq!(exact, expected_exact, "exact flag mismatch for m={m}, k={k}");
3216
3217            // The defining bracket, computed in-width.
3218            let root_pow = root.pow(k);
3219            assert!(root_pow <= value, "root^k > m for m={m}, k={k}");
3220            let next = root.wrapping_add(Uint::<N>::ONE);
3221            // (root+1)^k overflowing the width still satisfies > m.
3222            if let Some(next_pow) = next.checked_pow(k) { assert!(next_pow > value, "(root+1)^k <= m for m={m}, k={k}") }
3223        }
3224
3225        let samples: [u128; 14] = [
3226            0,
3227            1,
3228            2,
3229            7,
3230            8,
3231            9,
3232            26,
3233            27,
3234            28,
3235            1000,
3236            1023,
3237            1024,
3238            1_000_000,
3239            u64::MAX as u128,
3240        ];
3241        for &m in &samples {
3242            for k in [2u32, 3, 5] {
3243                check::<4>(m, k);
3244                check::<2>(m, k);
3245                check::<8>(m, k);
3246            }
3247        }
3248
3249        // k=2 cross-check against shipped isqrt for a wide value.
3250        let big = Uint::<4>::from_limbs([0xFFFF_FFFF_FFFF_FFFF, 0x1234_5678, 0, 0]);
3251        assert_eq!(big.root_int(2).0, big.isqrt());
3252
3253        // Large exact perfect cube: (2^40)^3 = 2^120.
3254        let base = Uint::<4>::ONE.shl(40);
3255        let cube = base.wrapping_cube();
3256        let (r, exact) = cube.root_int(3);
3257        assert_eq!(r, base);
3258        assert!(exact);
3259        // One less than a perfect cube is not exact and floors down.
3260        let (r2, exact2) = cube.wrapping_sub(Uint::<4>::ONE).root_int(3);
3261        assert_eq!(r2, base.wrapping_sub(Uint::<4>::ONE));
3262        assert!(!exact2);
3263    }
3264
3265    #[test]
3266    fn uint_widen_zero_extends() {
3267        let a = Uint::<2>::from_limbs([7, 8]);
3268        let w = a.widen::<4>();
3269        assert_eq!(*w.as_limbs(), [7, 8, 0, 0]);
3270        assert_eq!(a.resize_n::<4>(), w);
3271    }
3272
3273    #[test]
3274    fn uint_narrow_checks_dropped_limbs() {
3275        let fits = Uint::<4>::from_limbs([7, 8, 0, 0]);
3276        assert_eq!(*fits.narrow::<2>().unwrap().as_limbs(), [7, 8]);
3277
3278        let too_big = Uint::<4>::from_limbs([7, 8, 1, 0]);
3279        assert!(too_big.narrow::<2>().is_none());
3280
3281        // widen → narrow round-trips losslessly.
3282        let a = Uint::<2>::from_limbs([3, 4]);
3283        assert_eq!(a.widen::<4>().narrow::<2>().unwrap(), a);
3284    }
3285
3286    #[test]
3287    fn int_widen_sign_extends() {
3288        // -1 is all-ones; sign-extension keeps it all-ones at any width.
3289        let neg = Int::<2>::from_i64(-1);
3290        assert_eq!(*neg.widen::<4>().as_limbs(), [u64::MAX; 4]);
3291        assert_eq!(neg.widen::<4>(), Int::<4>::from_i64(-1));
3292        // Positive values zero-extend.
3293        let pos = Int::<2>::from_i64(5);
3294        assert_eq!(*pos.widen::<4>().as_limbs(), [5, 0, 0, 0]);
3295    }
3296
3297    #[test]
3298    fn int_narrow_requires_sign_consistency() {
3299        // Negative value whose dropped limbs are all the sign fill: fits.
3300        let neg = Int::<4>::from_i64(-1);
3301        assert_eq!(neg.narrow::<2>().unwrap(), Int::<2>::from_i64(-1));
3302        // Positive magnitude with a non-sign high limb: does not fit.
3303        let big = Int::<4>::from_limbs([0, 0, 1, 0]);
3304        assert!(big.narrow::<2>().is_none());
3305        // Negative top bit but a dropped limb that isn't all-ones: no fit.
3306        let weird = Int::<4>::from_limbs([1, 0, 0, u64::MAX]);
3307        assert!(weird.narrow::<2>().is_none());
3308        // Small value round-trips.
3309        let p = Int::<2>::from_i64(42);
3310        assert_eq!(p.widen::<4>().narrow::<2>().unwrap(), p);
3311    }
3312
3313    #[test]
3314    fn int_resize_const_two_complement() {
3315        // Widen round-trips: positive zero-extends, value preserved.
3316        let pos = Int::<2>::from_i64(123);
3317        let wide: Int<4> = pos.resize_n::<4>();
3318        assert_eq!(*wide.as_limbs(), [123, 0, 0, 0]);
3319        assert_eq!(wide.resize_n::<2>(), pos);
3320
3321        // Sign-extend of negatives: high limbs all-ones, value preserved.
3322        let neg = Int::<2>::from_i64(-7);
3323        let wneg: Int<4> = neg.resize_n::<4>();
3324        assert_eq!(*wneg.as_limbs(), [(-7i64) as u64, u64::MAX, u64::MAX, u64::MAX]);
3325        assert_eq!(wneg, Int::<4>::from_i64(-7));
3326
3327        // Truncate drops the surplus high limbs.
3328        let v = Int::<4>::from_limbs([1, 2, 3, 4]);
3329        assert_eq!(*v.resize_n::<2>().as_limbs(), [1, 2]);
3330
3331        // try_narrow Some/None at the signed boundary.
3332        // -1 narrows: dropped limbs are the sign fill.
3333        assert_eq!(Int::<4>::from_i64(-1).try_narrow::<2>().unwrap(), Int::<2>::from_i64(-1));
3334        // A positive value with a set high limb does not fit.
3335        assert!(Int::<4>::from_limbs([0, 0, 1, 0]).try_narrow::<2>().is_none());
3336        // 2^63 is positive and fits Int<2> but NOT Int<1>: narrowing to
3337        // one limb would set the top bit, flipping it negative, and the
3338        // dropped high limb (0) is not the negative sign fill — so None.
3339        let too_big = Int::<2>::from_limbs([0x8000_0000_0000_0000, 0]);
3340        assert!(too_big.try_narrow::<1>().is_none());
3341        // i64::MIN genuinely fits Int<1>: try_narrow returns Some.
3342        let min2 = Int::<2>::from_i64(i64::MIN);
3343        assert_eq!(min2.try_narrow::<1>().unwrap(), Int::<1>::from_i64(i64::MIN));
3344        // Its widening then narrowing round-trips.
3345        assert_eq!(min2.resize_n::<4>().try_narrow::<2>().unwrap(), min2);
3346
3347        // Const evaluation smoke: resize_n is usable in const context.
3348        const W: Int<4> = Int::<2>::from_i64(-7).resize_n::<4>();
3349        assert_eq!(W, Int::<4>::from_i64(-7));
3350    }
3351
3352    #[test]
3353    fn bit_count_is_const() {
3354        // const-smoke: these compile only if the inherent methods are
3355        // `const fn`. Called fully-qualified so the inherent `&self` impl
3356        // is selected over the (non-const) `BigInt` trait method of the
3357        // same name, which takes `self` by value and would otherwise win
3358        // value-receiver method resolution.
3359        const Z: u32 = Int::<2>::leading_zeros(&Int::<2>::MAX);
3360        const BL: u32 = Int::<2>::bit_length(&Int::<2>::MAX);
3361        const UZ: u32 = Uint::<2>::leading_zeros(&Uint::<2>::MAX);
3362        const UBL: u32 = Uint::<2>::bit_length(&Uint::<2>::MAX);
3363        // Int<2>::MAX is positive with the sign bit clear: 1 leading zero,
3364        // bit_length = 127.
3365        assert_eq!(Z, 1);
3366        assert_eq!(BL, 127);
3367        // Uint<2>::MAX is all-ones: 0 leading zeros, full 128-bit length.
3368        assert_eq!(UZ, 0);
3369        assert_eq!(UBL, 128);
3370    }
3371
3372    #[test]
3373    fn int_cross_width_comparison() {
3374        // Equal values across widths compare ==.
3375        let a = Int::<1>::from_i64(5);
3376        let b = Int::<2>::from_i64(5);
3377        assert!(a == b);
3378        assert!(b == a);
3379        assert_eq!(a.partial_cmp(&b), Some(Ordering::Equal));
3380
3381        // Ordering across widths.
3382        let small = Int::<1>::from_i64(3);
3383        let big = Int::<2>::from_i64(100);
3384        assert!(small < big);
3385        assert!(big > small);
3386
3387        // A value only fitting the wider type still orders right: 2^70
3388        // lives in Int<2> (and up) but exceeds Int<1>; it must compare
3389        // greater than any Int<1>.
3390        let huge = Int::<2>::from_limbs([0, 64]); // 64 * 2^64 = 2^70
3391        assert!(huge > Int::<1>::MAX);
3392        assert!(Int::<1>::from_i64(-1) < huge);
3393
3394        // Negatives: -1 (Int<1>) vs 1 (Int<2>) — neg < pos.
3395        assert!(Int::<1>::from_i64(-1) < Int::<2>::from_i64(1));
3396        // Two negatives across widths: larger magnitude is the smaller.
3397        assert!(Int::<1>::from_i64(-100) < Int::<2>::from_i64(-3));
3398        assert!(Int::<2>::from_i64(-3) > Int::<1>::from_i64(-100));
3399        // Equal negatives across widths.
3400        assert!(Int::<1>::from_i64(-7) == Int::<2>::from_i64(-7));
3401
3402        // Same-type Ord still sorts a Vec.
3403        let mut v = vec![
3404            Int::<2>::from_i64(3),
3405            Int::<2>::from_i64(-10),
3406            Int::<2>::from_i64(0),
3407            Int::<2>::from_i64(7),
3408        ];
3409        v.sort();
3410        assert_eq!(
3411            v,
3412            vec![
3413                Int::<2>::from_i64(-10),
3414                Int::<2>::from_i64(0),
3415                Int::<2>::from_i64(3),
3416                Int::<2>::from_i64(7),
3417            ]
3418        );
3419    }
3420
3421    #[test]
3422    fn consts_and_round_trip() {
3423        assert_eq!(Uint::<4>::LIMBS, 4);
3424        assert_eq!(Uint::<4>::BITS, 256);
3425        assert_eq!(*Uint::<4>::ZERO.as_limbs(), [0, 0, 0, 0]);
3426        assert_eq!(*Uint::<4>::ONE.as_limbs(), [1, 0, 0, 0]);
3427        assert_eq!(*Uint::<4>::MAX.as_limbs(), [u64::MAX; 4]);
3428
3429        let v = Uint::<4>::from_limbs([7, 8, 9, 10]);
3430        assert_eq!(*v.as_limbs(), [7, 8, 9, 10]);
3431    }
3432
3433    #[test]
3434    fn widths_have_expected_bits() {
3435        assert_eq!(Int::<4>::BITS, 256);
3436        assert_eq!(Int::<64>::BITS, 4096);
3437        assert_eq!(Uint::<16>::LIMBS, 16);
3438    }
3439
3440    #[test]
3441    fn wrapping_sub_borrows_across_limbs() {
3442        // 2^64 - 1 ... computed as 0 - 1 wrapping, then check a clean borrow.
3443        let a = Uint::<4>::from_limbs([0, 1, 0, 0]);
3444        let d = a.wrapping_sub(Uint::<4>::ONE);
3445        assert_eq!(*d.as_limbs(), [u64::MAX, 0, 0, 0]);
3446
3447        // 0 - 1 wraps to all-ones (modulo 2^256).
3448        let wrap = Uint::<4>::ZERO.wrapping_sub(Uint::<4>::ONE);
3449        assert_eq!(*wrap.as_limbs(), [u64::MAX; 4]);
3450    }
3451
3452    #[test]
3453    fn unsigned_ordering() {
3454        let small = Uint::<4>::from_limbs([5, 0, 0, 0]);
3455        let big = Uint::<4>::from_limbs([0, 1, 0, 0]); // 2^64 > 5
3456        assert!(small < big);
3457        assert!(big > small);
3458        assert_eq!(small, small);
3459        assert!(Uint::<4>::ZERO < Uint::<4>::MAX);
3460        // round-trips through derived PartialOrd helpers
3461        assert_eq!(small.max(big), big);
3462    }
3463
3464    #[test]
3465    fn wrapping_add_carries_across_limbs() {
3466        // (2^64 - 1) + 1 carries into the next limb.
3467        let a = Uint::<4>::from_limbs([u64::MAX, 0, 0, 0]);
3468        let sum = a.wrapping_add(Uint::<4>::ONE);
3469        assert_eq!(*sum.as_limbs(), [0, 1, 0, 0]);
3470
3471        // All-ones + 1 wraps to zero (modulo 2^256).
3472        let wrap = Uint::<4>::MAX.wrapping_add(Uint::<4>::ONE);
3473        assert_eq!(*wrap.as_limbs(), [0, 0, 0, 0]);
3474    }
3475
3476    #[test]
3477    fn uint_wrapping_mul_cross_limb_product() {
3478        // 2^64 * 2^64 = 2^128 — lands exactly in limb 2.
3479        let a = Uint::<4>::from_limbs([0, 1, 0, 0]);
3480        let p = a.wrapping_mul(a);
3481        assert_eq!(*p.as_limbs(), [0, 0, 1, 0]);
3482
3483        // (2^64 - 1) * 3 = 3*2^64 - 3 = [u64::MAX - 2, 2, 0, 0].
3484        let m = Uint::<4>::from_limbs([u64::MAX, 0, 0, 0]);
3485        let three = Uint::<4>::from_limbs([3, 0, 0, 0]);
3486        let q = m.wrapping_mul(three);
3487        assert_eq!(*q.as_limbs(), [u64::MAX - 2, 2, 0, 0]);
3488
3489        // Multiply by one is identity.
3490        let v = Uint::<4>::from_limbs([7, 8, 9, 10]);
3491        assert_eq!(v.wrapping_mul(Uint::<4>::ONE), v);
3492    }
3493
3494    #[test]
3495    fn uint_wrapping_mul_truncates_modulo_width() {
3496        // 2^192 * 2^192 = 2^384, fully above 2^256 → wraps to zero.
3497        let hi = Uint::<4>::from_limbs([0, 0, 0, 1]);
3498        assert_eq!(hi.wrapping_mul(hi), Uint::<4>::ZERO);
3499
3500        // MAX * MAX mod 2^256: (2^256 - 1)^2 = 2^512 - 2^257 + 1.
3501        // mod 2^256 that is 1 (since 2^512 and 2^257 are both 0 mod
3502        // 2^256). So the low limb is 1, the rest zero.
3503        let r = Uint::<4>::MAX.wrapping_mul(Uint::<4>::MAX);
3504        assert_eq!(*r.as_limbs(), [1, 0, 0, 0]);
3505    }
3506
3507    #[test]
3508    fn uint_checked_add_sub_overflow() {
3509        assert_eq!(
3510            Uint::<4>::ONE.checked_add(Uint::<4>::ONE),
3511            Some(Uint::<4>::from_limbs([2, 0, 0, 0]))
3512        );
3513        // MAX + 1 overflows.
3514        assert_eq!(Uint::<4>::MAX.checked_add(Uint::<4>::ONE), None);
3515
3516        assert_eq!(
3517            Uint::<4>::from_limbs([5, 0, 0, 0]).checked_sub(Uint::<4>::from_limbs([3, 0, 0, 0])),
3518            Some(Uint::<4>::from_limbs([2, 0, 0, 0]))
3519        );
3520        // 0 - 1 underflows.
3521        assert_eq!(Uint::<4>::ZERO.checked_sub(Uint::<4>::ONE), None);
3522    }
3523
3524    #[test]
3525    fn uint_checked_mul_overflow() {
3526        // 2^64 * 2^64 = 2^128 fits in 256 bits.
3527        let a = Uint::<4>::from_limbs([0, 1, 0, 0]);
3528        assert_eq!(a.checked_mul(a), Some(Uint::<4>::from_limbs([0, 0, 1, 0])));
3529        // 2^192 * 2^192 overflows 256 bits.
3530        let hi = Uint::<4>::from_limbs([0, 0, 0, 1]);
3531        assert_eq!(hi.checked_mul(hi), None);
3532        // MAX * 2 overflows.
3533        assert_eq!(
3534            Uint::<4>::MAX.checked_mul(Uint::<4>::from_limbs([2, 0, 0, 0])),
3535            None
3536        );
3537    }
3538
3539    #[test]
3540    fn uint_div_rem_with_remainder() {
3541        // 1000 / 7 = 142 r 6.
3542        let n = Uint::<4>::from_limbs([1000, 0, 0, 0]);
3543        let d = Uint::<4>::from_limbs([7, 0, 0, 0]);
3544        assert_eq!(*n.wrapping_div(d).as_limbs(), [142, 0, 0, 0]);
3545        assert_eq!(*n.wrapping_rem(d).as_limbs(), [6, 0, 0, 0]);
3546
3547        // 2^128 / 2^64 = 2^64, remainder 0.
3548        let big = Uint::<4>::from_limbs([0, 0, 1, 0]);
3549        let by = Uint::<4>::from_limbs([0, 1, 0, 0]);
3550        assert_eq!(*big.wrapping_div(by).as_limbs(), [0, 1, 0, 0]);
3551        assert!(big.wrapping_rem(by).is_zero());
3552    }
3553
3554    #[test]
3555    #[should_panic(expected = "divide by zero")]
3556    fn uint_div_by_zero_panics() {
3557        let _ = Uint::<4>::ONE.wrapping_div(Uint::<4>::ZERO);
3558    }
3559
3560    #[test]
3561    fn uint_bitwise_ops() {
3562        let a = Uint::<4>::from_limbs([0b1100, 0xFF, 0, 0]);
3563        let b = Uint::<4>::from_limbs([0b1010, 0x0F, 0, 0]);
3564        assert_eq!(*(a & b).as_limbs(), [0b1000, 0x0F, 0, 0]);
3565        assert_eq!(*(a | b).as_limbs(), [0b1110, 0xFF, 0, 0]);
3566        assert_eq!(*(a ^ b).as_limbs(), [0b0110, 0xF0, 0, 0]);
3567        assert_eq!(*(!Uint::<4>::ZERO).as_limbs(), [u64::MAX; 4]);
3568    }
3569
3570    #[test]
3571    fn uint_shifts() {
3572        let one = Uint::<4>::ONE;
3573        // 1 << 64 lands in limb 1.
3574        assert_eq!(*(one << 64).as_limbs(), [0, 1, 0, 0]);
3575        // 1 << 130 = limb 2 bit 2.
3576        assert_eq!(*(one << 130).as_limbs(), [0, 0, 0b100, 0]);
3577        // Right shift back.
3578        let v = Uint::<4>::from_limbs([0, 0, 0b100, 0]);
3579        assert_eq!(*(v >> 130).as_limbs(), [1, 0, 0, 0]);
3580        // Shift past the width drops everything.
3581        assert_eq!(one << 256, Uint::<4>::ZERO);
3582    }
3583
3584    #[test]
3585    fn uint_is_zero_bitlen_leading_zeros() {
3586        assert!(Uint::<4>::ZERO.is_zero());
3587        assert!(!Uint::<4>::ONE.is_zero());
3588        assert_eq!(Uint::<4>::ZERO.bit_length(), 0);
3589        assert_eq!(Uint::<4>::ONE.bit_length(), 1);
3590        // 2^64 has bit length 65.
3591        let b = Uint::<4>::from_limbs([0, 1, 0, 0]);
3592        assert_eq!(b.bit_length(), 65);
3593        assert_eq!(b.leading_zeros(), 256 - 65);
3594        assert_eq!(Uint::<4>::ZERO.leading_zeros(), 256);
3595        assert_eq!(Uint::<4>::MAX.leading_zeros(), 0);
3596    }
3597
3598    #[test]
3599    fn uint_operator_traits_delegate() {
3600        let a = Uint::<4>::from_limbs([10, 0, 0, 0]);
3601        let b = Uint::<4>::from_limbs([3, 0, 0, 0]);
3602        assert_eq!(*(a + b).as_limbs(), [13, 0, 0, 0]);
3603        assert_eq!(*(a - b).as_limbs(), [7, 0, 0, 0]);
3604        assert_eq!(*(a * b).as_limbs(), [30, 0, 0, 0]);
3605    }
3606
3607    #[test]
3608    fn int_from_i64_sign_extends() {
3609        // Positive: only the low limb is set.
3610        assert_eq!(*Int::<4>::from_i64(5).as_limbs(), [5, 0, 0, 0]);
3611        // -1 sign-extends to all-ones.
3612        assert_eq!(*Int::<4>::from_i64(-1).as_limbs(), [u64::MAX; 4]);
3613        // -2 → low limb u64::MAX - 1, upper limbs all-ones.
3614        assert_eq!(
3615            *Int::<4>::from_i64(-2).as_limbs(),
3616            [u64::MAX - 1, u64::MAX, u64::MAX, u64::MAX]
3617        );
3618        assert!(Int::<4>::from_i64(-1).is_negative());
3619        assert!(Int::<4>::from_i64(1).is_positive());
3620        assert!(Int::<4>::from_i64(0).is_zero());
3621    }
3622
3623    #[test]
3624    fn int_wrapping_neg_two_complement() {
3625        let five = Int::<4>::from_i64(5);
3626        let neg_five = five.wrapping_neg();
3627        assert_eq!(neg_five, Int::<4>::from_i64(-5));
3628        // Negating twice returns the original.
3629        assert_eq!(neg_five.wrapping_neg(), five);
3630        // -1 negates to 1.
3631        assert_eq!(Int::<4>::from_i64(-1).wrapping_neg(), Int::<4>::ONE);
3632        // Neg operator delegates.
3633        assert_eq!(-five, neg_five);
3634    }
3635
3636    #[test]
3637    fn int_add_sub_mul_with_signs() {
3638        let a = Int::<4>::from_i64(7);
3639        let b = Int::<4>::from_i64(-3);
3640        // 7 + (-3) = 4
3641        assert_eq!(a.wrapping_add(b), Int::<4>::from_i64(4));
3642        // 7 - (-3) = 10
3643        assert_eq!(a.wrapping_sub(b), Int::<4>::from_i64(10));
3644        // 7 * (-3) = -21
3645        assert_eq!(a.wrapping_mul(b), Int::<4>::from_i64(-21));
3646        // (-3) * (-3) = 9
3647        assert_eq!(b.wrapping_mul(b), Int::<4>::from_i64(9));
3648        // operator delegation
3649        assert_eq!(a + b, Int::<4>::from_i64(4));
3650        assert_eq!(a - b, Int::<4>::from_i64(10));
3651        assert_eq!(a * b, Int::<4>::from_i64(-21));
3652    }
3653
3654    #[test]
3655    fn int_mul_crosses_limbs_with_sign() {
3656        // 2^64 * (-1) = -2^64.
3657        let big = Int::<4>::from_i64(0).wrapping_add(Int::<4>::from_limbs([0, 1, 0, 0]));
3658        let neg = big.wrapping_mul(Int::<4>::from_i64(-1));
3659        assert_eq!(neg, big.wrapping_neg());
3660        // -2^64 should be [0, u64::MAX, u64::MAX, u64::MAX].
3661        assert_eq!(*neg.as_limbs(), [0, u64::MAX, u64::MAX, u64::MAX]);
3662    }
3663
3664    #[test]
3665    fn int_abs_signum() {
3666        assert_eq!(Int::<4>::from_i64(-9).abs(), Int::<4>::from_i64(9));
3667        assert_eq!(Int::<4>::from_i64(9).abs(), Int::<4>::from_i64(9));
3668        assert_eq!(Int::<4>::from_i64(-9).signum(), -1);
3669        assert_eq!(Int::<4>::from_i64(9).signum(), 1);
3670        assert_eq!(Int::<4>::from_i64(0).signum(), 0);
3671    }
3672
3673    #[test]
3674    fn int_from_i128_round_trips() {
3675        for v in [
3676            0i128,
3677            1,
3678            -1,
3679            42,
3680            -42,
3681            i64::MAX as i128,
3682            i64::MIN as i128,
3683            i128::MAX,
3684            i128::MIN,
3685            123_456_789_012_345_678,
3686        ] {
3687            let a = Int::<4>::from_i128(v);
3688            assert_eq!(a.to_i128_checked(), Some(v), "round-trip i128 {v}");
3689            let b = Int::<8>::from_i128(v);
3690            assert_eq!(b.to_i128_checked(), Some(v), "round-trip i128 {v} (N=8)");
3691        }
3692        // from_u128 of a value above i128::MAX is not representable as i128.
3693        let big = Int::<4>::from_u128(u128::MAX);
3694        assert_eq!(big.to_i128_checked(), None);
3695        assert_eq!(big.to_u128_checked(), Some(u128::MAX));
3696        // Negative has no u128.
3697        assert_eq!(Int::<4>::from_i128(-1).to_u128_checked(), None);
3698    }
3699
3700    #[test]
3701    fn int_from_str_radix_and_display_round_trip() {
3702        let cases = [
3703            "0",
3704            "1",
3705            "-1",
3706            "42",
3707            "-42",
3708            "1000000000000000000000",
3709            "-340282366920938463463374607431768211455",
3710        ];
3711        for s in cases {
3712            let v = Int::<4>::from_str_radix(s, 10).unwrap();
3713            assert_eq!(format!("{v}"), s, "display round-trip {s}");
3714            // FromStr delegates to from_str_radix(_, 10).
3715            let v2: Int<4> = s.parse().unwrap();
3716            assert_eq!(v, v2);
3717        }
3718        // Non-base-10 and malformed input reject.
3719        assert!(Int::<4>::from_str_radix("10", 16).is_err());
3720        assert!(Int::<4>::from_str_radix("12x", 10).is_err());
3721        assert!(Int::<4>::from_str_radix("", 10).is_err());
3722        assert!(Int::<4>::from_str_radix("-", 10).is_err());
3723    }
3724
3725    #[test]
3726    fn int_div_rem_signs_match_truncating() {
3727        // Truncating division: quotient truncates toward zero, remainder
3728        // carries the sign of the dividend.
3729        let cases: [(i128, i128); 6] = [
3730            (1000, 7),
3731            (-1000, 7),
3732            (1000, -7),
3733            (-1000, -7),
3734            (7, 1000),
3735            (-7, 1000),
3736        ];
3737        for (a, b) in cases {
3738            let (q, r) = Int::<4>::from_i128(a).div_rem(Int::<4>::from_i128(b));
3739            assert_eq!(q.to_i128_checked(), Some(a / b), "quot {a}/{b}");
3740            assert_eq!(r.to_i128_checked(), Some(a % b), "rem {a}%{b}");
3741        }
3742    }
3743
3744    #[test]
3745    #[should_panic(expected = "divide by zero")]
3746    fn int_div_rem_by_zero_panics() {
3747        let _ = Int::<4>::ONE.div_rem(Int::<4>::ZERO);
3748    }
3749
3750    #[test]
3751    fn int_bit_reads_twos_complement() {
3752        let v = Int::<4>::from_i128(0b1010);
3753        assert!(!v.bit(0));
3754        assert!(v.bit(1));
3755        assert!(!v.bit(2));
3756        assert!(v.bit(3));
3757        // Above the value's set bits: clear for positive.
3758        assert!(!v.bit(200));
3759        // Negative: high bits read as the sign extension (all ones).
3760        let neg = Int::<4>::from_i128(-1);
3761        assert!(neg.bit(0));
3762        assert!(neg.bit(255));
3763        assert!(neg.bit(1000)); // out of range → sign bit
3764    }
3765
3766    #[test]
3767    fn int_mul_u64_matches_wide_mul() {
3768        let v = Int::<4>::from_i128(123_456_789);
3769        assert_eq!(
3770            v.mul_u64(1000),
3771            Int::<4>::from_i128(123_456_789_000)
3772        );
3773        // Sign preserved.
3774        let n = Int::<4>::from_i128(-123_456_789);
3775        assert_eq!(
3776            n.mul_u64(1000),
3777            Int::<4>::from_i128(-123_456_789_000)
3778        );
3779        // Times zero / one.
3780        assert_eq!(v.mul_u64(0), Int::<4>::ZERO);
3781        assert_eq!(v.mul_u64(1), v);
3782    }
3783
3784    #[test]
3785    #[should_panic(expected = "mul overflow")]
3786    fn int_mul_u64_overflow_panics() {
3787        // max_value * 2 overflows the signed range.
3788        let _ = Int::<4>::max_value().mul_u64(2);
3789    }
3790
3791    #[test]
3792    fn int_div_rem_operators_match_div_rem() {
3793        // The Div / Rem operators must agree with the inherent div_rem,
3794        // which is what BigInt requires as supertraits.
3795        let a = Int::<4>::from_i128(-1000);
3796        let b = Int::<4>::from_i128(7);
3797        assert_eq!(a / b, Int::<4>::from_i128(-1000 / 7));
3798        assert_eq!(a % b, Int::<4>::from_i128(-1000 % 7));
3799        let (q, r) = a.div_rem(b);
3800        assert_eq!(a / b, q);
3801        assert_eq!(a % b, r);
3802    }
3803
3804    // Exercises Int<8> isqrt (work width 9) beyond the narrow build's scratch.
3805    #[cfg(feature = "_wide-support")]
3806    #[test]
3807    fn int_wide_storage_surface() {
3808        use crate::int::types::traits::BigInt;
3809        fn exercises<T: BigInt>() {
3810            assert!(<T as BigInt>::ZERO == <T as BigInt>::from_i128(0));
3811            assert!(<T as BigInt>::ONE == <T as BigInt>::from_i128(1));
3812            assert!(<T as BigInt>::TEN == <T as BigInt>::from_i128(10));
3813
3814            let twelve = <T as BigInt>::from_i128(12);
3815            let three = <T as BigInt>::from_i128(3);
3816            // pow / isqrt
3817            assert!(three.pow(3) == <T as BigInt>::from_i128(27));
3818            assert!(<T as BigInt>::from_i128(144).isqrt() == <T as BigInt>::from_i128(12));
3819            // div_rem
3820            let (q, r) = twelve.div_rem(<T as BigInt>::from_i128(5));
3821            assert!(q == <T as BigInt>::from_i128(2));
3822            assert!(r == <T as BigInt>::from_i128(2));
3823            // bit / leading_zeros
3824            assert!(twelve.bit(2) && twelve.bit(3) && !twelve.bit(0));
3825            assert!(<T as BigInt>::ONE.leading_zeros() == <T as BigInt>::BITS - 1);
3826            // mul_u64 / f64 round-trips
3827            assert!(twelve.mul_u64(10) == <T as BigInt>::from_i128(120));
3828            assert!(twelve.to_f64() == 12.0);
3829            assert!(<T as BigInt>::from_f64_val(7.9) == <T as BigInt>::from_i128(7));
3830        }
3831        exercises::<Int<4>>();
3832        exercises::<Int<8>>();
3833
3834        // resize_to widens/narrows across the family.
3835        let v = Int::<4>::from_i128(-123_456_789);
3836        let w: Int<8> = BigInt::resize_to(v);
3837        assert_eq!(w.to_i128_checked(), Some(-123_456_789));
3838        let back: Int<4> = BigInt::resize_to(w);
3839        assert_eq!(back, v);
3840    }
3841
3842    // Perfect-square round-trip at Int<8> exceeds the narrow build's scratch.
3843    #[cfg(feature = "_wide-support")]
3844    #[test]
3845    fn int_isqrt_matches_uint_magnitude() {
3846        use crate::int::types::traits::BigInt;
3847        // Signed isqrt is the magnitude isqrt (macro parity).
3848        let n = Int::<4>::from_i128(1_000_000_000_000);
3849        let r = BigInt::isqrt(n);
3850        assert_eq!(r, Int::<4>::from_i128(1_000_000));
3851        // Perfect square round-trip at width 8.
3852        let big = Int::<8>::from_i128(987_654_321);
3853        let sq = big.checked_mul(big).unwrap();
3854        assert_eq!(BigInt::isqrt(sq), big);
3855    }
3856
3857    /// `Uint<N>::sqr` must equal `x * x` at every tested width; the policy
3858    /// dispatcher must agree with `wrapping_sqr`.
3859    #[test]
3860    fn uint_sqr_policy_matches_wrapping_sqr() {
3861        fn check<const N: usize>(limbs: [u64; N]) {
3862            let value = Uint::<N>::from_limbs(limbs);
3863            assert_eq!(value.sqr(), value.wrapping_sqr(), "sqr mismatch at {limbs:?}");
3864            assert_eq!(value.sqr(), value.wrapping_mul(value), "sqr != x*x at {limbs:?}");
3865        }
3866        // Width 1
3867        check::<1>([0]);
3868        check::<1>([1]);
3869        check::<1>([u64::MAX]);
3870        check::<1>([12345]);
3871        // Width 2
3872        check::<2>([0, 0]);
3873        check::<2>([1, 0]);
3874        check::<2>([u64::MAX, u64::MAX]);
3875        check::<2>([0xDEAD_BEEF, 0x1234]);
3876        // Width 4
3877        check::<4>([u64::MAX, u64::MAX, u64::MAX, u64::MAX]);
3878        check::<4>([0x1234_5678_9ABC_DEF0, 0xFEDC_BA98, 0, 0]);
3879        // Width 6
3880        check::<6>([7, 8, 9, 10, 11, 12]);
3881    }
3882
3883    /// `Uint<N>::cube` must equal `x * x * x` at every tested width.
3884    #[test]
3885    fn uint_cube_policy_matches_wrapping_cube() {
3886        fn check<const N: usize>(limbs: [u64; N]) {
3887            let value = Uint::<N>::from_limbs(limbs);
3888            assert_eq!(value.cube(), value.wrapping_cube(), "cube mismatch at {limbs:?}");
3889            assert_eq!(
3890                value.cube(),
3891                value.wrapping_mul(value).wrapping_mul(value),
3892                "cube != x*x*x at {limbs:?}"
3893            );
3894        }
3895        check::<1>([0]);
3896        check::<1>([1]);
3897        check::<1>([3]);
3898        check::<1>([u64::MAX]);
3899        check::<2>([0, 0]);
3900        check::<2>([1, 0]);
3901        check::<2>([100, 0]);
3902        check::<2>([u64::MAX, u64::MAX]);
3903        check::<4>([5, 0, 0, 0]);
3904        check::<4>([0x1234, 0x5678, 0, 0]);
3905    }
3906
3907    /// `Int<N>::sqr` and `Int<N>::cube` must match their wrapping siblings.
3908    #[test]
3909    fn int_sqr_cube_match_wrapping() {
3910        fn check<const N: usize>(value: i128) {
3911            let int = Int::<N>::from_i128(value);
3912            assert_eq!(int.sqr(), int.wrapping_sqr(), "int sqr mismatch v={value}");
3913            assert_eq!(int.cube(), int.wrapping_cube(), "int cube mismatch v={value}");
3914        }
3915        for value in [0i128, 1, -1, 12, -12, 100, -100, 1_000_000, -1_000_000] {
3916            check::<4>(value);
3917            check::<8>(value);
3918        }
3919    }
3920
3921    /// `Uint<N>::icbrt` must return `floor(x^(1/3))` for a range of
3922    /// values: perfect cubes, non-cubes, 0, 1, and large values.
3923    #[test]
3924    fn uint_icbrt_floor_correctness() {
3925        // Brute-force floor cube-root of a u64, used as oracle for small inputs.
3926        fn brute_cbrt(value: u64) -> u64 {
3927            if value == 0 {
3928                return 0;
3929            }
3930            let mut root: u64 = 0;
3931            while (root + 1).checked_mul((root + 1) * (root + 1) + root + 1)
3932                .is_some_and(|power| power <= value)
3933                || (root + 1).checked_pow(3).is_some_and(|power| power <= value)
3934            {
3935                root += 1;
3936            }
3937            root
3938        }
3939
3940        fn check<const N: usize>(value: u64) {
3941            let uint = Uint::<N>::from_u64(value);
3942            let root = uint.icbrt();
3943            let root_u64 = root.as_limbs()[0];
3944            let expected = brute_cbrt(value);
3945            assert_eq!(root_u64, expected, "icbrt({value}) = {root_u64}, expected {expected}");
3946            // Higher limbs of root must be zero for a u64 input.
3947            for i in 1..N {
3948                assert_eq!(root.as_limbs()[i], 0, "icbrt({value}) high limb {i} nonzero");
3949            }
3950        }
3951
3952        // Perfect cubes.
3953        for value in [0u64, 1, 8, 27, 64, 125, 216, 343, 512, 729, 1000,
3954                      1_000_000_000u64, 8_000_000_000u64] {
3955            check::<1>(value);
3956            check::<2>(value);
3957            check::<4>(value);
3958        }
3959
3960        // Non-cubes (floor must be correct).
3961        for value in [2u64, 3, 4, 5, 6, 7, 9, 10, 26, 28, 63, 65, 999,
3962                      1_000_000_001u64] {
3963            check::<1>(value);
3964            check::<2>(value);
3965            check::<4>(value);
3966        }
3967
3968        // Boundary cases: 0 and 1.
3969        check::<1>(0);
3970        check::<1>(1);
3971        check::<4>(0);
3972        check::<4>(1);
3973
3974        // Large 64-bit values.
3975        for value in [u64::MAX, u64::MAX - 1, u64::MAX - 100, 1 << 60, 1 << 48] {
3976            check::<2>(value);
3977            check::<4>(value);
3978        }
3979    }
3980
3981    /// `Uint<N>::icbrt` for values spanning multiple limbs (N >= 3 path).
3982    /// The cube root of a 2-limb value still fits in a single u64; we
3983    /// verify the floor identity `r³ <= x < (r+1)³`.
3984    #[test]
3985    fn uint_icbrt_wide_floor_identity() {
3986        fn check_wide<const N: usize>(limbs: [u64; N]) {
3987            let value = Uint::<N>::from_limbs(limbs);
3988            let root = value.icbrt();
3989            // Verify r³ <= x.
3990            let root_cubed = root.wrapping_pow(3);
3991            assert!(
3992                root_cubed <= value,
3993                "icbrt violated: r³ > x for {limbs:?}"
3994            );
3995            // Verify (r+1)³ > x (i.e. r is the floor).
3996            let next_root = root.wrapping_add(Uint::<N>::ONE);
3997            let next_root_cubed = next_root.wrapping_pow(3);
3998            // (r+1)³ wraps to 0 only if r+1 itself wraps (i.e. r == MAX),
3999            // which for a cube root of a representable value cannot happen.
4000            assert!(
4001                next_root_cubed > value || next_root == Uint::<N>::ZERO,
4002                "icbrt not floor: (r+1)³ <= x for {limbs:?}"
4003            );
4004        }
4005
4006        // Perfect cubes in 2 limbs (root fits one limb).
4007        // 10^18 cubed = 10^54 — too large; use modest values.
4008        // 1_000_000^3 = 10^18, which fits 2 limbs (u64 goes to ~1.8 * 10^19).
4009        let base = 1_000_000u64;
4010        let base_cubed = base * base * base; // 10^18, fits u64
4011        check_wide::<4>([base_cubed, 0, 0, 0]);
4012        check_wide::<4>([base_cubed + 1, 0, 0, 0]);
4013        check_wide::<4>([base_cubed - 1, 0, 0, 0]);
4014
4015        // A 2-limb value: combine two u64 halves.
4016        let hi = 1u64;
4017        let lo = 0u64;
4018        check_wide::<4>([lo, hi, 0, 0]); // 2^64
4019
4020        check_wide::<4>([u64::MAX, u64::MAX, 0, 0]); // near 2^128
4021
4022        // Width 6 (N >= 3, Newton path for sure).
4023        check_wide::<6>([base_cubed, 0, 0, 0, 0, 0]);
4024        check_wide::<6>([u64::MAX, u64::MAX, u64::MAX, 0, 0, 0]); // near 2^192
4025    }
4026
4027    /// `Int<N>::icbrt` must return the unsigned cube root of the magnitude.
4028    #[test]
4029    fn int_icbrt_magnitude() {
4030        for v in [0i128, 1, -1, 8, -8, 27, -27, 1000, -1000] {
4031            let x = Int::<4>::from_i128(v);
4032            let expected = Int::<4>::from_i128((v.unsigned_abs() as f64).cbrt() as i128);
4033            let got = x.icbrt();
4034            assert_eq!(got, expected, "int icbrt({v}) mismatch");
4035        }
4036    }
4037
4038    #[test]
4039    fn int_wideint_mag_sign_round_trips() {
4040        use crate::int::types::traits::BigInt;
4041        // mag_into_u128 / from_mag_sign_u128 round-trip for signed
4042        // values, including the magnitude + sign split.
4043        for v in [0i128, 1, -1, 123_456_789_012_345_678, -987_654_321] {
4044            let a = Int::<4>::from_i128(v);
4045            let mut mag = [0u128; 2];
4046            let neg = a.mag_into_u128(&mut mag);
4047            assert_eq!(neg, a.is_negative());
4048            assert_eq!(Int::<4>::from_mag_sign_u128(&mag, neg), a, "mag/sign {v}");
4049        }
4050        // U128_LIMBS = ceil(N/2): even and odd N.
4051        assert_eq!(<Int<4> as BigInt>::U128_LIMBS, 2);
4052        assert_eq!(<Int<3> as BigInt>::U128_LIMBS, 2);
4053        assert_eq!(<Int<8> as BigInt>::U128_LIMBS, 4);
4054
4055        // mag_into_u128 / from_mag_sign_u128 round-trip (the hot-path
4056        // buffer bypass), including the odd-N tail at N=3.
4057        let v3 = Int::<3>::from_i128(-170_141_183_460_469_231_731);
4058        let mut buf = [0u128; 2];
4059        let neg = v3.mag_into_u128(&mut buf);
4060        assert_eq!(Int::<3>::from_mag_sign_u128(&buf, neg), v3);
4061
4062        let v4 = Int::<4>::from_i128(i128::MIN);
4063        let mut buf4 = [0u128; 2];
4064        let neg4 = v4.mag_into_u128(&mut buf4);
4065        assert_eq!(Int::<4>::from_mag_sign_u128(&buf4, neg4), v4);
4066    }
4067
4068    #[test]
4069    fn int_to_from_f64_and_negate_ten() {
4070        assert_eq!(Int::<4>::from_i64(5).to_f64(), 5.0);
4071        assert_eq!(Int::<4>::from_i64(-5).to_f64(), -5.0);
4072        assert_eq!(Int::<4>::from_f64(42.9), Int::<4>::from_i64(42));
4073        assert_eq!(Int::<4>::from_f64(-42.9), Int::<4>::from_i64(-42));
4074        // Non-finite maps to ZERO.
4075        assert_eq!(Int::<4>::from_f64(f64::NAN), Int::<4>::ZERO);
4076        // negate is wrapping_neg; TEN const is 10.
4077        assert_eq!(Int::<4>::from_i64(5).negate(), Int::<4>::from_i64(-5));
4078        assert_eq!(Int::<4>::TEN, Int::<4>::from_i64(10));
4079        // from_limbs_le / limbs_le round-trip.
4080        let v = Int::<4>::from_i128(-9_876_543_210);
4081        assert_eq!(Int::<4>::from_limbs_le(v.limbs_le()), v);
4082    }
4083
4084    #[test]
4085    fn int_signed_ordering() {
4086        let neg = Int::<4>::from_i64(-5);
4087        let zero = Int::<4>::ZERO;
4088        let pos = Int::<4>::from_i64(5);
4089        // Negative < zero < positive even though -5's limbs are larger
4090        // unsigned than 5's.
4091        assert!(neg < zero);
4092        assert!(zero < pos);
4093        assert!(neg < pos);
4094        // Two negatives compare by magnitude with sign accounted for.
4095        assert!(Int::<4>::from_i64(-10) < Int::<4>::from_i64(-1));
4096        assert_eq!(neg.max(pos), pos);
4097        assert_eq!(neg.min(pos), neg);
4098        assert_eq!(pos.cmp(&pos), Ordering::Equal);
4099    }
4100}
4101
4102/// Feasibility proof for the unified narrow-tier divide path: the same
4103/// `widen_mul` → `div_wide_pow10` pipeline the wide tiers already
4104/// run must produce the correct `(a · b) / 10^scale` at the narrow
4105/// limb widths `N = 1` (`Int64`) and `N = 2` (`Int128`) that the
4106/// D18/D38-unify steps will rewire onto. This locks in the
4107/// `widen_mul::<wider>` then `div_wide_pow10::<wider>`
4108/// composition before any decimal type is rewired; it is additive and
4109/// asserts only — no behaviour is changed here.
4110#[cfg(all(test, feature = "wide"))]
4111mod unified_mg_feasibility {
4112    use super::Int;
4113    use crate::algos::support::mg_divide::div_wide_pow10;
4114    use crate::int::types::compute_limbs::{ComputeLimbs, Limbs};
4115    use crate::int::types::traits::BigInt;
4116    use crate::support::rounding::RoundingMode;
4117
4118    /// `(lhs · rhs) / 10^scale` through the unified pipeline, computed as
4119    /// `Int<N>::widen_mul::<Int<M>>` (full product into the wider type)
4120    /// then `div_wide_pow10::<Int<M>>`. The wider type's u128 magnitude
4121    /// width is read from its scratch carrier's [`ComputeLimbs`] buffer
4122    /// (`single_u128`) inside the divide — the `Limbs<N>` carrier IS the
4123    /// mechanism for the wider work intermediate, so no work-width const
4124    /// parameter is named. Returns the scaled wider-width quotient.
4125    fn scaled<const N: usize, const M: usize>(lhs: Int<N>, rhs: Int<N>, scale: u32) -> Int<M>
4126    where
4127        Limbs<N>: ComputeLimbs,
4128        Int<M>: BigInt,
4129        Limbs<M>: ComputeLimbs,
4130        <Int<M> as BigInt>::Scratch: ComputeLimbs,
4131    {
4132        let prod: Int<M> = lhs.widen_mul::<Int<M>>(rhs);
4133        div_wide_pow10::<Int<M>>(prod, scale, RoundingMode::HalfToEven)
4134    }
4135
4136    /// N = 2 → widen to N = 4, scale 5 (the plan's anchor case).
4137    #[test]
4138    fn n2_widen4_scale5() {
4139        let a = Int::<2>::from_i64(123456789);
4140        let b = Int::<2>::from_i64(987654321);
4141        let got = scaled::<2, 4>(a, b, 5);
4142        assert_eq!(got, Int::<4>::from_i64(1219326311126));
4143    }
4144
4145    /// N = 1 → widen to N = 2, scale 3 (the plan's anchor case).
4146    #[test]
4147    fn n1_widen2_scale3() {
4148        let a = Int::<1>::from_i64(123456);
4149        let b = Int::<1>::from_i64(654321);
4150        let got = scaled::<1, 2>(a, b, 3);
4151        assert_eq!(got, Int::<2>::from_i64(80779853));
4152    }
4153
4154    /// Round-trip: `(x · 10^k) / 10^k == x` at both narrow widths. The
4155    /// widen_mul forms `x · 10^k` exactly in the wider type and the MG
4156    /// divide undoes it with no residue.
4157    #[test]
4158    fn round_trip_mul_then_div() {
4159        // N = 1 → 2: x = 4242, k = 4.
4160        let x1 = 4242i64;
4161        let ten_pow_4 = Int::<1>::from_i64(10_000);
4162        let rt1 = scaled::<1, 2>(Int::<1>::from_i64(x1), ten_pow_4, 4);
4163        assert_eq!(rt1, Int::<2>::from_i64(x1));
4164
4165        // N = 2 → 4: x = 9_876_543_210, k = 7.
4166        let x2 = 9_876_543_210i64;
4167        let ten_pow_7 = Int::<2>::from_i64(10_000_000);
4168        let rt2 = scaled::<2, 4>(Int::<2>::from_i64(x2), ten_pow_7, 7);
4169        assert_eq!(rt2, Int::<4>::from_i64(x2));
4170    }
4171
4172    /// Scale-0 identity: callers short-circuit `scale == 0` as a no-op
4173    /// (`div_wide_pow10` is only ever invoked for `1..=38`), so the
4174    /// scaled value at scale 0 is exactly the full widen_mul product.
4175    /// This locks that contract for the narrow widths.
4176    #[test]
4177    fn scale_zero_is_widen_mul_identity() {
4178        // N = 1 → 2.
4179        let a1 = Int::<1>::from_i64(123456);
4180        let b1 = Int::<1>::from_i64(654321);
4181        let prod1: Int<2> = a1.widen_mul::<Int<2>>(b1);
4182        assert_eq!(prod1, Int::<2>::from_i64(123456 * 654321));
4183
4184        // N = 2 → 4.
4185        let a2 = Int::<2>::from_i64(123456789);
4186        let b2 = Int::<2>::from_i64(987654321);
4187        let prod2: Int<4> = a2.widen_mul::<Int<4>>(b2);
4188        assert_eq!(prod2, Int::<4>::from_i64(123456789i64 * 987654321i64));
4189    }
4190
4191    /// Max-operand case at each narrow width: the widest product the
4192    /// source width can hold must widen and scale without truncation.
4193    #[test]
4194    fn max_operand_each_width() {
4195        // N = 1: u64::MAX-ish operands. Use the largest i64 magnitudes
4196        // whose product the widen_mul (into N = 2) holds exactly, then
4197        // scale by 10^9 and check against the u128 reference.
4198        let a1 = Int::<1>::from_i64(i64::MAX);
4199        let b1 = Int::<1>::from_i64(i64::MAX);
4200        let got1 = scaled::<1, 2>(a1, b1, 9);
4201        let exact1 = (i64::MAX as i128) * (i64::MAX as i128);
4202        let pow9 = 1_000_000_000i128;
4203        let q1 = exact1 / pow9;
4204        let r1 = exact1 % pow9;
4205        // HalfToEven bump when the remainder is past the halfway point.
4206        let half = pow9 / 2;
4207        let expect1 = if r1 > half || (r1 == half && (q1 & 1) == 1) {
4208            q1 + 1
4209        } else {
4210            q1
4211        };
4212        assert_eq!(got1, Int::<2>::from_i128(expect1));
4213
4214        // N = 2: the most-positive signed operand (2^127 - 1) squared,
4215        // widened into N = 4, then scaled by 10^20. Compare against the
4216        // exact reference reconstructed from the full 256-bit product.
4217        let big = Int::<2>::MAX; // 2^127 - 1
4218        let got2 = scaled::<2, 4>(big, big, 20);
4219        let prod2: Int<4> = big.widen_mul::<Int<4>>(big);
4220        // Exact (2^127 - 1)^2 / 10^20, HalfToEven, via the wider type's
4221        // own div_rem against 10^20 built in Int<4>.
4222        let pow20 = Int::<4>::TEN.pow(20);
4223        let (q2, r2) = prod2.div_rem(pow20);
4224        let half20 = pow20.div_rem(Int::<4>::from_i64(2)).0;
4225        let expect2 = match r2.cmp(&half20) {
4226            core::cmp::Ordering::Greater => q2.wrapping_add(Int::<4>::ONE),
4227            core::cmp::Ordering::Equal => {
4228                if (q2.as_limbs()[0] & 1) == 1 {
4229                    q2.wrapping_add(Int::<4>::ONE)
4230                } else {
4231                    q2
4232                }
4233            }
4234            core::cmp::Ordering::Less => q2,
4235        };
4236        assert_eq!(got2, expect2);
4237    }
4238}
4239
4240#[cfg(test)]
4241mod bit_count_contract_tests {
4242    use super::{Int, Uint};
4243
4244    /// `Int<N>`'s four bit-count methods must match the `iN`
4245    /// two's-complement contract at every edge, and `bit_length` must
4246    /// stay magnitude-based. Where the value fits a primitive we assert
4247    /// against `i64`/`i128`; otherwise against the known answer.
4248    #[test]
4249    fn int_bit_counts_match_signed_contract_at_edges() {
4250        // ── Int<1> (64-bit): cross-check directly against i64. ──
4251        for v in [0_i64, 1, -1, i64::MAX, i64::MIN, 42, -42, 1 << 40, -(1 << 40)] {
4252            let x = Int::<1>::from_i64(v);
4253            assert_eq!(x.leading_zeros(), v.leading_zeros(), "i64 lz {v}");
4254            assert_eq!(x.trailing_zeros(), v.trailing_zeros(), "i64 tz {v}");
4255            assert_eq!(x.count_ones(), v.count_ones(), "i64 popcount {v}");
4256            assert_eq!(x.count_zeros(), v.count_zeros(), "i64 cz {v}");
4257            // bit_length is magnitude-based: significant bits of |v|.
4258            let mag_bits = 64 - v.unsigned_abs().leading_zeros();
4259            assert_eq!(x.bit_length(), mag_bits, "i64 bit_length {v}");
4260        }
4261
4262        // ── Int<2> (128-bit): cross-check directly against i128. ──
4263        for v in [
4264            0_i128,
4265            1,
4266            -1,
4267            i128::MAX,
4268            i128::MIN,
4269            (1_i128 << 64),       // 2^64 — limb boundary
4270            (1_i128 << 64) - 1,   // 2^64 - 1 — fills the low limb
4271            -(1_i128 << 64),
4272            i64::MAX as i128,
4273            i64::MIN as i128,
4274        ] {
4275            let x = Int::<2>::from_i128(v);
4276            assert_eq!(x.leading_zeros(), v.leading_zeros(), "i128 lz {v}");
4277            assert_eq!(x.trailing_zeros(), v.trailing_zeros(), "i128 tz {v}");
4278            assert_eq!(x.count_ones(), v.count_ones(), "i128 popcount {v}");
4279            assert_eq!(x.count_zeros(), v.count_zeros(), "i128 cz {v}");
4280            let mag_bits = 128 - v.unsigned_abs().leading_zeros();
4281            assert_eq!(x.bit_length(), mag_bits, "i128 bit_length {v}");
4282        }
4283
4284        // ── Int<4> (256-bit): values beyond i128 — assert the known
4285        //    two's-complement answer directly. ──
4286        const B: u32 = Int::<4>::BITS; // 256
4287
4288        // Zero: BITS leading zeros, BITS trailing zeros, no ones,
4289        // BITS clear bits, zero magnitude bits.
4290        let z = Int::<4>::ZERO;
4291        assert_eq!(z.leading_zeros(), B);
4292        assert_eq!(z.trailing_zeros(), B);
4293        assert_eq!(z.count_ones(), 0);
4294        assert_eq!(z.count_zeros(), B);
4295        assert_eq!(z.bit_length(), 0);
4296
4297        // One.
4298        let one = Int::<4>::ONE;
4299        assert_eq!(one.leading_zeros(), B - 1);
4300        assert_eq!(one.trailing_zeros(), 0);
4301        assert_eq!(one.count_ones(), 1);
4302        assert_eq!(one.count_zeros(), B - 1);
4303        assert_eq!(one.bit_length(), 1);
4304
4305        // -1 == all ones in two's complement: sign bit set so zero
4306        // leading zeros, every bit a one, zero trailing zeros.
4307        // Magnitude is 1, so bit_length is 1.
4308        let neg_one = Int::<4>::from_i64(-1);
4309        assert_eq!(*neg_one.as_limbs(), [u64::MAX; 4]);
4310        assert_eq!(neg_one.leading_zeros(), 0);
4311        assert_eq!(neg_one.trailing_zeros(), 0);
4312        assert_eq!(neg_one.count_ones(), B);
4313        assert_eq!(neg_one.count_zeros(), 0);
4314        assert_eq!(neg_one.bit_length(), 1);
4315
4316        // MAX == 0b0111…1: top bit clear, the rest set.
4317        let max = Int::<4>::MAX;
4318        assert_eq!(max.leading_zeros(), 1);
4319        assert_eq!(max.trailing_zeros(), 0);
4320        assert_eq!(max.count_ones(), B - 1);
4321        assert_eq!(max.count_zeros(), 1);
4322        assert_eq!(max.bit_length(), B - 1); // magnitude is 2^(B-1) - 1
4323
4324        // MIN == 0b1000…0: only the sign bit set. Negative → zero
4325        // leading zeros; the single set bit is the top one, so
4326        // trailing_zeros == BITS-1; magnitude |MIN| == 2^(B-1).
4327        let min = Int::<4>::MIN;
4328        assert_eq!(min.leading_zeros(), 0);
4329        assert_eq!(min.trailing_zeros(), B - 1);
4330        assert_eq!(min.count_ones(), 1);
4331        assert_eq!(min.count_zeros(), B - 1);
4332        assert_eq!(min.bit_length(), B); // |MIN| = 2^(B-1) ⇒ B significant bits
4333
4334        // Limb-boundary magnitudes: 2^64 and 2^64 - 1 in Int<4>.
4335        let two_64 = Int::<4>::from_u128(1u128 << 64);
4336        assert_eq!(two_64.trailing_zeros(), 64);
4337        assert_eq!(two_64.count_ones(), 1);
4338        assert_eq!(two_64.leading_zeros(), B - 65);
4339        assert_eq!(two_64.bit_length(), 65);
4340
4341        let two_64_m1 = Int::<4>::from_u128((1u128 << 64) - 1);
4342        assert_eq!(two_64_m1.trailing_zeros(), 0);
4343        assert_eq!(two_64_m1.count_ones(), 64);
4344        assert_eq!(two_64_m1.leading_zeros(), B - 64);
4345        assert_eq!(two_64_m1.bit_length(), 64);
4346    }
4347
4348    /// `Uint<N>`'s bit-count surface (`leading_zeros`, `count_ones`,
4349    /// `bit_length`) must match the `uN` contract at the edges.
4350    #[test]
4351    fn uint_bit_counts_match_unsigned_contract_at_edges() {
4352        // ── Uint<1> (64-bit): cross-check against u64. ──
4353        for v in [0_u64, 1, u64::MAX, 42, 1 << 40] {
4354            let x = Uint::<1>::from_u64(v);
4355            assert_eq!(x.leading_zeros(), v.leading_zeros(), "u64 lz {v}");
4356            assert_eq!(x.count_ones(), v.count_ones(), "u64 popcount {v}");
4357            // For unsigned, bit_length == BITS - leading_zeros.
4358            assert_eq!(x.bit_length(), 64 - v.leading_zeros(), "u64 bit_length {v}");
4359        }
4360
4361        // ── Uint<2> (128-bit): cross-check against u128, incl. limb
4362        //    boundary 2^64 / 2^64 - 1. ──
4363        for v in [0_u128, 1, u128::MAX, 1u128 << 64, (1u128 << 64) - 1] {
4364            let x = Uint::<2>::from_u128(v);
4365            assert_eq!(x.leading_zeros(), v.leading_zeros(), "u128 lz {v}");
4366            assert_eq!(x.count_ones(), v.count_ones(), "u128 popcount {v}");
4367            assert_eq!(x.bit_length(), 128 - v.leading_zeros(), "u128 bit_length {v}");
4368        }
4369
4370        // ── Uint<4> (256-bit): values beyond u128 — known answers. ──
4371        const B: u32 = Uint::<4>::BITS; // 256
4372        let zero = Uint::<4>::ZERO;
4373        assert_eq!(zero.leading_zeros(), B);
4374        assert_eq!(zero.count_ones(), 0);
4375        assert_eq!(zero.bit_length(), 0);
4376
4377        // All-ones (Uint MAX): no leading zeros, every bit set.
4378        let umax = Uint::<4>::from_limbs([u64::MAX; 4]);
4379        assert_eq!(umax.leading_zeros(), 0);
4380        assert_eq!(umax.count_ones(), B);
4381        assert_eq!(umax.bit_length(), B);
4382
4383        // 2^64 across the low limb boundary.
4384        let two_64 = Uint::<4>::from_u128(1u128 << 64);
4385        assert_eq!(two_64.count_ones(), 1);
4386        assert_eq!(two_64.leading_zeros(), B - 65);
4387        assert_eq!(two_64.bit_length(), 65);
4388    }
4389}
4390
4391/// Behavioural sanity checks for the const-generic `Int<N>` / `Uint<N>`
4392/// public surface, exercising the limb kernels end-to-end through the
4393/// type API (add/sub/neg, mul/div/rem, overflow, parse/pow, ordering /
4394/// resize, isqrt / f64, the unsigned bit helpers, and `as_u128`).
4395#[cfg(test)]
4396mod hint_tests {
4397    use super::{Int, Uint};
4398
4399    #[test]
4400    fn signed_add_sub_neg() {
4401        let a = Int::<4>::from_i128(5);
4402        let b = Int::<4>::from_i128(3);
4403        assert_eq!(a.wrapping_add(b), Int::<4>::from_i128(8));
4404        assert_eq!(a.wrapping_sub(b), Int::<4>::from_i128(2));
4405        assert_eq!(b.wrapping_sub(a), Int::<4>::from_i128(-2));
4406        assert_eq!(a.negate(), Int::<4>::from_i128(-5));
4407        assert_eq!(Int::<4>::ZERO.negate(), Int::<4>::ZERO);
4408    }
4409
4410    #[test]
4411    fn signed_mul_div_rem() {
4412        let six = Int::<8>::from_i128(6);
4413        let two = Int::<8>::from_i128(2);
4414        let three = Int::<8>::from_i128(3);
4415        assert_eq!(six.wrapping_mul(three), Int::<8>::from_i128(18));
4416        assert_eq!(six.wrapping_div(two), three);
4417        assert_eq!(
4418            Int::<8>::from_i128(7).wrapping_rem(three),
4419            Int::<8>::from_i128(1)
4420        );
4421        assert_eq!(
4422            Int::<8>::from_i128(-7).wrapping_rem(three),
4423            Int::<8>::from_i128(-1)
4424        );
4425        assert_eq!(six.negate().wrapping_mul(three), Int::<8>::from_i128(-18));
4426    }
4427
4428    #[test]
4429    fn checked_overflow() {
4430        assert_eq!(Int::<4>::MAX.checked_add(Int::<4>::ONE), None);
4431        assert_eq!(Int::<4>::MIN.checked_sub(Int::<4>::ONE), None);
4432        assert_eq!(Int::<4>::MIN.checked_neg(), None);
4433        assert_eq!(
4434            Int::<4>::from_i128(2).checked_add(Int::<4>::from_i128(3)),
4435            Some(Int::<4>::from_i128(5))
4436        );
4437    }
4438
4439    #[test]
4440    fn from_str_and_pow() {
4441        let ten = Int::<16>::from_str_radix("10", 10).unwrap();
4442        assert_eq!(ten, Int::<16>::from_i128(10));
4443        assert_eq!(ten.pow(3), Int::<16>::from_i128(1000));
4444        let big = Int::<16>::from_str_radix("10", 10).unwrap().pow(40);
4445        let from_str =
4446            Int::<16>::from_str_radix("10000000000000000000000000000000000000000", 10).unwrap();
4447        assert_eq!(big, from_str);
4448        assert_eq!(
4449            Int::<4>::from_str_radix("-42", 10).unwrap(),
4450            Int::<4>::from_i128(-42)
4451        );
4452    }
4453
4454    #[test]
4455    fn ordering_and_resize() {
4456        assert!(Int::<4>::from_i128(-1) < Int::<4>::ZERO);
4457        assert!(Int::<4>::MIN < Int::<4>::MAX);
4458        let v = Int::<4>::from_i128(-123_456_789);
4459        let wide: Int<16> = v.resize();
4460        let back: Int<4> = wide.resize();
4461        assert_eq!(back, v);
4462        assert_eq!(wide, Int::<16>::from_i128(-123_456_789));
4463    }
4464
4465    // Int<8> isqrt (work width 9) exceeds the narrow build's int scratch.
4466    #[cfg(feature = "_wide-support")]
4467    #[test]
4468    fn isqrt_and_f64() {
4469        assert_eq!(Int::<8>::from_i128(144).isqrt(), Int::<8>::from_i128(12));
4470        assert_eq!(Int::<4>::from_i128(1_000_000).as_f64(), 1_000_000.0);
4471        assert_eq!(Int::<4>::from_f64(-2_500.0), Int::<4>::from_i128(-2500));
4472    }
4473
4474    /// `Uint<4>` (the unsigned macro emission) supports the same
4475    /// bit/sign-manipulation surface as the signed sibling.
4476    #[test]
4477    fn uint256_is_zero_and_bit_helpers() {
4478        let zero = Uint::<4>::ZERO;
4479        let one = Uint::<4>::from_str_radix("1", 10).unwrap();
4480        let two = Uint::<4>::from_str_radix("2", 10).unwrap();
4481        assert!(zero.is_zero());
4482        assert!(!one.is_zero());
4483        assert!(one.is_power_of_two());
4484        assert!(two.is_power_of_two());
4485        let three = Uint::<4>::from_str_radix("3", 10).unwrap();
4486        assert!(!three.is_power_of_two());
4487        assert_eq!(zero.next_power_of_two(), one);
4488        assert_eq!(one.next_power_of_two(), one);
4489        let four = Uint::<4>::from_str_radix("4", 10).unwrap();
4490        assert_eq!(three.next_power_of_two(), four);
4491        assert_eq!(zero.count_ones(), 0);
4492        assert_eq!(one.count_ones(), 1);
4493        assert_eq!(zero.leading_zeros(), Uint::<4>::BITS);
4494        assert_eq!(one.leading_zeros(), Uint::<4>::BITS - 1);
4495    }
4496
4497    #[test]
4498    fn uint256_parse_arithmetic_and_pow() {
4499        assert!(Uint::<4>::from_str_radix("10", 2).is_err());
4500        assert!(Uint::<4>::from_str_radix("1a", 10).is_err());
4501        let two = Uint::<4>::from_str_radix("2", 10).unwrap();
4502        let three = Uint::<4>::from_str_radix("3", 10).unwrap();
4503        let six = Uint::<4>::from_str_radix("6", 10).unwrap();
4504        let seven = Uint::<4>::from_str_radix("7", 10).unwrap();
4505        assert_eq!(three - two, Uint::<4>::from_str_radix("1", 10).unwrap());
4506        assert_eq!(six / two, three);
4507        assert_eq!(seven % three, Uint::<4>::from_str_radix("1", 10).unwrap());
4508        let five = Uint::<4>::from_str_radix("5", 10).unwrap();
4509        let four = Uint::<4>::from_str_radix("4", 10).unwrap();
4510        let one = Uint::<4>::from_str_radix("1", 10).unwrap();
4511        assert_eq!(five & four, four);
4512        assert_eq!(five | one, five);
4513        assert_eq!(five ^ four, one);
4514        let p10 = two.pow(10);
4515        assert_eq!(p10, Uint::<4>::from_str_radix("1024", 10).unwrap());
4516        let signed = three.cast_signed();
4517        assert_eq!(signed, Int::<4>::from_i128(3));
4518    }
4519
4520    /// `Int::<4>::bit` reports the two's-complement bit at any index.
4521    #[test]
4522    fn signed_bit_and_trailing_zeros() {
4523        let v = Int::<4>::from_i128(0b1100);
4524        assert!(v.bit(2));
4525        assert!(v.bit(3));
4526        assert!(!v.bit(0));
4527        assert!(!v.bit(1));
4528        assert!(!v.bit(1000));
4529        let n = Int::<4>::from_i128(-1);
4530        assert!(n.bit(1000));
4531        assert_eq!(Int::<4>::from_i128(8).trailing_zeros(), 3);
4532        assert_eq!(Int::<4>::ZERO.trailing_zeros(), Int::<4>::BITS);
4533    }
4534
4535    /// `Int::<4>::as_u128` returns the low 128 magnitude bits.
4536    #[test]
4537    fn as_u128_returns_low_magnitude_bits() {
4538        let src = Int::<4>::from_i128(123_456_789);
4539        let dst: u128 = src.as_u128();
4540        assert_eq!(dst, 123_456_789);
4541        let dst: u128 = Int::<4>::ZERO.as_u128();
4542        assert_eq!(dst, 0);
4543    }
4544}