Skip to main content

sciforge_lib/maths/polynomial/
poly.rs

1use std::fmt;
2use std::ops::{Add, Div, Mul, Neg, Sub};
3
4#[derive(Clone, Debug)]
5pub struct Polynomial {
6    pub coeffs: Vec<f64>,
7}
8
9impl Polynomial {
10    pub fn new(coeffs: Vec<f64>) -> Self {
11        let mut p = Self { coeffs };
12        p.trim();
13        p
14    }
15
16    pub fn zero() -> Self {
17        Self { coeffs: vec![0.0] }
18    }
19    pub fn one() -> Self {
20        Self { coeffs: vec![1.0] }
21    }
22    pub fn monomial(degree: usize, coeff: f64) -> Self {
23        let mut c = vec![0.0; degree + 1];
24        c[degree] = coeff;
25        Self { coeffs: c }
26    }
27
28    fn trim(&mut self) {
29        while self.coeffs.len() > 1 && self.coeffs.last().is_some_and(|&c| c.abs() < 1e-30) {
30            self.coeffs.pop();
31        }
32    }
33
34    pub fn degree(&self) -> usize {
35        self.coeffs.len().saturating_sub(1)
36    }
37
38    pub fn eval(&self, x: f64) -> f64 {
39        let mut result = 0.0;
40        for c in self.coeffs.iter().rev() {
41            result = result * x + c;
42        }
43        result
44    }
45
46    pub fn derivative(&self) -> Self {
47        if self.coeffs.len() <= 1 {
48            return Self::zero();
49        }
50        Self::new(
51            self.coeffs
52                .iter()
53                .enumerate()
54                .skip(1)
55                .map(|(i, &c)| c * i as f64)
56                .collect(),
57        )
58    }
59
60    pub fn integral(&self, constant: f64) -> Self {
61        let mut coeffs = vec![constant];
62        for (i, &c) in self.coeffs.iter().enumerate() {
63            coeffs.push(c / (i + 1) as f64);
64        }
65        Self::new(coeffs)
66    }
67
68    pub fn definite_integral(&self, a: f64, b: f64) -> f64 {
69        let anti = self.integral(0.0);
70        anti.eval(b) - anti.eval(a)
71    }
72
73    pub fn scale(&self, s: f64) -> Self {
74        Self::new(self.coeffs.iter().map(|c| c * s).collect())
75    }
76
77    pub fn compose(&self, other: &Self) -> Self {
78        let mut result = Self::zero();
79        let mut power = Self::one();
80        for &c in &self.coeffs {
81            result = result + power.scale(c);
82            power = power.clone() * other.clone();
83        }
84        result
85    }
86
87    pub fn div_rem(&self, divisor: &Self) -> (Self, Self) {
88        if divisor.coeffs.iter().all(|c| c.abs() < 1e-30) {
89            panic!("division by zero polynomial");
90        }
91        let mut remainder = self.coeffs.clone();
92        let d_deg = divisor.degree();
93        let lead = *divisor.coeffs.last().unwrap();
94        let mut quotient = vec![];
95
96        while remainder.len() > d_deg && !remainder.is_empty() {
97            let coeff = *remainder.last().unwrap() / lead;
98            quotient.push(coeff);
99            for (i, &dc) in divisor.coeffs.iter().enumerate() {
100                let idx = remainder.len() - d_deg - 1 + i;
101                remainder[idx] -= coeff * dc;
102            }
103            remainder.pop();
104        }
105        quotient.reverse();
106        if quotient.is_empty() {
107            quotient.push(0.0);
108        }
109        (Self::new(quotient), Self::new(remainder))
110    }
111
112    pub fn gcd(&self, other: &Self) -> Self {
113        let mut a = self.clone();
114        let mut b = other.clone();
115        while b.coeffs.iter().any(|c| c.abs() > 1e-30) {
116            let (_, r) = a.div_rem(&b);
117            a = b;
118            b = r;
119        }
120        let lead = *a.coeffs.last().unwrap_or(&1.0);
121        if lead.abs() > 1e-30 {
122            a.scale(1.0 / lead)
123        } else {
124            a
125        }
126    }
127
128    pub fn from_roots(roots: &[f64]) -> Self {
129        let mut result = Self::one();
130        for &r in roots {
131            result = result * Self::new(vec![-r, 1.0]);
132        }
133        result
134    }
135}
136
137impl Add for Polynomial {
138    type Output = Self;
139    fn add(self, rhs: Self) -> Self {
140        let n = self.coeffs.len().max(rhs.coeffs.len());
141        let mut coeffs = vec![0.0; n];
142        for (i, &c) in self.coeffs.iter().enumerate() {
143            coeffs[i] += c;
144        }
145        for (i, &c) in rhs.coeffs.iter().enumerate() {
146            coeffs[i] += c;
147        }
148        Self::new(coeffs)
149    }
150}
151
152impl Sub for Polynomial {
153    type Output = Self;
154    fn sub(self, rhs: Self) -> Self {
155        let n = self.coeffs.len().max(rhs.coeffs.len());
156        let mut coeffs = vec![0.0; n];
157        for (i, &c) in self.coeffs.iter().enumerate() {
158            coeffs[i] += c;
159        }
160        for (i, &c) in rhs.coeffs.iter().enumerate() {
161            coeffs[i] -= c;
162        }
163        Self::new(coeffs)
164    }
165}
166
167impl Mul for Polynomial {
168    type Output = Self;
169    fn mul(self, rhs: Self) -> Self {
170        let mut coeffs = vec![0.0; self.coeffs.len() + rhs.coeffs.len() - 1];
171        for (i, &a) in self.coeffs.iter().enumerate() {
172            for (j, &b) in rhs.coeffs.iter().enumerate() {
173                coeffs[i + j] += a * b;
174            }
175        }
176        Self::new(coeffs)
177    }
178}
179
180impl Div for Polynomial {
181    type Output = Self;
182    fn div(self, rhs: Self) -> Self {
183        let (q, _) = self.div_rem(&rhs);
184        q
185    }
186}
187
188impl Neg for Polynomial {
189    type Output = Self;
190    fn neg(self) -> Self {
191        self.scale(-1.0)
192    }
193}
194
195impl fmt::Display for Polynomial {
196    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197        let mut terms = vec![];
198        for (i, &c) in self.coeffs.iter().enumerate().rev() {
199            if c.abs() < 1e-30 {
200                continue;
201            }
202            match i {
203                0 => terms.push(format!("{c}")),
204                1 => terms.push(format!("{c}x")),
205                _ => terms.push(format!("{c}x^{i}")),
206            }
207        }
208        if terms.is_empty() {
209            write!(f, "0")
210        } else {
211            write!(f, "{}", terms.join(" + "))
212        }
213    }
214}