clock_curve_math/extensible/variable_precision.rs
1//! Variable-precision arithmetic support.
2//!
3//! This module provides support for arithmetic with different limb counts
4//! and field sizes, preparing for future cryptographic primitives.
5
6#[cfg(feature = "alloc")]
7extern crate alloc;
8use super::multiplication::MultiplicationAlgorithm;
9use crate::bigint::BigInt;
10use crate::error::MathError;
11#[cfg(feature = "alloc")]
12use alloc::vec::Vec;
13
14/// Variable-precision arithmetic support for cryptographic computations.
15///
16/// This struct provides a unified interface for modular arithmetic with different
17/// precision levels and reduction techniques. It supports multiple limb counts
18/// (64-bit words) and automatically precomputes constants for various reduction
19/// algorithms to optimize performance for different modulus sizes.
20///
21/// # Supported Reduction Techniques
22/// - **Montgomery Reduction**: Efficient for general moduli, provides constant-time
23/// multiplication and reduction operations
24/// - **Barrett Reduction**: Fast reduction using precomputed constants, avoids
25/// expensive division operations
26/// - **Solinas Reduction**: Specialized for moduli of the form 2^a ± 2^b ± 1,
27/// used by curves like Curve25519 (p = 2^255 - 19)
28///
29/// # Performance Optimization
30/// Precomputes all necessary constants during construction to minimize per-operation
31/// overhead. Supports algorithm selection based on operand sizes and available
32/// computational resources.
33///
34/// # Use Cases
35/// - Elliptic curve cryptography with different field sizes
36/// - Multi-precision arithmetic for cryptographic protocols
37/// - Performance-critical modular computations
38///
39/// # Memory Layout
40/// Values are stored as multi-precision integers using the specified number of
41/// limbs. The Montgomery form uses R = 2^(limb_count × 64) as the Montgomery radix.
42#[derive(Clone, Debug)]
43#[allow(dead_code)]
44pub struct VariablePrecision {
45 /// The number of limbs (64-bit words) used for multi-precision arithmetic
46 limb_count: usize,
47 /// The modulus defining the finite field for all operations
48 modulus: BigInt,
49 /// Montgomery radix R = 2^(limb_count × 64)
50 montgomery_r: BigInt,
51 /// Montgomery constant R² mod modulus for conversion to Montgomery form
52 montgomery_r2: BigInt,
53 /// Montgomery inverse N' = -modulus⁻¹ mod R for Montgomery reduction
54 montgomery_n_prime: BigInt,
55 /// Barrett reduction constant μ = floor(2^(2×k) / modulus) where k = bit_length
56 barrett_mu: Option<BigInt>,
57 /// Solinas reduction constants for special moduli of form 2^a ± 2^b ± 1
58 #[cfg(feature = "alloc")]
59 solinas_constants: Option<Vec<BigInt>>,
60}
61
62impl VariablePrecision {
63 /// Create a new variable precision arithmetic context.
64 ///
65 /// Initializes a context for modular arithmetic with the specified limb count
66 /// and modulus. Precomputes all necessary constants for Montgomery, Barrett,
67 /// and other reduction techniques to optimize subsequent operations.
68 ///
69 /// # Arguments
70 /// * `limb_count` - Number of 64-bit limbs for multi-precision arithmetic (1-8)
71 /// * `modulus` - The prime modulus defining the finite field
72 ///
73 /// # Returns
74 /// A new `VariablePrecision` instance ready for modular arithmetic operations
75 ///
76 /// # Errors
77 /// Returns `MathError::InvalidInput` if:
78 /// - `limb_count` is 0
79 /// - `limb_count` exceeds 8 (implementation limit)
80 ///
81 /// # Precomputation Details
82 /// - **Montgomery Constants**: Computes R, R², and N' for Montgomery arithmetic
83 /// - **Barrett Constant**: Computes μ for Barrett reduction
84 /// - **Solinas Constants**: Placeholder for future Solinas prime support
85 ///
86 /// # Performance
87 /// Construction involves modular exponentiation and inverse computation,
88 /// which is expensive but done once. Subsequent operations are optimized.
89 ///
90 /// # Examples
91 /// ```ignore
92 /// use clock_curve_math::extensible::variable_precision::VariablePrecision;
93 /// use clock_curve_math::BigInt;
94 ///
95 /// // Create context for 256-bit arithmetic (4 limbs × 64 bits)
96 /// let modulus = BigInt::from_hex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f")?; // secp256k1 modulus
97 /// let ctx = VariablePrecision::new(4, modulus)?;
98 /// # Ok::<(), clock_curve_math::MathError>(())
99 /// ```
100 pub fn new(limb_count: usize, modulus: BigInt) -> Result<Self, MathError> {
101 if limb_count == 0 || limb_count > 8 {
102 return Err(MathError::InvalidInput);
103 }
104
105 // Compute Montgomery constants
106 let r_limbs = limb_count as u32;
107 let montgomery_r = BigInt::from_u64(1).shl(r_limbs * 64);
108
109 // Compute R² = R^2 mod modulus where R = 2^(limb_count * 64)
110 let montgomery_r2 = {
111 let r_mod_n = super::utils::mod_pow(
112 &BigInt::from_u64(2),
113 &BigInt::from_u64((r_limbs * 64) as u64),
114 &modulus,
115 );
116 super::utils::mod_pow(&r_mod_n, &BigInt::from_u64(2), &modulus)
117 };
118
119 // Compute N' = -N^(-1) mod R
120 let montgomery_n_prime =
121 if let Some(inv) = super::utils::mod_inverse(&modulus, &montgomery_r) {
122 let mut result = BigInt::from_u64(0).sub(&inv);
123 while result.ct_cmp(&BigInt::from_u64(0)) == core::cmp::Ordering::Less {
124 result = result.add(&montgomery_r);
125 }
126 result
127 } else {
128 // This shouldn't happen for prime moduli, but handle gracefully
129 BigInt::from_u64(0)
130 };
131
132 // Compute Barrett reduction constant mu = floor(2^(2*k) / m) where k is bit length
133 let bit_length = modulus.bit_length();
134 let barrett_mu = if bit_length > 0 {
135 BigInt::from_u64(1).shl(2 * bit_length).div_rem(&modulus).0
136 } else {
137 BigInt::from_u64(0)
138 };
139
140 Ok(Self {
141 limb_count,
142 modulus,
143 montgomery_r,
144 montgomery_r2,
145 montgomery_n_prime,
146 barrett_mu: Some(barrett_mu),
147 solinas_constants: None, // Would be computed for Solinas primes
148 })
149 }
150
151 /// Get the number of limbs used for multi-precision arithmetic.
152 ///
153 /// Returns the limb count specified during construction, which determines
154 /// the maximum precision for arithmetic operations.
155 ///
156 /// # Returns
157 /// The number of 64-bit limbs (1-8) used for multi-precision arithmetic
158 pub fn limb_count(&self) -> usize {
159 self.limb_count
160 }
161
162 /// Get the modulus defining the finite field.
163 ///
164 /// Returns a reference to the prime modulus used for all modular arithmetic
165 /// operations in this context.
166 ///
167 /// # Returns
168 /// A reference to the BigInt modulus
169 pub fn modulus(&self) -> &BigInt {
170 &self.modulus
171 }
172
173 /// Perform Montgomery multiplication with variable precision.
174 ///
175 /// Computes `(a * b * R^(-1)) mod modulus` where `R = 2^(limb_count * 64)`.
176 /// This function implements the Montgomery multiplication algorithm for
177 /// efficient modular arithmetic.
178 ///
179 /// # Arguments
180 /// * `a` - First operand (must be in Montgomery form)
181 /// * `b` - Second operand (must be in Montgomery form)
182 ///
183 /// # Returns
184 /// The product `(a * b * R^(-1)) mod modulus` in Montgomery form
185 ///
186 /// # Algorithm
187 /// Uses the standard Montgomery multiplication formula:
188 /// 1. Compute t = a * b
189 /// 2. Compute m = (t * N') mod R
190 /// 3. Compute u = (t + m * N) / R
191 /// 4. If u ≥ N, return u - N, else return u
192 ///
193 /// Where N is the modulus, R is the Montgomery radix, and N' is the
194 /// precomputed Montgomery inverse.
195 ///
196 /// # Performance
197 /// O(limb_count²) time complexity, constant-time for fixed limb counts.
198 /// Significantly faster than standard modular multiplication for multiple operations.
199 ///
200 /// # Note
201 /// Both operands must already be in Montgomery form. Use `to_montgomery()` to
202 /// convert standard form values to Montgomery form before multiplication.
203 pub fn montgomery_mul(&self, a: &BigInt, b: &BigInt) -> BigInt {
204 // Montgomery multiplication algorithm
205 let t = a.mul(b);
206 let m = t
207 .mul(&self.montgomery_n_prime)
208 .div_rem(&self.montgomery_r)
209 .1;
210 let u = t.add(&m.mul(&self.modulus)).div_rem(&self.montgomery_r).1;
211
212 // Final reduction
213 if u.ct_cmp(&self.modulus) != core::cmp::Ordering::Less {
214 u.sub(&self.modulus)
215 } else {
216 u
217 }
218 }
219
220 /// Convert a value to Montgomery form for efficient modular arithmetic.
221 ///
222 /// Montgomery form represents the value x as `x * R mod modulus`, where
223 /// R is a power of 2 larger than the modulus. This representation enables
224 /// faster modular multiplication by avoiding expensive division operations.
225 ///
226 /// # Arguments
227 /// * `value` - The value to convert to Montgomery form (0 ≤ value < modulus)
228 ///
229 /// # Returns
230 /// The value converted to Montgomery form: `(value * R) mod modulus`
231 ///
232 /// # Mathematical Background
233 /// In Montgomery arithmetic, numbers are represented in a transformed space
234 /// where multiplication can be performed more efficiently. The conversion is:
235 ///
236 /// `montgomery_value = (value * R) mod modulus`
237 ///
238 /// Where R = 2^(limb_count × 64) is chosen to be larger than modulus.
239 ///
240 /// # Usage
241 /// ```text
242 /// let a_mont = ctx.to_montgomery(&a);
243 /// let b_mont = ctx.to_montgomery(&b);
244 /// let product_mont = ctx.montgomery_mul(&a_mont, &b_mont);
245 /// let product = ctx.from_montgomery(&product_mont);
246 /// ```
247 ///
248 /// # Performance
249 /// Uses precomputed R² constant for efficient conversion via Montgomery multiplication.
250 pub fn to_montgomery(&self, value: &BigInt) -> BigInt {
251 // x * R mod N = montgomery_mul(x, R²)
252 self.montgomery_mul(value, &self.montgomery_r2)
253 }
254
255 /// Convert a value from Montgomery form back to standard representation.
256 ///
257 /// Converts a Montgomery-form value back to its standard integer representation.
258 /// This is the inverse operation of `to_montgomery()`.
259 ///
260 /// # Arguments
261 /// * `value` - The Montgomery-form value to convert
262 ///
263 /// # Returns
264 /// The value converted back to standard form: `(value * R^(-1)) mod modulus`
265 ///
266 /// # Mathematical Background
267 /// Given a Montgomery value `mont_value = (x * R) mod modulus`, this function
268 /// computes `x = (mont_value * R^(-1)) mod modulus` using Montgomery reduction.
269 ///
270 /// The algorithm performs Montgomery reduction to multiply by R^(-1) modulo modulus:
271 /// 1. Compute m = (value * N') mod R
272 /// 2. Compute u = (value + m * N) / R
273 /// 3. Return u - N if u ≥ N, else return u
274 ///
275 /// # Usage
276 /// ```text
277 /// let a_mont = ctx.to_montgomery(&a);
278 /// let b_mont = ctx.to_montgomery(&b);
279 /// let product_mont = ctx.montgomery_mul(&a_mont, &b_mont);
280 /// let product = ctx.from_montgomery(&product_mont); // Get final result
281 /// ```
282 ///
283 /// # Performance
284 /// O(limb_count²) time complexity using Montgomery reduction algorithm.
285 pub fn from_montgomery(&self, value: &BigInt) -> BigInt {
286 // Montgomery reduction to convert back to standard form
287 let m = value
288 .mul(&self.montgomery_n_prime)
289 .div_rem(&self.montgomery_r)
290 .1;
291 let u = value
292 .add(&m.mul(&self.modulus))
293 .div_rem(&self.montgomery_r)
294 .1;
295
296 // Final reduction
297 if u.ct_cmp(&self.modulus) != core::cmp::Ordering::Less {
298 u.sub(&self.modulus)
299 } else {
300 u
301 }
302 }
303
304 /// Perform Barrett reduction for fast modular reduction.
305 ///
306 /// Barrett reduction uses precomputed constants to avoid expensive divisions.
307 /// This is faster than standard modular reduction for large moduli.
308 ///
309 pub fn barrett_reduce(&self, value: &BigInt) -> BigInt {
310 if let Some(mu) = &self.barrett_mu {
311 // Barrett reduction: x mod m ≈ x - floor(x * mu / 2^k) * m
312 let k = self.modulus.bit_length();
313 let q = value.mul(mu).shr(k); // Approximate quotient
314 let r = value.sub(&q.mul(&self.modulus)); // Approximate remainder
315
316 // Correct the result if necessary
317 if r.ct_cmp(&self.modulus) != core::cmp::Ordering::Less {
318 r.sub(&self.modulus)
319 } else {
320 r
321 }
322 } else {
323 // Fallback to standard reduction
324 value.div_rem(&self.modulus).1
325 }
326 }
327
328 /// Perform Solinas reduction for special moduli of cryptographic interest.
329 ///
330 /// Solinas reduction is a specialized algorithm for moduli of the form
331 /// `2^a ± 2^b ± 1`. These moduli are common in elliptic curve cryptography,
332 /// including Curve25519 (p = 2^255 - 19) and other standardized curves.
333 ///
334 /// # Arguments
335 /// * `value` - The value to reduce modulo the special modulus
336 ///
337 /// # Returns
338 /// The reduced value: `value mod modulus` (0 ≤ result < modulus)
339 ///
340 /// # Algorithm
341 /// For a modulus p = 2^a ± 2^b ± 1, Solinas reduction exploits the special
342 /// form to perform reduction efficiently:
343 ///
344 /// 1. Compute q = floor(x / 2^a)
345 /// 2. Compute correction = q * (2^a ± 2^b ± 1)
346 /// 3. Compute result = x - correction
347 /// 4. Final adjustment to ensure result ∈ [0, p-1]
348 ///
349 /// This avoids expensive division by using bit shifts and the special
350 /// structure of the modulus.
351 ///
352 /// # Supported Moduli
353 /// - **Curve25519**: p = 2^255 - 19
354 /// - **Ed25519**: Same as Curve25519
355 /// - **NIST P-256**: p = 2^256 - 2^224 + 2^192 + 2^96 - 1 (Solinas-like)
356 /// - Other moduli of the form 2^a ± 2^b ± 1
357 ///
358 /// # Performance
359 /// O(1) time complexity for fixed parameter sizes, using only bit operations
360 /// and additions. Extremely fast for supported moduli.
361 ///
362 /// # Configuration
363 /// Requires precomputed Solinas constants specifying the parameters (a, b)
364 /// and signs for the modulus form. Currently not implemented - falls back
365 /// to standard modular reduction.
366 ///
367 /// # Note
368 /// This implementation is currently a placeholder. Full Solinas reduction
369 /// requires additional constants and parameter detection logic.
370 pub fn solinas_reduce(&self, value: &BigInt) -> BigInt {
371 #[cfg(feature = "alloc")]
372 if let Some(constants) = &self.solinas_constants {
373 // Solinas reduction for moduli of the form 2^a ± 2^b ± 1
374 // The constants contain the parameters (a, b, sign1, sign2)
375
376 // For a modulus p = 2^a ± 2^b ± 1, we can reduce x mod p using:
377 // x mod p = x - floor(x / 2^a) * (2^a ± 2^b ± 1) + adjustments
378
379 // Extract parameters from constants
380 // constants[0] = a, constants[1] = b
381 // constants[2] = sign for 2^b term, constants[3] = sign for constant term
382 if constants.len() >= 4 {
383 let a = constants[0].to_u64().unwrap_or(0) as u32;
384 let b = constants[1].to_u64().unwrap_or(0) as u32;
385 let sign_b = constants[2].to_u64().unwrap_or(0) as i32;
386 let sign_1 = constants[3].to_u64().unwrap_or(0) as i32;
387
388 // Compute x div 2^a and x mod 2^a
389 let x_shift_a = value.shr(a);
390 let mask = BigInt::from_u64(1).shl(a).sub(&BigInt::from_u64(1));
391 let _x_low_a = value.and(&mask);
392
393 // Compute the correction term: floor(x / 2^a) * (2^a ± 2^b ± 1)
394 let mut correction = x_shift_a.shl(a); // floor(x / 2^a) * 2^a
395
396 if sign_b != 0 {
397 let b_term = x_shift_a.shl(b);
398 if sign_b > 0 {
399 correction = correction.add(&b_term);
400 } else {
401 correction = correction.sub(&b_term);
402 }
403 }
404
405 if sign_1 != 0 {
406 if sign_1 > 0 {
407 correction = correction.add(&x_shift_a);
408 } else {
409 correction = correction.sub(&x_shift_a);
410 }
411 }
412
413 // Compute result = x - correction
414 let mut result = value.sub(&correction);
415
416 // Final reduction: ensure result is in [0, p-1]
417 if result.ct_cmp(&BigInt::from_u64(0)) == core::cmp::Ordering::Less {
418 result = result.add(&self.modulus);
419 }
420
421 while result.ct_cmp(&self.modulus) != core::cmp::Ordering::Less {
422 result = result.sub(&self.modulus);
423 }
424
425 result
426 } else {
427 // Invalid constants, fall back to standard reduction
428 value.div_rem(&self.modulus).1
429 }
430 } else {
431 // No Solinas constants available, fall back to standard reduction
432 value.div_rem(&self.modulus).1
433 }
434 }
435
436 /// Perform multi-limb multiplication using a specified algorithm.
437 ///
438 /// This method provides fine-grained control over the multiplication algorithm
439 /// used for computing `a * b`. Different algorithms offer various trade-offs
440 /// between performance, memory usage, and computational complexity.
441 ///
442 /// # Arguments
443 /// * `a` - First operand
444 /// * `b` - Second operand
445 /// * `algorithm` - The multiplication algorithm to use
446 ///
447 /// # Returns
448 /// The product `a * b` computed using the specified algorithm
449 ///
450 /// # Supported Algorithms
451 /// - **`Standard`/`Schoolbook`**: Traditional long multiplication, O(n²)
452 /// - **`Karatsuba`**: Divide-and-conquer algorithm, O(n^1.585)
453 /// - **`ToomCook`**: Polynomial evaluation/interpolation, O(n^1.465)
454 /// - **`FFT`**: Fast Fourier Transform based, O(n log n)
455 ///
456 /// # Algorithm Selection Guidelines
457 /// - **Small operands** (< 1000 bits): Use `Standard` - overhead not worth it
458 /// - **Medium operands** (1000-10000 bits): Use `Karatsuba` - good balance
459 /// - **Large operands** (10000-50000 bits): Use `ToomCook` - better asymptotic
460 /// - **Very large operands** (>50000 bits): Use `FFT` - optimal asymptotic
461 ///
462 /// # Performance Notes
463 /// - Algorithm selection affects both time and space complexity
464 /// - Some algorithms may have higher constant factors
465 /// - FFT requires additional feature flags and may not be available
466 ///
467 /// # Examples
468 /// ```ignore
469 /// use crate::extensible::multiplication::MultiplicationAlgorithm;
470 ///
471 /// // Use Karatsuba for medium-sized numbers
472 /// let product = ctx.mul_with_algorithm(&a, &b, MultiplicationAlgorithm::Karatsuba);
473 ///
474 /// // Use FFT for very large numbers (if available)
475 /// let big_product = ctx.mul_with_algorithm(&x, &y, MultiplicationAlgorithm::FFT);
476 /// ```
477 pub fn mul_with_algorithm(
478 &self,
479 a: &BigInt,
480 b: &BigInt,
481 algorithm: MultiplicationAlgorithm,
482 ) -> BigInt {
483 match algorithm {
484 MultiplicationAlgorithm::Standard | MultiplicationAlgorithm::Schoolbook => a.mul(b),
485 MultiplicationAlgorithm::Karatsuba => {
486 super::multiplication::mul_karatsuba_standalone(a, b)
487 }
488 MultiplicationAlgorithm::ToomCook => {
489 super::multiplication::mul_toom_cook_standalone(a, b)
490 }
491 MultiplicationAlgorithm::FFT => super::multiplication::mul_fft_standalone(a, b),
492 }
493 }
494}