fixed_bigint/heapless/bits.rs
1//! `bit_length` and `leading_zeros` for `HeaplessBigInt`.
2//!
3//! Both dispatch on `P` like `FixedUInt`: `Nct` scans MSB-to-LSB and
4//! stops at the highest non-zero limb; `Ct` scans the full width with an
5//! `undecided` lock so the loop is value-independent (mirroring
6//! `const_leading_zeros_ct`). The returned count is the magnitude either
7//! way — a caller that must keep the magnitude secret does not call these.
8//!
9//! `bit_length` is the position of the highest set bit plus one, so
10//! `bit_length(0) == 0` and `bit_length(1) == 1`. `leading_zeros` is
11//! taken against the value's **width** (`len * word_bits`), not capacity
12//! — so `bit_length + leading_zeros == bits_precision()` and `CAP` never
13//! enters. A caller that wants zeros relative to a wider window sizes it
14//! from that window's own `bits_precision`, not this value's capacity.
15
16use super::HeaplessBigInt;
17use crate::MachineWord;
18use const_num_traits::{Personality, PersonalityTag};
19
20impl<T: MachineWord, const CAP: usize, P: Personality> HeaplessBigInt<T, CAP, P> {
21 /// Number of bits needed to represent the value: `0` for zero,
22 /// otherwise the position of the highest set bit plus one.
23 pub fn bit_length(&self) -> usize {
24 let width = self.len as usize * core::mem::size_of::<T>() * 8;
25 width - self.leading_zeros()
26 }
27
28 /// Leading zeros against the value's width (`len * word_bits`), so
29 /// `leading_zeros + bit_length == bits_precision()`. A `len = 0`
30 /// value has width 0, hence `leading_zeros() == 0`.
31 pub fn leading_zeros(&self) -> usize {
32 let word_bits = core::mem::size_of::<T>() * 8;
33 match P::TAG {
34 // MSB-to-LSB; the first non-zero limb fixes the count and the
35 // invariant guarantees limbs above it are zero. Zero words above
36 // that limb contribute a full `word_bits` each.
37 PersonalityTag::Nct => {
38 let len = self.len as usize;
39 let mut i = len;
40 while i > 0 {
41 i -= 1;
42 let limb = self.limbs[i];
43 if !super::is_zero(&limb) {
44 return (len - 1 - i) * word_bits + limb.leading_zeros() as usize;
45 }
46 }
47 len * word_bits
48 }
49 // Shared full-width branchless scan (see `const_leading_zeros_ct`).
50 PersonalityTag::Ct => {
51 let n = self.len as usize;
52 let s = self.limbs.get(..n).unwrap_or(&self.limbs);
53 crate::fixeduint::const_leading_zeros_ct(s) as usize
54 }
55 }
56 }
57}
58
59// ── const_num_traits::BitWidth (bit-length) / BitsPrecision (width) ──
60//
61// Two distinct quantities (bit-vocabulary canon): `bit_width` is the
62// significant-bit count (per-value magnitude); `bits_precision` is the
63// operating width, which for this variable-width carrier is the
64// constructed `len·word_bits` — NOT `CAP` (capacity stays out of any
65// trait answer). `bit_length <= bits_precision` always. Value receiver
66// per the traits; `&Self` mirrors (no reference blanket upstream).
67
68impl<T: MachineWord, const CAP: usize, P: Personality> const_num_traits::BitWidth
69 for HeaplessBigInt<T, CAP, P>
70{
71 fn bit_width(self) -> u32 {
72 self.bit_length() as u32
73 }
74}
75
76impl<T: MachineWord, const CAP: usize, P: Personality> const_num_traits::BitWidth
77 for &HeaplessBigInt<T, CAP, P>
78{
79 fn bit_width(self) -> u32 {
80 self.bit_length() as u32
81 }
82}
83
84impl<T: MachineWord, const CAP: usize, P: Personality> const_num_traits::BitsPrecision
85 for HeaplessBigInt<T, CAP, P>
86{
87 fn bits_precision(&self) -> u32 {
88 self.len as u32 * (core::mem::size_of::<T>() as u32 * 8)
89 }
90}
91
92impl<T: MachineWord, const CAP: usize, P: Personality> const_num_traits::BitsPrecision
93 for &HeaplessBigInt<T, CAP, P>
94{
95 fn bits_precision(&self) -> u32 {
96 self.len as u32 * (core::mem::size_of::<T>() as u32 * 8)
97 }
98}
99
100// Establish a width from a witness. Grow-only, value-preserving: widen `self`
101// to the whole-word length covering `bits_precision` bits, never shrinking.
102// The zero/one/witness constructors (`zero_with_precision_of(&modulus)` etc.)
103// default over this, giving generic reducers a correctly-sized seed instead of
104// the minimal-width `zero()` that silently truncates. `CAP` is the ceiling:
105// `widened` panics if the requested width exceeds it.
106impl<T: MachineWord, const CAP: usize, P: Personality> const_num_traits::WithPrecision
107 for HeaplessBigInt<T, CAP, P>
108{
109 fn widen_to_precision(self, bits_precision: u32) -> Self {
110 let word_bits = core::mem::size_of::<T>() as u32 * 8;
111 let target_len = bits_precision.div_ceil(word_bits) as u16;
112 self.widened(target_len.max(self.len()))
113 }
114}