clock_curve_math/extensible/
fft.rs

1//! Fast Fourier Transform (FFT) implementation for polynomial multiplication.
2//!
3//! This module provides O(n log n) multiplication of large integers using
4//! the Fast Fourier Transform algorithm. It serves as a high-performance
5//! alternative to Karatsuba and Toom-Cook multiplication for very large operands.
6//!
7//! Note: Full FFT operations require the "std" feature for trigonometric functions
8//! and memory allocation. Without std, it falls back to schoolbook multiplication.
9
10use crate::bigint::BigInt;
11use crate::error::MathError;
12
13/// Complex number representation for FFT
14///
15/// This struct provides basic complex arithmetic operations needed
16/// for Fast Fourier Transform algorithms. It includes addition,
17/// multiplication, and scaling operations optimized for FFT usage.
18#[derive(Clone, Copy, Debug)]
19pub struct Complex {
20    /// The real part of the complex number
21    pub real: f64,
22    /// The imaginary part of the complex number
23    pub imag: f64,
24}
25
26impl Complex {
27    /// Create a new complex number
28    ///
29    /// # Arguments
30    /// * `real` - The real part
31    /// * `imag` - The imaginary part
32    ///
33    /// # Returns
34    /// A new Complex number with the specified real and imaginary parts
35    pub const fn new(real: f64, imag: f64) -> Self {
36        Self { real, imag }
37    }
38
39    /// Create a complex number from a real number
40    ///
41    /// # Arguments
42    /// * `real` - The real value to convert
43    ///
44    /// # Returns
45    /// A complex number with the real value and zero imaginary part
46    pub const fn from_real(real: f64) -> Self {
47        Self::new(real, 0.0)
48    }
49
50    /// Complex addition
51    ///
52    /// # Arguments
53    /// * `other` - The complex number to add
54    ///
55    /// # Returns
56    /// The sum of self and other
57    pub fn add(self, other: Self) -> Self {
58        Self::new(self.real + other.real, self.imag + other.imag)
59    }
60
61    /// Complex subtraction
62    ///
63    /// # Arguments
64    /// * `other` - The complex number to subtract
65    ///
66    /// # Returns
67    /// The difference of self and other
68    pub fn sub(self, other: Self) -> Self {
69        Self::new(self.real - other.real, self.imag - other.imag)
70    }
71
72    /// Complex multiplication
73    ///
74    /// # Arguments
75    /// * `other` - The complex number to multiply
76    ///
77    /// # Returns
78    /// The product of self and other
79    pub fn mul(self, other: Self) -> Self {
80        Self::new(
81            self.real * other.real - self.imag * other.imag,
82            self.real * other.imag + self.imag * other.real,
83        )
84    }
85
86    /// Scalar multiplication
87    ///
88    /// # Arguments
89    /// * `scalar` - The real number to multiply by
90    ///
91    /// # Returns
92    /// The complex number scaled by the scalar
93    pub fn scale(self, scalar: f64) -> Self {
94        Self::new(self.real * scalar, self.imag * scalar)
95    }
96
97    /// Complex exponential (Euler's formula)
98    ///
99    /// Computes e^(i*angle) = cos(angle) + i*sin(angle)
100    ///
101    /// # Arguments
102    /// * `angle` - The angle in radians
103    ///
104    /// # Returns
105    /// The complex exponential of the angle
106    #[cfg(feature = "std")]
107    pub fn exp(angle: f64) -> Self {
108        Self::new(angle.cos(), angle.sin())
109    }
110
111    /// Fallback for no_std - requires std feature
112    ///
113    /// This function is not available in no_std environments and will panic.
114    /// Use the std feature to enable FFT functionality.
115    #[cfg(not(feature = "std"))]
116    pub fn exp(_angle: f64) -> Self {
117        panic!("FFT requires std feature for trigonometric functions")
118    }
119}
120
121/// FFT multiplier for large integer multiplication
122///
123/// This struct provides Fast Fourier Transform-based multiplication
124/// for very large integers. It offers O(n log n) complexity compared
125/// to O(n²) for schoolbook multiplication, making it ideal for
126/// cryptographic applications dealing with large numbers.
127///
128/// The FFT implementation requires the "std" feature for trigonometric
129/// functions and memory allocation. Without std, it falls back to
130/// schoolbook multiplication.
131#[cfg(feature = "std")]
132pub struct FFTMultiplier {
133    // No fields needed for basic implementation
134}
135
136#[cfg(feature = "std")]
137impl FFTMultiplier {
138    /// Create a new FFT multiplier
139    ///
140    /// # Returns
141    /// A new FFTMultiplier instance ready for large integer multiplication
142    pub fn new() -> Self {
143        Self {}
144    }
145
146    /// Check if n is a power of 2
147    ///
148    /// # Arguments
149    /// * `n` - The number to check
150    ///
151    /// # Returns
152    /// true if n is a power of 2, false otherwise
153    fn is_power_of_two(n: usize) -> bool {
154        n > 0 && (n & (n - 1)) == 0
155    }
156
157    /// Find next power of 2 >= n
158    ///
159    /// This is used to pad input arrays to optimal FFT sizes.
160    ///
161    /// # Arguments
162    /// * `n` - The minimum size required
163    ///
164    /// # Returns
165    /// The smallest power of 2 that is >= n
166    fn next_power_of_two(n: usize) -> usize {
167        if n <= 1 {
168            return 1;
169        }
170        // Use bit manipulation to find next power of 2
171        let mut result = 1;
172        while result < n {
173            result <<= 1;
174        }
175        result
176    }
177
178    /// Perform bit-reversal permutation on complex array
179    ///
180    /// This reorders the array elements to match the Cooley-Tukey
181    /// FFT algorithm's requirements, improving cache locality.
182    ///
183    /// # Arguments
184    /// * `coeffs` - The complex coefficient array to permute in-place
185    fn bit_reverse_permutation(coeffs: &mut [Complex]) {
186        let n = coeffs.len();
187        let log_n = (n as f64).log2() as usize;
188
189        for i in 0..n {
190            let mut j = 0;
191            for k in 0..log_n {
192                if (i & (1 << k)) != 0 {
193                    j |= 1 << (log_n - 1 - k);
194                }
195            }
196            if j > i {
197                coeffs.swap(i, j);
198            }
199        }
200    }
201
202    /// Cooley-Tukey Fast Fourier Transform
203    ///
204    /// Implements the iterative Cooley-Tukey FFT algorithm for transforming
205    /// a sequence of complex coefficients between time and frequency domains.
206    ///
207    /// # Arguments
208    /// * `coeffs` - The complex coefficient array (modified in-place)
209    /// * `inverse` - If true, compute inverse FFT; if false, forward FFT
210    ///
211    /// # Panics
212    /// Panics if the length of coeffs is not a power of 2
213    pub fn cooley_tukey_fft(&self, coeffs: &mut [Complex], inverse: bool) {
214        let n = coeffs.len();
215
216        // Bit-reversal permutation
217        Self::bit_reverse_permutation(coeffs);
218
219        // Iterative Cooley-Tukey algorithm
220        let mut size = 2;
221        while size <= n {
222            let half_size = size / 2;
223            let angle_step =
224                if inverse { 2.0 } else { -2.0 } * std::f64::consts::PI / (size as f64);
225            let wlen = Complex::exp(angle_step);
226
227            let mut i = 0;
228            while i < n {
229                let mut w = Complex::new(1.0, 0.0);
230                for j in 0..half_size {
231                    let u = coeffs[i + j];
232                    let v = coeffs[i + j + half_size].mul(w);
233                    coeffs[i + j] = u.add(v);
234                    coeffs[i + j + half_size] = u.sub(v);
235                    w = w.mul(wlen);
236                }
237                i += size;
238            }
239
240            size *= 2;
241        }
242
243        // Scale for inverse transform
244        if inverse {
245            let scale = 1.0 / (n as f64);
246            for coeff in coeffs.iter_mut() {
247                *coeff = coeff.scale(scale);
248            }
249        }
250    }
251
252    /// Convert BigInt coefficients to complex Vec
253    fn bigint_to_complex(coeffs: &[BigInt]) -> Vec<Complex> {
254        coeffs
255            .iter()
256            .map(|c| {
257                // Convert BigInt to f64 (this may lose precision for very large numbers)
258                // For cryptographic use, we limit to reasonable sizes
259                let real_val = c.to_u64().unwrap_or(0) as f64;
260                Complex::from_real(real_val)
261            })
262            .collect()
263    }
264
265    /// Convert complex array back to rounded integer coefficients
266    fn complex_to_coeffs(complex_coeffs: &[Complex]) -> Vec<i64> {
267        complex_coeffs
268            .iter()
269            .map(|c| {
270                // Round to nearest integer
271                c.real.round() as i64
272            })
273            .collect()
274    }
275
276    /// Multiply two polynomials using FFT
277    ///
278    /// This implements the standard FFT-based polynomial multiplication algorithm
279    /// with O(n log n) complexity instead of O(n²) for schoolbook multiplication.
280    ///
281    /// The algorithm:
282    /// 1. Convert coefficients to complex numbers
283    /// 2. Pad to next power of 2 length for optimal FFT performance
284    /// 3. Compute forward FFT of both polynomials
285    /// 4. Multiply pointwise in frequency domain
286    /// 5. Compute inverse FFT to get result coefficients
287    /// 6. Extract and round integer coefficients
288    ///
289    /// # Arguments
290    /// * `a` - Coefficients of the first polynomial
291    /// * `b` - Coefficients of the second polynomial
292    ///
293    /// # Returns
294    /// Result coefficients of the product polynomial, or MathError on failure
295    pub fn polynomial_multiply(
296        &self,
297        a: &[BigInt],
298        b: &[BigInt],
299    ) -> Result<Vec<BigInt>, MathError> {
300        // Determine FFT size
301        let n = Self::next_power_of_two(a.len() + b.len() - 1);
302
303        // Convert to complex numbers and pad
304        let mut a_complex = Self::bigint_to_complex(a);
305        let mut b_complex = Self::bigint_to_complex(b);
306
307        a_complex.resize(n, Complex::from_real(0.0));
308        b_complex.resize(n, Complex::from_real(0.0));
309
310        // Forward FFT
311        self.cooley_tukey_fft(&mut a_complex, false);
312        self.cooley_tukey_fft(&mut b_complex, false);
313
314        // Pointwise multiplication in frequency domain
315        let mut c_complex: Vec<Complex> = a_complex
316            .iter()
317            .zip(b_complex.iter())
318            .map(|(x, y)| x.mul(*y))
319            .collect();
320
321        // Inverse FFT
322        self.cooley_tukey_fft(&mut c_complex, true);
323
324        // Convert back to integer coefficients
325        let coeffs_i64 = Self::complex_to_coeffs(&c_complex);
326
327        // Convert to BigInt and remove trailing zeros
328        let mut result: Vec<BigInt> = coeffs_i64
329            .iter()
330            .map(|&x| {
331                if x >= 0 {
332                    BigInt::from_u64(x as u64)
333                } else {
334                    // Handle negative coefficients (shouldn't happen in multiplication)
335                    BigInt::from_u64(0)
336                }
337            })
338            .collect();
339
340        // Remove trailing zeros
341        while result.len() > 1 {
342            if let Some(last) = result.last() {
343                if last.is_zero() {
344                    result.pop();
345                } else {
346                    break;
347                }
348            } else {
349                break;
350            }
351        }
352
353        Ok(result)
354    }
355
356    /// Multiply two large integers using FFT
357    ///
358    /// This method provides asymptotically optimal multiplication for very large integers
359    /// by converting them to polynomial coefficients, multiplying using FFT, and converting
360    /// back to integer representation.
361    ///
362    /// # Arguments
363    /// * `a` - First large integer to multiply
364    /// * `b` - Second large integer to multiply
365    ///
366    /// # Returns
367    /// The product a * b as a BigInt, or MathError on failure
368    ///
369    /// # Performance
370    /// Offers O(n log n) complexity for n-bit numbers vs O(n²) for schoolbook multiplication
371    pub fn multiply_big_ints(&self, a: &BigInt, b: &BigInt) -> Result<BigInt, MathError> {
372        // Use base 10^9 for coefficient conversion (good balance of precision and efficiency)
373        const BASE: u64 = 1_000_000_000;
374
375        // Convert to base-10^9 digits
376        let a_coeffs = Self::big_int_to_base_digits(a, BASE);
377        let b_coeffs = Self::big_int_to_base_digits(b, BASE);
378
379        // Multiply polynomials
380        let product_coeffs = self.polynomial_multiply(&a_coeffs, &b_coeffs)?;
381
382        // Convert back to BigInt
383        let result = Self::coeffs_to_big_int(&product_coeffs, BASE);
384
385        Ok(result)
386    }
387
388    /// Convert BigInt to base digits
389    ///
390    /// Decomposes a large integer into digits in the specified base.
391    /// This is used to convert integers to polynomial coefficients for FFT multiplication.
392    ///
393    /// # Arguments
394    /// * `n` - The BigInt to convert
395    /// * `base` - The numerical base for digit decomposition
396    ///
397    /// # Returns
398    /// Vector of digits in the specified base (least significant first)
399    fn big_int_to_base_digits(n: &BigInt, base: u64) -> Vec<BigInt> {
400        if n.is_zero() {
401            return vec![BigInt::from_u64(0)];
402        }
403
404        let mut digits = Vec::new();
405        let mut current = n.clone();
406        let base_big = BigInt::from_u64(base);
407
408        while !current.is_zero() {
409            let (quotient, remainder) = current.div_rem(&base_big);
410            digits.push(remainder);
411            current = quotient;
412        }
413
414        digits
415    }
416
417    /// Convert coefficient array back to BigInt
418    ///
419    /// Reconstructs a BigInt from its base-digit representation.
420    ///
421    /// # Arguments
422    /// * `coeffs` - The digit coefficients (least significant first)
423    /// * `base` - The numerical base used for the digits
424    ///
425    /// # Returns
426    /// The reconstructed BigInt value
427    fn coeffs_to_big_int(coeffs: &[BigInt], base: u64) -> BigInt {
428        let mut result = BigInt::from_u64(0);
429        let base_big = BigInt::from_u64(base);
430
431        for (i, coeff) in coeffs.iter().enumerate() {
432            if i == 0 {
433                result = coeff.clone();
434            } else {
435                // result += coeff * (base^i)
436                // For efficiency, we use iterative multiplication instead of pow
437                let mut power = BigInt::from_u64(1);
438                for _ in 0..i {
439                    power = power.mul(&base_big);
440                }
441                let term = coeff.mul(&power);
442                result = result.add(&term);
443            }
444        }
445
446        result
447    }
448}
449
450/// Fallback FFT multiplier for no_std environments
451///
452/// This implementation provides API compatibility when the std feature is not available.
453/// It falls back to basic schoolbook multiplication since FFT requires trigonometric
454/// functions and complex arithmetic which are not available in no_std.
455#[cfg(not(feature = "std"))]
456pub struct FFTMultiplier;
457
458#[cfg(not(feature = "std"))]
459impl FFTMultiplier {
460    /// Create a new FFT multiplier (no_std fallback)
461    ///
462    /// # Returns
463    /// A new FFTMultiplier instance that uses schoolbook multiplication
464    pub const fn new() -> Self {
465        Self
466    }
467
468    /// Multiply using schoolbook multiplication in no_std
469    ///
470    /// Since FFT requires std features for trigonometric functions,
471    /// this falls back to basic BigInt multiplication.
472    ///
473    /// # Arguments
474    /// * `a` - First integer to multiply
475    /// * `b` - Second integer to multiply
476    ///
477    /// # Returns
478    /// The product a * b
479    pub fn multiply_big_ints(&self, a: &BigInt, b: &BigInt) -> Result<BigInt, MathError> {
480        // In no_std, fall back to schoolbook multiplication
481        // FFT requires std feature for trigonometric functions
482        Ok(mul_schoolbook(a, b))
483    }
484}
485
486/// Schoolbook multiplication for fallback
487///
488/// Basic polynomial multiplication using BigInt's built-in multiplication.
489/// This serves as the fallback when FFT is not available.
490fn mul_schoolbook(a: &BigInt, b: &BigInt) -> BigInt {
491    a.mul(b)
492}
493
494/// Standalone FFT multiplication function
495///
496/// This is the main entry point for FFT-based multiplication in the library.
497/// It automatically chooses between FFT and fallback algorithms based on input size
498/// and available features.
499///
500/// # Algorithm Selection:
501/// - Uses FFT for very large numbers (>10,000 bits) when std feature is enabled
502/// - Falls back to Toom-Cook multiplication for medium-sized numbers
503/// - Uses schoolbook multiplication for small numbers or when std is unavailable
504///
505/// # Arguments
506/// * `a` - First large integer to multiply
507/// * `b` - Second large integer to multiply
508///
509/// # Returns
510/// The product a * b as a BigInt
511///
512/// # Performance
513/// Provides O(n log n) complexity for large integers where FFT is beneficial
514pub fn mul_fft_standalone(a: &BigInt, b: &BigInt) -> BigInt {
515    // FFT multiplication for extremely large operands
516    // FFT provides O(n log n) complexity vs O(n^2) for schoolbook multiplication
517
518    let a_bits = a.bit_length();
519    let b_bits = b.bit_length();
520    let max_bits = core::cmp::max(a_bits, b_bits);
521
522    // Use FFT threshold - in practice this would be much lower with full implementation
523    const FFT_THRESHOLD_BITS: u32 = 10000; // Use FFT for numbers > 10,000 bits
524
525    if max_bits < FFT_THRESHOLD_BITS {
526        return super::multiplication::mul_toom_cook_standalone(a, b);
527    }
528
529    // Use FFT for very large numbers (requires std feature)
530    #[cfg(feature = "std")]
531    {
532        let multiplier = FFTMultiplier::new();
533        match multiplier.multiply_big_ints(a, b) {
534            Ok(result) => result,
535            Err(_) => {
536                // Fall back to Toom-Cook if FFT fails
537                super::multiplication::mul_toom_cook_standalone(a, b)
538            }
539        }
540    }
541
542    #[cfg(not(feature = "std"))]
543    {
544        // Fall back to Toom-Cook in no_std environments
545        super::multiplication::mul_toom_cook_standalone(a, b)
546    }
547}
548
549#[cfg(test)]
550mod tests {
551    use super::*;
552
553    /// Test basic complex number arithmetic operations
554    #[test]
555    fn test_complex_arithmetic() {
556        let z1 = Complex::new(1.0, 2.0);
557        let z2 = Complex::new(3.0, 4.0);
558
559        let sum = z1.add(z2);
560        assert!((sum.real - 4.0).abs() < 1e-10);
561        assert!((sum.imag - 6.0).abs() < 1e-10);
562
563        let product = z1.mul(z2);
564        assert!((product.real - -5.0).abs() < 1e-10);
565        assert!((product.imag - 10.0).abs() < 1e-10);
566    }
567
568    /// Test power-of-two detection utility function
569    #[cfg(feature = "std")]
570    #[test]
571    fn test_power_of_two() {
572        assert!(FFTMultiplier::is_power_of_two(1));
573        assert!(FFTMultiplier::is_power_of_two(2));
574        assert!(FFTMultiplier::is_power_of_two(4));
575        assert!(FFTMultiplier::is_power_of_two(8));
576        assert!(!FFTMultiplier::is_power_of_two(3));
577        assert!(!FFTMultiplier::is_power_of_two(6));
578    }
579
580    /// Test next power-of-two calculation
581    #[cfg(feature = "std")]
582    #[test]
583    fn test_next_power_of_two() {
584        assert_eq!(FFTMultiplier::next_power_of_two(1), 1);
585        assert_eq!(FFTMultiplier::next_power_of_two(2), 2);
586        assert_eq!(FFTMultiplier::next_power_of_two(3), 4);
587        assert_eq!(FFTMultiplier::next_power_of_two(5), 8);
588        assert_eq!(FFTMultiplier::next_power_of_two(9), 16);
589    }
590
591    /// Test polynomial multiplication using FFT
592    #[cfg(feature = "std")]
593    #[test]
594    fn test_fft_polynomial_multiply() {
595        let multiplier = FFTMultiplier::new();
596
597        // (1 + 2x + 3x²) * (4 + 5x) = 4 + 13x + 22x² + 15x³
598        let a = vec![
599            BigInt::from_u64(1),
600            BigInt::from_u64(2),
601            BigInt::from_u64(3),
602        ];
603        let b = vec![BigInt::from_u64(4), BigInt::from_u64(5)];
604
605        let result = multiplier.polynomial_multiply(&a, &b).unwrap();
606
607        assert_eq!(result.len(), 4);
608        assert_eq!(result[0].to_u64().unwrap(), 4);
609        assert_eq!(result[1].to_u64().unwrap(), 13);
610        assert_eq!(result[2].to_u64().unwrap(), 22);
611        assert_eq!(result[3].to_u64().unwrap(), 15);
612    }
613
614    /// Test big integer multiplication using FFT
615    #[test]
616    fn test_big_int_multiply() {
617        #[cfg(feature = "std")]
618        let multiplier = FFTMultiplier::new();
619
620        #[cfg(not(feature = "std"))]
621        let multiplier = FFTMultiplier;
622
623        let a = BigInt::from_u64(123);
624        let b = BigInt::from_u64(456);
625        let expected = BigInt::from_u64(56088);
626
627        let result = multiplier.multiply_big_ints(&a, &b).unwrap();
628        assert_eq!(result, expected);
629    }
630
631    /// Test standalone FFT multiplication function
632    #[test]
633    fn test_fft_standalone_fallback() {
634        let a = BigInt::from_u64(123);
635        let b = BigInt::from_u64(456);
636        let expected = BigInt::from_u64(56088);
637
638        let result = mul_fft_standalone(&a, &b);
639        assert_eq!(result, expected);
640    }
641}