clock_bigint/
types.rs

1//! Core BigInt types and traits.
2//!
3//! This module provides both dynamic-length and fixed-length (const-generic)
4//! BigInt types for different use cases.
5
6use crate::error::{BigIntError, Result};
7use crate::limbs::{Limb, canonicalize, is_zero};
8
9/// Core trait for BigInt operations.
10///
11/// This trait defines common operations that both dynamic and fixed-length
12/// BigInt types must implement.
13pub trait BigIntCore {
14    /// Get the number of limbs.
15    fn limb_count(&self) -> usize;
16
17    /// Get a reference to the limbs.
18    fn limbs(&self) -> &[Limb];
19
20    /// Get a mutable reference to the limbs.
21    fn limbs_mut(&mut self) -> &mut [Limb];
22
23    /// Get the sign bit (false = positive, true = negative).
24    fn sign(&self) -> bool;
25
26    /// Set the sign bit.
27    fn set_sign(&mut self, sign: bool);
28
29    /// Check if the value is zero.
30    fn is_zero(&self) -> bool {
31        is_zero(self.limbs())
32    }
33
34    /// Ensure canonical form.
35    ///
36    /// For dynamic BigInt, this removes leading zeros.
37    /// For fixed BigInt, this is a no-op (canonicalization not required).
38    fn canonicalize(&mut self) -> Result<()>;
39}
40
41#[cfg(feature = "alloc")]
42/// Dynamic-length BigInt.
43///
44/// This type uses heap allocation and can grow up to `max_limbs`.
45/// It enforces canonical form (no leading zeros, zero sign rules).
46#[derive(Clone, Debug)]
47pub struct BigInt {
48    /// Sign bit: false = positive, true = negative.
49    sign: bool,
50    /// Limb array in little-endian order.
51    limbs: alloc::vec::Vec<Limb>,
52    /// Maximum number of limbs allowed.
53    max_limbs: usize,
54}
55
56#[cfg(feature = "alloc")]
57impl BigInt {
58    /// Create a new BigInt with the specified maximum limb count.
59    pub fn new(max_limbs: usize) -> Self {
60        Self {
61            sign: false,
62            limbs: alloc::vec![0],
63            max_limbs,
64        }
65    }
66
67    /// Create a BigInt from a u64 value.
68    pub fn from_u64(value: u64, max_limbs: usize) -> Self {
69        if value == 0 {
70            Self {
71                sign: false,
72                limbs: alloc::vec![0],
73                max_limbs,
74            }
75        } else {
76            Self {
77                sign: false,
78                limbs: alloc::vec![value],
79                max_limbs,
80            }
81        }
82    }
83
84    /// Create a BigInt from a slice of limbs.
85    ///
86    /// The limbs are assumed to be in little-endian order.
87    pub fn from_limbs(limbs: &[Limb], max_limbs: usize) -> Result<Self> {
88        if limbs.is_empty() {
89            return Ok(Self {
90                sign: false,
91                limbs: alloc::vec![0],
92                max_limbs,
93            });
94        }
95
96        if limbs.len() > max_limbs {
97            return Err(BigIntError::Overflow);
98        }
99
100        let mut limbs_vec = limbs.to_vec();
101        let len = canonicalize(&mut limbs_vec);
102        limbs_vec.truncate(len);
103
104        // Check for zero and enforce zero sign rule
105        let is_zero = is_zero(&limbs_vec[..len]);
106        let sign = if is_zero { false } else { false }; // Start with positive, will be set by caller if needed
107
108        Ok(Self {
109            sign,
110            limbs: limbs_vec,
111            max_limbs,
112        })
113    }
114
115    /// Get the maximum number of limbs.
116    pub fn max_limbs(&self) -> usize {
117        self.max_limbs
118    }
119
120    /// Ensure capacity for at least `n` limbs.
121    pub fn ensure_capacity(&mut self, n: usize) -> Result<()> {
122        if n > self.max_limbs {
123            return Err(BigIntError::Overflow);
124        }
125        if self.limbs.len() < n {
126            self.limbs.resize(n, 0);
127        }
128        Ok(())
129    }
130}
131
132#[cfg(feature = "alloc")]
133impl BigIntCore for BigInt {
134    fn limb_count(&self) -> usize {
135        self.limbs.len()
136    }
137
138    fn limbs(&self) -> &[Limb] {
139        &self.limbs
140    }
141
142    fn limbs_mut(&mut self) -> &mut [Limb] {
143        &mut self.limbs
144    }
145
146    fn sign(&self) -> bool {
147        self.sign
148    }
149
150    fn set_sign(&mut self, sign: bool) {
151        // Enforce zero sign rule: zero must have sign = false
152        if self.is_zero() {
153            self.sign = false;
154        } else {
155            self.sign = sign;
156        }
157    }
158
159    fn canonicalize(&mut self) -> Result<()> {
160        let len = canonicalize(&mut self.limbs);
161        self.limbs.truncate(len);
162
163        // Enforce zero sign rule
164        if self.is_zero() {
165            self.sign = false;
166        }
167
168        Ok(())
169    }
170}
171
172/// Fixed-length BigInt with const-generic limb count.
173///
174/// This type uses a fixed-size array and is suitable for VM registers
175/// and fixed-width cryptographic types.
176#[derive(Clone, Copy, Debug)]
177pub struct BigIntFixed<const L: usize> {
178    /// Sign bit: false = positive, true = negative.
179    sign: bool,
180    /// Fixed-size limb array in little-endian order.
181    limbs: [Limb; L],
182}
183
184impl<const L: usize> BigIntFixed<L> {
185    /// Create a new zero BigIntFixed.
186    pub const fn zero() -> Self {
187        Self {
188            sign: false,
189            limbs: [0; L],
190        }
191    }
192
193    /// Create a BigIntFixed from a u64 value.
194    pub fn from_u64(value: u64) -> Self {
195        let mut limbs = [0u64; L];
196        if value != 0 {
197            limbs[0] = value;
198        }
199        Self { sign: false, limbs }
200    }
201
202    /// Create a BigIntFixed from a slice of limbs.
203    ///
204    /// If the slice is longer than L, this will truncate.
205    /// If shorter, the remaining limbs will be zero.
206    pub fn from_limbs(limbs: &[Limb]) -> Result<Self> {
207        let mut result_limbs = [0u64; L];
208        let copy_len = limbs.len().min(L);
209        result_limbs[..copy_len].copy_from_slice(&limbs[..copy_len]);
210
211        // Check for overflow if input was longer
212        if limbs.len() > L {
213            // Check if any truncated limbs were non-zero
214            if limbs[L..].iter().any(|&x| x != 0) {
215                return Err(BigIntError::Overflow);
216            }
217        }
218
219        Ok(Self {
220            sign: false,
221            limbs: result_limbs,
222        })
223    }
224
225    /// Get a reference to the limb array.
226    pub fn as_limbs(&self) -> &[Limb; L] {
227        &self.limbs
228    }
229
230    /// Get a mutable reference to the limb array.
231    pub fn as_limbs_mut(&mut self) -> &mut [Limb; L] {
232        &mut self.limbs
233    }
234}
235
236impl<const L: usize> BigIntCore for BigIntFixed<L> {
237    fn limb_count(&self) -> usize {
238        L
239    }
240
241    fn limbs(&self) -> &[Limb] {
242        &self.limbs
243    }
244
245    fn limbs_mut(&mut self) -> &mut [Limb] {
246        &mut self.limbs
247    }
248
249    fn sign(&self) -> bool {
250        self.sign
251    }
252
253    fn set_sign(&mut self, sign: bool) {
254        // Enforce zero sign rule
255        if self.is_zero() {
256            self.sign = false;
257        } else {
258            self.sign = sign;
259        }
260    }
261
262    fn canonicalize(&mut self) -> Result<()> {
263        // Fixed-length BigInt skips canonicalization per spec
264        // But we still enforce zero sign rule
265        if self.is_zero() {
266            self.sign = false;
267        }
268        Ok(())
269    }
270}
271
272/// Type alias for 256-bit BigInt (4 limbs).
273pub type U256 = BigIntFixed<4>;
274
275/// Type alias for 512-bit BigInt (8 limbs).
276pub type U512 = BigIntFixed<8>;
277
278/// Type alias for 1024-bit BigInt (16 limbs).
279pub type U1024 = BigIntFixed<16>;
280
281/// Type alias for 2048-bit BigInt (32 limbs).
282pub type U2048 = BigIntFixed<32>;
283
284#[cfg(test)]
285mod tests {
286    use super::*;
287
288    #[test]
289    fn test_bigint_from_u64() {
290        let bi = BigInt::from_u64(42, 10);
291        assert_eq!(bi.limbs(), &[42]);
292        assert!(!bi.sign());
293        assert!(!bi.is_zero());
294    }
295
296    #[test]
297    fn test_bigint_zero() {
298        let bi = BigInt::from_u64(0, 10);
299        assert_eq!(bi.limbs(), &[0]);
300        assert!(!bi.sign());
301        assert!(bi.is_zero());
302    }
303
304    #[test]
305    fn test_bigint_fixed_from_u64() {
306        let bi = U256::from_u64(42);
307        assert_eq!(bi.limbs()[0], 42);
308        assert!(!bi.sign());
309    }
310
311    #[test]
312    fn test_bigint_fixed_zero() {
313        let bi = U256::zero();
314        assert!(bi.is_zero());
315        assert!(!bi.sign());
316    }
317
318    #[test]
319    fn test_canonicalize_zero_sign() {
320        let mut bi = BigInt::from_u64(0, 10);
321        bi.set_sign(true); // Try to set negative
322        bi.canonicalize().unwrap();
323        assert!(!bi.sign()); // Should be forced to positive
324    }
325}