mathhook_core/core/polynomial/poly/
arithmetic.rs

1use super::Poly;
2use crate::core::polynomial::traits::Ring;
3use std::ops::{Add, Mul, Neg, Sub};
4
5impl<T: Ring> Poly<T> {
6    /// Polynomial addition
7    #[inline]
8    pub fn add(&self, other: &Self) -> Self {
9        let max_len = self.coeffs.len().max(other.coeffs.len());
10        let mut result = Vec::with_capacity(max_len);
11
12        for i in 0..max_len {
13            let a = self.coeff(i);
14            let b = other.coeff(i);
15            result.push(a.wrapping_add(&b));
16        }
17
18        Self::from_coeffs(result)
19    }
20
21    /// Polynomial subtraction
22    #[inline]
23    pub fn sub(&self, other: &Self) -> Self {
24        let max_len = self.coeffs.len().max(other.coeffs.len());
25        let mut result = Vec::with_capacity(max_len);
26
27        for i in 0..max_len {
28            let a = self.coeff(i);
29            let b = other.coeff(i);
30            result.push(a.wrapping_sub(&b));
31        }
32
33        Self::from_coeffs(result)
34    }
35
36    /// Polynomial multiplication (schoolbook algorithm)
37    ///
38    /// O(n*m) where n, m are degrees
39    #[inline]
40    pub fn mul(&self, other: &Self) -> Self {
41        if self.is_zero() || other.is_zero() {
42            return Self::zero();
43        }
44
45        let result_len = self.coeffs.len() + other.coeffs.len() - 1;
46        let mut result = Vec::with_capacity(result_len);
47        for _ in 0..result_len {
48            result.push(T::zero());
49        }
50
51        for (i, a) in self.coeffs.iter().enumerate() {
52            if a.is_zero() {
53                continue;
54            }
55            for (j, b) in other.coeffs.iter().enumerate() {
56                let prod = a.wrapping_mul(b);
57                result[i + j] = result[i + j].wrapping_add(&prod);
58            }
59        }
60
61        Self::from_coeffs(result)
62    }
63
64    /// Scalar multiplication
65    #[inline]
66    pub fn scale(&self, c: &T) -> Self {
67        if c.is_zero() {
68            return Self::zero();
69        }
70        if c.is_one() {
71            return self.clone();
72        }
73
74        let coeffs: Vec<T> = self.coeffs.iter().map(|x| x.wrapping_mul(c)).collect();
75        Self::from_coeffs(coeffs)
76    }
77
78    /// Negation
79    #[inline]
80    pub fn negate(&self) -> Self {
81        let coeffs: Vec<T> = self.coeffs.iter().map(|x| -x.clone()).collect();
82        Self { coeffs }
83    }
84
85    /// Derivative
86    #[inline]
87    pub fn derivative(&self) -> Self {
88        if self.coeffs.len() <= 1 {
89            return Self::zero();
90        }
91
92        let coeffs: Vec<T> = self.coeffs[1..]
93            .iter()
94            .enumerate()
95            .map(|(i, c)| {
96                let multiplier = T::zero().wrapping_add(&T::one());
97                let mut result = multiplier.clone();
98                for _ in 0..i {
99                    result = result.wrapping_add(&multiplier);
100                }
101                c.wrapping_mul(&result)
102            })
103            .collect();
104
105        Self::from_coeffs(coeffs)
106    }
107
108    /// Evaluate polynomial at a point using Horner's method
109    ///
110    /// Uses Horner's method: O(n) multiplications instead of O(n²)
111    #[inline]
112    pub fn evaluate(&self, x: &T) -> T {
113        if self.coeffs.is_empty() {
114            return T::zero();
115        }
116
117        let mut result = T::zero();
118        for coeff in self.coeffs.iter().rev() {
119            result = result.wrapping_mul(x).wrapping_add(coeff);
120        }
121        result
122    }
123}
124
125impl<T: Ring> Add for Poly<T> {
126    type Output = Self;
127
128    #[inline]
129    fn add(self, other: Self) -> Self {
130        Poly::add(&self, &other)
131    }
132}
133
134impl<T: Ring> Add for &Poly<T> {
135    type Output = Poly<T>;
136
137    #[inline]
138    fn add(self, other: Self) -> Poly<T> {
139        Poly::add(self, other)
140    }
141}
142
143impl<T: Ring> Sub for Poly<T> {
144    type Output = Self;
145
146    #[inline]
147    fn sub(self, other: Self) -> Self {
148        Poly::sub(&self, &other)
149    }
150}
151
152impl<T: Ring> Sub for &Poly<T> {
153    type Output = Poly<T>;
154
155    #[inline]
156    fn sub(self, other: Self) -> Poly<T> {
157        Poly::sub(self, other)
158    }
159}
160
161impl<T: Ring> Mul for Poly<T> {
162    type Output = Self;
163
164    #[inline]
165    fn mul(self, other: Self) -> Self {
166        Poly::mul(&self, &other)
167    }
168}
169
170impl<T: Ring> Mul for &Poly<T> {
171    type Output = Poly<T>;
172
173    #[inline]
174    fn mul(self, other: Self) -> Poly<T> {
175        Poly::mul(self, other)
176    }
177}
178
179impl<T: Ring> Neg for Poly<T> {
180    type Output = Self;
181
182    #[inline]
183    fn neg(self) -> Self {
184        self.negate()
185    }
186}
187
188impl<T: Ring> Neg for &Poly<T> {
189    type Output = Poly<T>;
190
191    #[inline]
192    fn neg(self) -> Poly<T> {
193        self.negate()
194    }
195}
196
197impl super::IntPoly {
198    /// Evaluate at integer point (IntPoly-specific optimized version)
199    #[inline]
200    pub fn evaluate_i64(&self, x: i64) -> i64 {
201        if self.coeffs.is_empty() {
202            return 0;
203        }
204
205        let mut result = 0i64;
206        for &coeff in self.coeffs.iter().rev() {
207            result = result.wrapping_mul(x).wrapping_add(coeff);
208        }
209        result
210    }
211
212    /// Scalar multiplication for i64 (IntPoly-specific optimized version)
213    #[inline]
214    pub fn scale_i64(&self, c: i64) -> Self {
215        if c == 0 {
216            return Self::zero();
217        }
218        if c == 1 {
219            return self.clone();
220        }
221
222        let coeffs: Vec<i64> = self.coeffs.iter().map(|&x| x.wrapping_mul(c)).collect();
223        Self::from_coeffs(coeffs)
224    }
225
226    /// Derivative for IntPoly (optimized version)
227    #[inline]
228    pub fn derivative_i64(&self) -> Self {
229        if self.coeffs.len() <= 1 {
230            return Self::zero();
231        }
232
233        let coeffs: Vec<i64> = self.coeffs[1..]
234            .iter()
235            .enumerate()
236            .map(|(i, &c)| c.wrapping_mul((i + 1) as i64))
237            .collect();
238
239        Self::from_coeffs(coeffs)
240    }
241}
242
243#[cfg(test)]
244mod tests {
245    use crate::core::polynomial::poly::IntPoly;
246
247    #[test]
248    fn test_addition() {
249        let p1 = IntPoly::from_coeffs(vec![1, 2, 3]);
250        let p2 = IntPoly::from_coeffs(vec![4, 5]);
251        let sum = &p1 + &p2;
252        assert_eq!(sum.coefficients(), &[5, 7, 3]);
253    }
254
255    #[test]
256    fn test_subtraction() {
257        let p1 = IntPoly::from_coeffs(vec![5, 7, 3]);
258        let p2 = IntPoly::from_coeffs(vec![4, 5]);
259        let diff = &p1 - &p2;
260        assert_eq!(diff.coefficients(), &[1, 2, 3]);
261    }
262
263    #[test]
264    fn test_multiplication() {
265        let p = IntPoly::from_coeffs(vec![1, 1]);
266        let product = &p * &p;
267        assert_eq!(product.coefficients(), &[1, 2, 1]);
268
269        let p1 = IntPoly::from_coeffs(vec![1, 1]);
270        let p2 = IntPoly::from_coeffs(vec![1, -1]);
271        let product = &p1 * &p2;
272        assert_eq!(product.coefficients(), &[1, 0, -1]);
273    }
274
275    #[test]
276    fn test_evaluation() {
277        let p = IntPoly::from_coeffs(vec![1, 2, 3]);
278        assert_eq!(p.evaluate_i64(2), 17);
279        assert_eq!(p.evaluate_i64(0), 1);
280        assert_eq!(p.evaluate_i64(1), 6);
281    }
282
283    #[test]
284    fn test_derivative() {
285        let p = IntPoly::from_coeffs(vec![1, 2, 3, 4]);
286        let dp = p.derivative_i64();
287        assert_eq!(dp.coefficients(), &[2, 6, 12]);
288    }
289
290    #[test]
291    fn test_negation() {
292        let p = IntPoly::from_coeffs(vec![1, -2, 3]);
293        let neg = -&p;
294        assert_eq!(neg.coefficients(), &[-1, 2, -3]);
295    }
296
297    #[test]
298    fn test_scalar_multiplication() {
299        let p = IntPoly::from_coeffs(vec![1, 2, 3]);
300        let scaled = p.scale_i64(3);
301        assert_eq!(scaled.coefficients(), &[3, 6, 9]);
302    }
303}