clock_curve_math/extensible/multiplication.rs
1//! Advanced multiplication algorithms for extensible arithmetic.
2//!
3//! This module provides various multiplication algorithms optimized for
4//! different operand sizes and computational requirements.
5
6use crate::bigint::BigInt;
7
8/// Multiplication algorithms for multi-limb arithmetic.
9///
10/// This enum defines different algorithms for multiplying large multi-precision
11/// integers (BigInt). Each algorithm offers different trade-offs between
12/// performance, memory usage, and complexity. The choice of algorithm depends
13/// on the size of the operands and the computational requirements.
14///
15/// # Performance Characteristics
16/// - **Standard/Schoolbook**: O(n²) - Simple, good for small numbers
17/// - **Karatsuba**: O(n^1.585) - Good balance for medium-sized numbers
18/// - **Toom-Cook**: O(n^1.465) - Better for large numbers, more complex
19/// - **FFT**: O(n log n) - Optimal for very large numbers, highest overhead
20///
21/// # Memory Usage
22/// Different algorithms have different memory requirements and may allocate
23/// temporary BigInt values during computation.
24///
25/// # Algorithm Selection
26/// The library automatically selects appropriate algorithms based on operand
27/// sizes, but this enum allows manual override for specific use cases.
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29pub enum MultiplicationAlgorithm {
30 /// Standard long multiplication (schoolbook algorithm).
31 ///
32 /// This is the traditional "grade school" multiplication method that
33 /// multiplies each digit of one number by each digit of the other.
34 /// Simple to implement and optimal for small numbers.
35 ///
36 /// **Complexity**: O(n²)
37 /// **Memory**: O(1) additional space
38 /// **Best for**: Numbers with < 1000 bits
39 Standard,
40
41 /// Alias for Standard multiplication algorithm.
42 ///
43 /// Provided for clarity and compatibility. Same performance
44 /// characteristics as Standard.
45 Schoolbook,
46
47 /// Karatsuba multiplication algorithm.
48 ///
49 /// Reduces the multiplication of two n-digit numbers to three
50 /// multiplications of n/2-digit numbers using the identity:
51 /// (a₁·2^(n/2) + a₀) · (b₁·2^(n/2) + b₀) =
52 /// a₁b₁·2^n + (a₁b₁ + a₀b₀ - (a₀+a₁)(b₀+b₁))·2^(n/2) + a₀b₀
53 ///
54 /// **Complexity**: O(n^1.585)
55 /// **Memory**: O(n) additional space
56 /// **Best for**: Numbers with 1000-10000 bits
57 Karatsuba,
58
59 /// Toom-Cook multiplication algorithm (Toom-3 variant).
60 ///
61 /// Generalizes Karatsuba by splitting numbers into more parts and
62 /// evaluating at multiple points. Uses polynomial interpolation to
63 /// reconstruct the result. This implementation uses a simplified
64 /// 3-way split with evaluation at points 0, 1, -1, 2, and ∞.
65 ///
66 /// **Complexity**: O(n^1.465)
67 /// **Memory**: O(n) additional space
68 /// **Best for**: Numbers with 10000-50000 bits
69 ToomCook,
70
71 /// Fast Fourier Transform (FFT) based multiplication.
72 ///
73 /// Uses FFT to multiply large numbers by converting them to the
74 /// frequency domain. Requires the numbers to be treated as
75 /// coefficients of polynomials. Most efficient asymptotically but
76 /// has high constant factors and memory requirements.
77 ///
78 /// **Complexity**: O(n log n)
79 /// **Memory**: O(n) additional space
80 /// **Best for**: Numbers with >50000 bits
81 /// **Note**: May fall back to Toom-Cook for smaller sizes due to overhead
82 FFT,
83}
84
85/// Standalone Karatsuba multiplication implementation for BigInt.
86///
87/// Performs multiplication using the Karatsuba algorithm, which reduces
88/// the multiplication of two n-digit numbers to three multiplications of
89/// n/2-digit numbers. This provides better asymptotic performance than
90/// standard long multiplication for large operands.
91///
92/// The algorithm splits each operand into high and low parts, then computes:
93/// - z₀ = a_low × b_low
94/// - z₂ = a_high × b_high
95/// - z₁ = (a_low + a_high) × (b_low + b_high) - z₀ - z₂
96///
97/// The final result is: result = z₂ × 2^(2×half) + z₁ × 2^half + z₀
98///
99/// # Arguments
100/// * `a` - First operand
101/// * `b` - Second operand
102///
103/// # Returns
104/// The product `a × b` computed using Karatsuba multiplication
105///
106/// # Performance
107/// Falls back to standard multiplication for small operands (< 64 bits).
108/// For larger operands, provides O(n^1.585) complexity vs O(n²) for schoolbook.
109pub fn mul_karatsuba_standalone(a: &BigInt, b: &BigInt) -> BigInt {
110 // Base case: small numbers use standard multiplication
111 // Use a higher threshold to prevent excessive recursion
112 if a.bit_length() < 192 || b.bit_length() < 192 {
113 return a.mul(b);
114 }
115
116 // Split at 128-bit boundary (2 limbs) for simplicity and correctness
117 // This ensures we're working with clean limb boundaries
118 let half_limbs = 2; // 128 bits
119 let half_bits = half_limbs * 64;
120
121 // Create masks for the split
122 // Low part: bottom 128 bits
123 let low_mask = BigInt::from_limbs(&[u64::MAX, u64::MAX, 0, 0]);
124 // High part: shift right by 128 bits
125 let a_low = a.and(&low_mask);
126 let a_high = a.shr(half_bits);
127 let b_low = b.and(&low_mask);
128 let b_high = b.shr(half_bits);
129
130 // Compute the three products using Karatsuba's algorithm
131 let z0 = mul_karatsuba_standalone(&a_low, &b_low);
132 let z2 = mul_karatsuba_standalone(&a_high, &b_high);
133 let z1 = mul_karatsuba_standalone(&(a_low.add(&a_high)), &(b_low.add(&b_high)));
134
135 // z1 = (a_low + a_high) * (b_low + b_high) - z0 - z2
136 let z1 = z1.sub(&z0).sub(&z2);
137
138 // Combine results: result = z2 * 2^(2*half_bits) + z1 * 2^half_bits + z0
139 let result = z2.shl(2 * half_bits).add(&z1.shl(half_bits)).add(&z0);
140
141 result
142}
143
144/// Standalone Toom-Cook multiplication implementation for BigInt.
145///
146/// Performs multiplication using a simplified Toom-3 algorithm, which splits
147/// numbers into three parts and evaluates polynomial multiplication at
148/// specific points (0, 1, -1, 2, ∞) for interpolation.
149///
150/// The algorithm:
151/// 1. Splits operands into three equal parts: a = a₂·2^(2s) + a₁·2^s + a₀
152/// 2. Evaluates products at evaluation points
153/// 3. Uses polynomial interpolation to reconstruct coefficients
154/// 4. Combines results with appropriate shifts
155///
156/// # Arguments
157/// * `a` - First operand
158/// * `b` - Second operand
159///
160/// # Returns
161/// The product `a × b` computed using Toom-Cook multiplication
162///
163/// # Performance
164/// Falls back to Karatsuba multiplication for small operands (< 256 bits).
165/// For larger operands, provides O(n^1.465) complexity.
166/// Uses simplified interpolation - full Toom-Cook would be more accurate but complex.
167pub fn mul_toom_cook_standalone(a: &BigInt, b: &BigInt) -> BigInt {
168 // Base case: fallback to Karatsuba for smaller numbers
169 if a.bit_length() < 256 || b.bit_length() < 256 {
170 return mul_karatsuba_standalone(a, b);
171 }
172
173 // For this implementation, we'll use a simplified Toom-3 algorithm
174 // Split numbers into 3 parts
175 let n = core::cmp::max(a.bit_length(), b.bit_length());
176 let part_size = (n + 2) / 3;
177
178 let mask1 = (BigInt::from_u64(1) << part_size) - BigInt::from_u64(1);
179
180 // Split a into a0, a1, a2
181 let a0 = a.and(&mask1);
182 let a1 = a.shr(part_size).and(&mask1);
183 let a2 = a.shr(2 * part_size);
184
185 // Split b into b0, b1, b2
186 let b0 = b.and(&mask1);
187 let b1 = b.shr(part_size).and(&mask1);
188 let b2 = b.shr(2 * part_size);
189
190 // Evaluate at points 0, 1, -1, 2, infinity
191 let p0 = mul_karatsuba_standalone(&a0, &b0);
192 let p1 = mul_karatsuba_standalone(&a0.add(&a1).add(&a2), &b0.add(&b1).add(&b2));
193 let p_minus1 = mul_karatsuba_standalone(&a0.sub(&a1).add(&a2), &b0.sub(&b1).add(&b2));
194 let p2 = mul_karatsuba_standalone(
195 &a0.add(&a1.shl(1)).add(&a2.shl(2)),
196 &b0.add(&b1.shl(1)).add(&b2.shl(2)),
197 );
198 let p_inf = mul_karatsuba_standalone(&a2, &b2);
199
200 // Interpolate to get coefficients
201 // This is a simplified interpolation - full Toom-Cook would be more complex
202 let c4 = p_inf;
203 let c3 = (p2.sub(&p_minus1)).div_rem(&BigInt::from_u64(3)).0;
204 let c2 = (p1.sub(&p_minus1).shr(1)).sub(&p2.shr(2));
205 let c1 = p_minus1.sub(&p0);
206 let c0 = p0;
207
208 // Combine results
209 c4.shl(4 * part_size)
210 .add(&c3.shl(3 * part_size))
211 .add(&c2.shl(2 * part_size))
212 .add(&c1.shl(part_size))
213 .add(&c0)
214}
215
216/// Standalone FFT multiplication implementation for BigInt.
217///
218/// Performs multiplication using FFT-based algorithms for extremely large operands.
219/// FFT multiplication provides asymptotically optimal O(n log n) complexity but
220/// has high constant factors and memory requirements.
221///
222/// The implementation uses a configurable threshold (10,000 bits) above which
223/// FFT multiplication is attempted. For smaller operands, it falls back to
224/// Toom-Cook multiplication to avoid FFT overhead.
225///
226/// # Arguments
227/// * `a` - First operand
228/// * `b` - Second operand
229///
230/// # Returns
231/// The product `a × b` computed using FFT-based multiplication
232///
233/// # Performance
234/// Only beneficial for very large numbers (>10,000 bits). For cryptographic
235/// applications, Toom-Cook multiplication usually provides sufficient performance.
236/// Delegates to the FFT module's standalone implementation for actual computation.
237pub fn mul_fft_standalone(a: &BigInt, b: &BigInt) -> BigInt {
238 // FFT multiplication for extremely large operands
239 // FFT provides O(n log n) complexity vs O(n^2) for schoolbook multiplication
240
241 let a_bits = a.bit_length();
242 let b_bits = b.bit_length();
243 let max_bits = core::cmp::max(a_bits, b_bits);
244
245 // Use FFT for large numbers, fall back to Toom-Cook for smaller ones
246 // The threshold can be tuned based on performance characteristics
247 const FFT_THRESHOLD_BITS: u32 = 10000; // Use FFT for numbers > 10,000 bits
248
249 if max_bits < FFT_THRESHOLD_BITS {
250 return mul_toom_cook_standalone(a, b);
251 }
252
253 // Use FFT for very large numbers
254 // This provides asymptotically optimal multiplication performance
255 crate::extensible::fft::mul_fft_standalone(a, b)
256}
257
258/// Implementation variant of Karatsuba multiplication.
259///
260/// This is an optimized implementation of the Karatsuba algorithm that uses
261/// direct BigInt operations instead of recursive calls. It provides the same
262/// mathematical algorithm as `mul_karatsuba_standalone` but with potentially
263/// better performance for certain operand sizes.
264///
265/// Uses the Karatsuba identity to reduce multiplication complexity:
266/// (a₁·2^(n/2) + a₀) · (b₁·2^(n/2) + b₀) =
267/// a₁b₁·2^n + (a₁b₁ + a₀b₀ - (a₀+a₁)(b₀+b₁))·2^(n/2) + a₀b₀
268///
269/// # Arguments
270/// * `a` - First operand
271/// * `b` - Second operand
272///
273/// # Returns
274/// The product `a × b` computed using the Karatsuba algorithm
275///
276/// # Performance
277/// Uses standard multiplication for small operands (< 64 bits).
278/// For larger operands, provides O(n^1.585) asymptotic complexity.
279/// Avoids recursion overhead compared to the standalone version.
280pub fn mul_karatsuba_impl(a: &BigInt, b: &BigInt) -> BigInt {
281 // Base case: small numbers use standard multiplication
282 if a.bit_length() < 64 || b.bit_length() < 64 {
283 return a.mul(b);
284 }
285
286 // Split numbers into high and low parts
287 let n = core::cmp::max(a.bit_length(), b.bit_length());
288 let half = n / 2;
289
290 // Create bit mask for splitting
291 let mask = (BigInt::from_u64(1) << half) - BigInt::from_u64(1);
292
293 // Split a: a = a_high * 2^half + a_low
294 let a_low = a.and(&mask);
295 let a_high = a.shr(half);
296
297 // Split b: b = b_high * 2^half + b_low
298 let b_low = b.and(&mask);
299 let b_high = b.shr(half);
300
301 // Compute the three products using Karatsuba's algorithm
302 let z0 = a_low.mul(&b_low);
303 let z2 = a_high.mul(&b_high);
304 let z1 = (a_low.add(&a_high))
305 .mul(&b_low.add(&b_high))
306 .sub(&z0)
307 .sub(&z2);
308
309 // Combine results: result = z2 * 2^(2*half) + z1 * 2^half + z0
310 z2.shl(2 * half).add(&z1.shl(half)).add(&z0)
311}
312
313/// Implementation variant of Toom-Cook multiplication.
314///
315/// This is an optimized implementation of the Toom-3 algorithm using direct
316/// BigInt operations. It splits operands into three parts and uses polynomial
317/// evaluation/interpolation for multiplication, providing better asymptotic
318/// performance than Karatsuba for large operands.
319///
320/// The algorithm evaluates the product polynomial at points {0, 1, -1, 2, ∞}
321/// and uses interpolation to recover the result coefficients. This implementation
322/// uses simplified interpolation formulas for efficiency.
323///
324/// # Arguments
325/// * `a` - First operand
326/// * `b` - Second operand
327///
328/// # Returns
329/// The product `a × b` computed using the Toom-3 algorithm
330///
331/// # Performance
332/// Falls back to Karatsuba for small operands (< 256 bits).
333/// For larger operands, provides O(n^1.465) asymptotic complexity.
334/// Uses direct operations rather than recursive calls for better performance.
335pub fn mul_toom_cook_impl(a: &BigInt, b: &BigInt) -> BigInt {
336 // Base case: fallback to Karatsuba for smaller numbers
337 if a.bit_length() < 256 || b.bit_length() < 256 {
338 return mul_karatsuba_impl(a, b);
339 }
340
341 // For this implementation, we'll use a simplified Toom-3 algorithm
342 // Split numbers into 3 parts
343 let n = core::cmp::max(a.bit_length(), b.bit_length());
344 let part_size = (n + 2) / 3;
345
346 let mask1 = (BigInt::from_u64(1) << part_size) - BigInt::from_u64(1);
347
348 // Split a into a0, a1, a2
349 let a0 = a.and(&mask1);
350 let a1 = a.shr(part_size).and(&mask1);
351 let a2 = a.shr(2 * part_size);
352
353 // Split b into b0, b1, b2
354 let b0 = b.and(&mask1);
355 let b1 = b.shr(part_size).and(&mask1);
356 let b2 = b.shr(2 * part_size);
357
358 // Evaluate at points 0, 1, -1, 2, infinity
359 let p0 = a0.mul(&b0);
360 let p1 = (a0.add(&a1).add(&a2)).mul(&b0.add(&b1).add(&b2));
361 let p_minus1 = (a0.sub(&a1).add(&a2)).mul(&b0.sub(&b1).add(&b2));
362 let p2 = (a0.add(&a1.shl(1)).add(&a2.shl(2))).mul(&b0.add(&b1.shl(1)).add(&b2.shl(2)));
363 let p_inf = a2.mul(&b2);
364
365 // Interpolate to get coefficients
366 // This is a simplified interpolation - full Toom-Cook would be more complex
367 let c4 = p_inf;
368 let c3 = (p2.sub(&p_minus1)).div_rem(&BigInt::from_u64(3)).0;
369 let c2 = (p1.sub(&p_minus1).shr(1)).sub(&p2.shr(2));
370 let c1 = p_minus1.sub(&p0);
371 let c0 = p0;
372
373 // Combine results
374 c4.shl(4 * part_size)
375 .add(&c3.shl(3 * part_size))
376 .add(&c2.shl(2 * part_size))
377 .add(&c1.shl(part_size))
378 .add(&c0)
379}
380
381/// Implementation variant of FFT-based multiplication.
382///
383/// This function provides FFT-based multiplication for extremely large operands.
384/// It uses the FFT multiplier implementation from the fft module to achieve
385/// O(n log n) asymptotic complexity for multiplication.
386///
387/// The FFT implementation requires the "std" feature for trigonometric functions
388/// and memory allocation. Without std, it falls back to Toom-Cook multiplication.
389///
390/// # Arguments
391/// * `a` - First operand
392/// * `b` - Second operand
393///
394/// # Returns
395/// The product `a × b` computed using FFT-based multiplication
396///
397/// # Performance
398/// Uses FFT multiplication for large operands (>10,000 bits) when std feature
399/// is enabled, providing O(n log n) complexity. Falls back to Toom-Cook for
400/// smaller operands or when std is not available.
401///
402/// # Features
403/// Requires the "std" feature for full FFT functionality. Without std,
404/// falls back to Toom-Cook multiplication.
405pub fn mul_fft_impl(a: &BigInt, b: &BigInt) -> BigInt {
406 // Delegate to the FFT module's standalone implementation
407 // This provides the actual FFT multiplication when std feature is available
408 crate::extensible::fft::mul_fft_standalone(a, b)
409}