Skip to main content

oxiz_math/polynomial/
arithmetic.rs

1//! Arithmetic operations for polynomials.
2
3use super::types::*;
4#[allow(unused_imports)]
5use crate::prelude::*;
6use num_rational::BigRational;
7use num_traits::{One, Zero};
8
9impl super::Polynomial {
10    /// Negate the polynomial.
11    pub fn neg(&self) -> super::Polynomial {
12        super::Polynomial {
13            terms: self
14                .terms
15                .iter()
16                .map(|t| Term::new(-t.coeff.clone(), t.monomial.clone()))
17                .collect(),
18            order: self.order,
19        }
20    }
21
22    /// Add two polynomials.
23    pub fn add(&self, other: &super::Polynomial) -> super::Polynomial {
24        let mut terms: Vec<Term> = self.terms.clone();
25        terms.extend(other.terms.iter().cloned());
26        super::Polynomial::from_terms(terms, self.order)
27    }
28
29    /// Subtract two polynomials.
30    pub fn sub(&self, other: &super::Polynomial) -> super::Polynomial {
31        self.add(&other.neg())
32    }
33
34    /// Multiply by a scalar.
35    pub fn scale(&self, c: &BigRational) -> super::Polynomial {
36        if c.is_zero() {
37            return super::Polynomial::zero();
38        }
39        if c.is_one() {
40            return self.clone();
41        }
42        super::Polynomial {
43            terms: self
44                .terms
45                .iter()
46                .map(|t| Term::new(&t.coeff * c, t.monomial.clone()))
47                .collect(),
48            order: self.order,
49        }
50    }
51
52    /// Multiply two polynomials.
53    /// Uses Karatsuba algorithm for large univariate polynomials.
54    pub fn mul(&self, other: &super::Polynomial) -> super::Polynomial {
55        if self.is_zero() || other.is_zero() {
56            return super::Polynomial::zero();
57        }
58
59        // Use Karatsuba for large univariate polynomials (threshold: 16 terms)
60        if self.is_univariate()
61            && other.is_univariate()
62            && self.max_var() == other.max_var()
63            && self.terms.len() >= 16
64            && other.terms.len() >= 16
65        {
66            return self.mul_karatsuba(other);
67        }
68
69        let mut terms: Vec<Term> = Vec::with_capacity(self.terms.len() * other.terms.len());
70
71        for t1 in &self.terms {
72            for t2 in &other.terms {
73                terms.push(Term::new(
74                    &t1.coeff * &t2.coeff,
75                    t1.monomial.mul(&t2.monomial),
76                ));
77            }
78        }
79
80        super::Polynomial::from_terms(terms, self.order)
81    }
82
83    /// Karatsuba multiplication for univariate polynomials.
84    /// Time complexity: O(n^1.585) instead of O(n^2) for naive multiplication.
85    ///
86    /// This is particularly efficient for polynomials with 16+ terms.
87    fn mul_karatsuba(&self, other: &super::Polynomial) -> super::Polynomial {
88        debug_assert!(self.is_univariate() && other.is_univariate());
89        debug_assert!(self.max_var() == other.max_var());
90
91        let var = self.max_var();
92        let deg1 = self.degree(var);
93        let deg2 = other.degree(var);
94
95        // Base case: use naive multiplication for small polynomials
96        if deg1 <= 8 || deg2 <= 8 {
97            let mut terms: Vec<Term> = Vec::with_capacity(self.terms.len() * other.terms.len());
98            for t1 in &self.terms {
99                for t2 in &other.terms {
100                    terms.push(Term::new(
101                        &t1.coeff * &t2.coeff,
102                        t1.monomial.mul(&t2.monomial),
103                    ));
104                }
105            }
106            return super::Polynomial::from_terms(terms, self.order);
107        }
108
109        // Split at middle degree
110        let split = deg1.max(deg2).div_ceil(2);
111
112        // Split self = low0 + high0 * x^split
113        let (low0, high0) = self.split_at_degree(var, split);
114
115        // Split other = low1 + high1 * x^split
116        let (low1, high1) = other.split_at_degree(var, split);
117
118        // Karatsuba: (low0 + high0*x^m)(low1 + high1*x^m)
119        // = low0*low1 + ((low0+high0)(low1+high1) - low0*low1 - high0*high1)*x^m + high0*high1*x^(2m)
120
121        let z0 = low0.mul_karatsuba(&low1); // low0 * low1
122        let z2 = high0.mul_karatsuba(&high1); // high0 * high1
123
124        let sum0 = super::Polynomial::add(&low0, &high0);
125        let sum1 = super::Polynomial::add(&low1, &high1);
126        let z1_temp = sum0.mul_karatsuba(&sum1);
127
128        // z1 = z1_temp - z0 - z2
129        let z1 = super::Polynomial::sub(&super::Polynomial::sub(&z1_temp, &z0), &z2);
130
131        // Result = z0 + z1*x^split + z2*x^(2*split)
132        let x_split = Monomial::from_var_power(var, split);
133        let x_2split = Monomial::from_var_power(var, 2 * split);
134
135        let term1 = z1.mul_monomial(&x_split);
136        let term2 = z2.mul_monomial(&x_2split);
137
138        super::Polynomial::add(&super::Polynomial::add(&z0, &term1), &term2)
139    }
140
141    /// Split a univariate polynomial into low and high parts at a given degree.
142    /// Returns (low, high) where poly = low + high * x^deg.
143    fn split_at_degree(&self, var: Var, deg: u32) -> (super::Polynomial, super::Polynomial) {
144        let mut low_terms = Vec::new();
145        let mut high_terms = Vec::new();
146
147        for term in &self.terms {
148            let term_deg = term.monomial.degree(var);
149            if term_deg < deg {
150                low_terms.push(term.clone());
151            } else {
152                // Shift down the high part
153                if let Some(new_mon) = term.monomial.div(&Monomial::from_var_power(var, deg)) {
154                    high_terms.push(Term::new(term.coeff.clone(), new_mon));
155                }
156            }
157        }
158
159        let low = if low_terms.is_empty() {
160            super::Polynomial::zero()
161        } else {
162            super::Polynomial::from_terms(low_terms, self.order)
163        };
164
165        let high = if high_terms.is_empty() {
166            super::Polynomial::zero()
167        } else {
168            super::Polynomial::from_terms(high_terms, self.order)
169        };
170
171        (low, high)
172    }
173
174    /// Multiply by a monomial.
175    pub fn mul_monomial(&self, m: &Monomial) -> super::Polynomial {
176        if m.is_unit() {
177            return self.clone();
178        }
179        super::Polynomial {
180            terms: self
181                .terms
182                .iter()
183                .map(|t| Term::new(t.coeff.clone(), t.monomial.mul(m)))
184                .collect(),
185            order: self.order,
186        }
187    }
188
189    /// Compute p^k.
190    pub fn pow(&self, k: u32) -> super::Polynomial {
191        if k == 0 {
192            return super::Polynomial::one();
193        }
194        if k == 1 {
195            return self.clone();
196        }
197        if self.is_zero() {
198            return super::Polynomial::zero();
199        }
200
201        // Binary exponentiation
202        let mut result = super::Polynomial::one();
203        let mut base = self.clone();
204        let mut exp = k;
205
206        while exp > 0 {
207            if exp & 1 == 1 {
208                result = super::Polynomial::mul(&result, &base);
209            }
210            base = super::Polynomial::mul(&base, &base);
211            exp >>= 1;
212        }
213
214        result
215    }
216}