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}