1use crate::{Cubic, InputError, TerminationCondition, different_signs};
2
3#[derive(Clone, Debug)]
10pub struct Poly {
11 coeffs: Vec<f64>,
15}
16
17impl<'a> std::ops::Mul<&'a Poly> for &'a Poly {
18 type Output = Poly;
19
20 fn mul(self, rhs: &Poly) -> Poly {
21 let mut coeffs = vec![0.0; (self.coeffs.len() + rhs.coeffs.len()).saturating_sub(1)];
22
23 for (i, c) in self.coeffs.iter().enumerate() {
24 for (j, d) in rhs.coeffs.iter().enumerate() {
25 coeffs[i + j] += c * d;
26 }
27 }
28 Poly { coeffs }
29 }
30}
31
32impl std::ops::Mul<&Poly> for Poly {
33 type Output = Poly;
34
35 fn mul(self, rhs: &Poly) -> Poly {
36 (&self) * rhs
37 }
38}
39
40impl Poly {
41 pub fn new(coeffs: impl IntoIterator<Item = f64>) -> Self {
46 Poly {
47 coeffs: coeffs.into_iter().collect(),
48 }
49 }
50
51 fn is_finite(&self) -> bool {
52 self.coeffs.iter().all(|c| c.is_finite())
53 }
54
55 pub fn deriv(&self) -> Poly {
57 let mut coeffs = Vec::with_capacity(self.coeffs.len() - 1);
58 for (i, c) in self.coeffs.iter().enumerate().skip(1) {
62 coeffs.push(c * (i as f64));
63 }
64 Poly { coeffs }
65 }
66
67 pub fn eval(&self, x: f64) -> f64 {
69 let mut ret = 0.0;
70 let mut x_pow = 1.0;
71 for &c in &self.coeffs {
72 ret += c * x_pow;
73 x_pow *= x;
74 }
75 ret
76 }
77
78 pub fn degree(&self) -> usize {
88 self.coeffs.len().saturating_sub(1)
89 }
90
91 pub fn to_cubic(&self) -> Option<Cubic> {
93 if self.degree() <= 3 {
94 Some(Cubic {
95 c0: self.coeffs.first().copied().unwrap_or(0.0),
96 c1: self.coeffs.get(1).copied().unwrap_or(0.0),
97 c2: self.coeffs.get(2).copied().unwrap_or(0.0),
98 c3: self.coeffs.get(3).copied().unwrap_or(0.0),
99 })
100 } else {
101 None
102 }
103 }
104
105 fn one_root<Term: TerminationCondition>(
106 &self,
107 deriv: &Poly,
108 mut lower: f64,
109 mut upper: f64,
110 val_lower: f64,
111 val_upper: f64,
112 term: Term,
113 ) -> f64 {
114 if !val_lower.is_finite() || !val_upper.is_finite() || !deriv.is_finite() {
115 return f64::NAN;
116 }
117 debug_assert!(different_signs(val_lower, val_upper));
118
119 let mut x = lower + (upper - lower) / 2.0;
120 let mut val_x = self.eval(x);
121 let mut step = (upper - lower) / 2.0;
122
123 while x.is_finite() && !term.stop(step, val_x) {
124 let root_in_first_half = different_signs(val_lower, val_x);
125 if root_in_first_half {
126 upper = x;
127 } else {
128 lower = x;
129 }
130
131 let deriv_x = self.deriv().eval(x);
132 debug_assert!(deriv_x.is_finite());
133 debug_assert!(val_x.is_finite());
134
135 step = -val_x / deriv_x;
136 let mut new_x = x + step;
137
138 if new_x <= lower || new_x >= upper {
139 new_x = lower + (upper - lower) / 2.0;
140
141 if new_x == upper || new_x == lower {
142 return new_x;
150 }
151 }
152 step = new_x - x;
153 x = new_x;
154 val_x = self.eval(x);
155 }
156 x
157 }
158
159 pub fn roots_in(&self, lower: f64, upper: f64, x_error: f64) -> Vec<f64> {
174 let mut ret = Vec::new();
175
176 if let Some(c) = self.to_cubic() {
177 ret.extend(c.all_roots(lower, upper, x_error));
178 return ret;
179 }
180
181 let deriv = self.deriv();
182 let mut possible_endpoints = deriv.roots_in(lower, upper, x_error);
183 possible_endpoints.push(upper);
184 let mut last = lower;
185 let mut last_val = self.eval(last);
186 for x in possible_endpoints {
187 if x > last && x <= upper {
188 let val = self.eval(x);
189 if different_signs(last_val, val) {
190 ret.push(self.one_root(&deriv, last, x, last_val, val, InputError(x_error)));
191 }
192
193 last = x;
194 last_val = val;
195 }
196 }
197 ret
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 #[test]
206 fn smoke() {
207 let x_minus_1 = Poly::new([-1.0, 1.0]);
208 let x_minus_2 = Poly::new([-2.0, 1.0]);
209 let x_minus_3 = Poly::new([-3.0, 1.0]);
210 let x_minus_4 = Poly::new([-4.0, 1.0]);
211
212 let p = &x_minus_1 * &x_minus_2 * &x_minus_3 * &x_minus_4;
213
214 let roots = p.roots_in(0.0, 5.0, 1e-6);
215 assert_eq!(roots.len(), 4);
216 assert!((roots[0] - 1.0).abs() <= 1e-6);
217 assert!((roots[1] - 2.0).abs() <= 1e-6);
218 assert!((roots[2] - 3.0).abs() <= 1e-6);
219 assert!((roots[3] - 4.0).abs() <= 1e-6);
220 }
221}