1use crate::polynomial::{Polynomial, Term, Var};
18#[allow(unused_imports)]
19use crate::prelude::*;
20use num_rational::BigRational;
21use num_traits::{One, Zero};
22
23#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct Point {
26 pub x: BigRational,
28 pub y: BigRational,
30}
31
32impl Point {
33 pub fn new(x: BigRational, y: BigRational) -> Self {
35 Self { x, y }
36 }
37
38 pub fn from_ints(x: i64, y: i64) -> Self {
40 Self {
41 x: BigRational::from_integer(x.into()),
42 y: BigRational::from_integer(y.into()),
43 }
44 }
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
49pub struct HermitePoint {
50 pub x: BigRational,
52 pub y: BigRational,
54 pub dy: BigRational,
56}
57
58impl HermitePoint {
59 pub fn new(x: BigRational, y: BigRational, dy: BigRational) -> Self {
61 Self { x, y, dy }
62 }
63}
64
65#[derive(Debug, Clone)]
67pub struct InterpolationConfig {
68 pub variable: Var,
70 pub check_stability: bool,
72 pub max_degree: usize,
74}
75
76impl Default for InterpolationConfig {
77 fn default() -> Self {
78 Self {
79 variable: 0,
80 check_stability: true,
81 max_degree: 100,
82 }
83 }
84}
85
86#[derive(Debug, Clone, Default)]
88pub struct InterpolationStats {
89 pub interpolations: u64,
91 pub avg_degree: f64,
93 pub instability_warnings: u64,
95}
96
97pub struct PolynomialInterpolator {
99 config: InterpolationConfig,
101 stats: InterpolationStats,
103}
104
105impl PolynomialInterpolator {
106 pub fn new(config: InterpolationConfig) -> Self {
108 Self {
109 config,
110 stats: InterpolationStats::default(),
111 }
112 }
113
114 pub fn default_config() -> Self {
116 Self::new(InterpolationConfig::default())
117 }
118
119 pub fn lagrange(&mut self, points: &[Point]) -> Result<Polynomial, InterpolationError> {
126 if points.is_empty() {
127 return Err(InterpolationError::NoPoints);
128 }
129
130 if points.len() > self.config.max_degree + 1 {
131 return Err(InterpolationError::DegreeTooHigh);
132 }
133
134 self.stats.interpolations += 1;
135
136 for i in 0..points.len() {
138 for j in (i + 1)..points.len() {
139 if points[i].x == points[j].x {
140 return Err(InterpolationError::DuplicateXValue);
141 }
142 }
143 }
144
145 let var = self.config.variable;
146 let mut result = Polynomial::zero();
147
148 for i in 0..points.len() {
150 let mut basis = Polynomial::one();
151
152 for j in 0..points.len() {
153 if i == j {
154 continue;
155 }
156
157 let numerator =
159 Polynomial::from_var(var) - Polynomial::constant(points[j].x.clone());
160 let denominator = &points[i].x - &points[j].x;
161
162 if denominator.is_zero() {
163 return Err(InterpolationError::DuplicateXValue);
164 }
165
166 basis = &basis * &numerator;
167 let inv = BigRational::from_integer(1.into()) / denominator;
169 basis = Self::multiply_by_constant(&basis, &inv);
170 }
171
172 basis = Self::multiply_by_constant(&basis, &points[i].y);
174 result = &result + &basis;
175 }
176
177 self.update_degree_stats(result.total_degree() as usize);
178
179 Ok(result)
180 }
181
182 pub fn newton(&mut self, points: &[Point]) -> Result<Polynomial, InterpolationError> {
188 if points.is_empty() {
189 return Err(InterpolationError::NoPoints);
190 }
191
192 if points.len() > self.config.max_degree + 1 {
193 return Err(InterpolationError::DegreeTooHigh);
194 }
195
196 self.stats.interpolations += 1;
197
198 let n = points.len();
200 let mut dd = vec![vec![BigRational::zero(); n]; n];
201
202 for i in 0..n {
204 dd[i][0] = points[i].y.clone();
205 }
206
207 for j in 1..n {
209 for i in 0..(n - j) {
210 let numerator = &dd[i + 1][j - 1] - &dd[i][j - 1];
211 let denominator = &points[i + j].x - &points[i].x;
212
213 if denominator.is_zero() {
214 return Err(InterpolationError::DuplicateXValue);
215 }
216
217 dd[i][j] = numerator / denominator;
218 }
219 }
220
221 let var = self.config.variable;
222 let mut result = Polynomial::constant(dd[0][0].clone());
223
224 let mut product = Polynomial::one();
226
227 for i in 1..n {
228 let factor = Polynomial::from_var(var) - Polynomial::constant(points[i - 1].x.clone());
230 product = &product * &factor;
231
232 let term = Self::multiply_by_constant(&product, &dd[0][i]);
234 result = &result + &term;
235 }
236
237 self.update_degree_stats(result.total_degree() as usize);
238
239 Ok(result)
240 }
241
242 pub fn hermite(&mut self, points: &[HermitePoint]) -> Result<Polynomial, InterpolationError> {
246 if points.is_empty() {
247 return Err(InterpolationError::NoPoints);
248 }
249
250 let n = points.len();
251 let total_conditions = 2 * n;
252
253 if total_conditions > self.config.max_degree + 1 {
254 return Err(InterpolationError::DegreeTooHigh);
255 }
256
257 self.stats.interpolations += 1;
258
259 let mut extended_points = Vec::new();
262
263 for point in points {
264 extended_points.push(Point::new(point.x.clone(), point.y.clone()));
266 extended_points.push(Point::new(point.x.clone(), point.y.clone()));
267 }
268
269 let m = extended_points.len();
271 let mut dd = vec![vec![BigRational::zero(); m]; m];
272
273 for i in 0..m {
275 dd[i][0] = extended_points[i].y.clone();
276 }
277
278 for i in 0..(m - 1) {
280 if extended_points[i].x == extended_points[i + 1].x {
281 let point_idx = i / 2;
283 dd[i][1] = points[point_idx].dy.clone();
284 } else {
285 let numerator = &dd[i + 1][0] - &dd[i][0];
286 let denominator = &extended_points[i + 1].x - &extended_points[i].x;
287
288 if !denominator.is_zero() {
289 dd[i][1] = numerator / denominator;
290 }
291 }
292 }
293
294 for j in 2..m {
296 for i in 0..(m - j) {
297 let denominator = &extended_points[i + j].x - &extended_points[i].x;
298
299 if !denominator.is_zero() {
300 let numerator = &dd[i + 1][j - 1] - &dd[i][j - 1];
301 dd[i][j] = numerator / denominator;
302 }
303 }
304 }
305
306 let var = self.config.variable;
308 let mut result = Polynomial::constant(dd[0][0].clone());
309 let mut product = Polynomial::one();
310
311 for i in 1..m {
312 let factor =
313 Polynomial::from_var(var) - Polynomial::constant(extended_points[i - 1].x.clone());
314 product = &product * &factor;
315
316 let term = Self::multiply_by_constant(&product, &dd[0][i]);
317 result = &result + &term;
318 }
319
320 self.update_degree_stats(result.total_degree() as usize);
321
322 Ok(result)
323 }
324
325 pub fn evaluate(&self, poly: &Polynomial, x: &BigRational) -> BigRational {
327 let mut result = BigRational::zero();
328
329 for term in poly.terms() {
330 let mut term_value = term.coeff.clone();
331
332 for var_power in term.monomial.vars() {
333 if var_power.var == self.config.variable {
334 let power_value = Self::power(x, var_power.power);
336 term_value *= power_value;
337 }
338 }
339
340 result += term_value;
341 }
342
343 result
344 }
345
346 fn power(x: &BigRational, n: u32) -> BigRational {
348 if n == 0 {
349 BigRational::one()
350 } else if n == 1 {
351 x.clone()
352 } else {
353 let mut result = x.clone();
354 for _ in 1..n {
355 result *= x;
356 }
357 result
358 }
359 }
360
361 fn update_degree_stats(&mut self, degree: usize) {
363 let count = self.stats.interpolations;
364 let old_avg = self.stats.avg_degree;
365 self.stats.avg_degree = (old_avg * (count - 1) as f64 + degree as f64) / count as f64;
366 }
367
368 pub fn stats(&self) -> &InterpolationStats {
370 &self.stats
371 }
372
373 pub fn reset_stats(&mut self) {
375 self.stats = InterpolationStats::default();
376 }
377
378 fn multiply_by_constant(poly: &Polynomial, scalar: &BigRational) -> Polynomial {
380 if scalar.is_zero() {
381 return Polynomial::zero();
382 }
383 if scalar.is_one() {
384 return poly.clone();
385 }
386
387 let new_terms: Vec<Term> = poly
389 .terms()
390 .iter()
391 .map(|term| Term {
392 coeff: &term.coeff * scalar,
393 monomial: term.monomial.clone(),
394 })
395 .collect();
396
397 Polynomial::from_terms(new_terms, poly.order)
398 }
399}
400
401#[derive(Debug, Clone, PartialEq, Eq)]
403pub enum InterpolationError {
404 NoPoints,
406 DuplicateXValue,
408 DegreeTooHigh,
410 NumericalInstability,
412}
413
414impl core::fmt::Display for InterpolationError {
415 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
416 match self {
417 InterpolationError::NoPoints => write!(f, "no interpolation points provided"),
418 InterpolationError::DuplicateXValue => write!(f, "duplicate x-values in points"),
419 InterpolationError::DegreeTooHigh => write!(f, "degree too high"),
420 InterpolationError::NumericalInstability => write!(f, "numerical instability"),
421 }
422 }
423}
424
425impl core::error::Error for InterpolationError {}
426
427#[cfg(test)]
428mod tests {
429 use super::*;
430
431 #[test]
432 fn test_point_creation() {
433 let p = Point::from_ints(1, 2);
434 assert_eq!(p.x, BigRational::from_integer(1.into()));
435 assert_eq!(p.y, BigRational::from_integer(2.into()));
436 }
437
438 #[test]
439 fn test_lagrange_linear() {
440 let mut interpolator = PolynomialInterpolator::default_config();
441
442 let points = vec![Point::from_ints(0, 1), Point::from_ints(1, 3)];
443
444 let poly = interpolator
445 .lagrange(&points)
446 .expect("interpolation failed");
447
448 let y0 = interpolator.evaluate(&poly, &BigRational::zero());
450 let y1 = interpolator.evaluate(&poly, &BigRational::one());
451
452 assert_eq!(y0, BigRational::from_integer(1.into()));
453 assert_eq!(y1, BigRational::from_integer(3.into()));
454 }
455
456 #[test]
457 fn test_lagrange_quadratic() {
458 let mut interpolator = PolynomialInterpolator::default_config();
459
460 let points = vec![
462 Point::from_ints(0, 0),
463 Point::from_ints(1, 1),
464 Point::from_ints(2, 4),
465 ];
466
467 let poly = interpolator
468 .lagrange(&points)
469 .expect("interpolation failed");
470
471 for point in &points {
473 let y = interpolator.evaluate(&poly, &point.x);
474 assert_eq!(y, point.y);
475 }
476 }
477
478 #[test]
479 fn test_newton_linear() {
480 let mut interpolator = PolynomialInterpolator::default_config();
481
482 let points = vec![Point::from_ints(0, 1), Point::from_ints(1, 3)];
483
484 let poly = interpolator.newton(&points).expect("interpolation failed");
485
486 let y0 = interpolator.evaluate(&poly, &BigRational::zero());
487 let y1 = interpolator.evaluate(&poly, &BigRational::one());
488
489 assert_eq!(y0, BigRational::from_integer(1.into()));
490 assert_eq!(y1, BigRational::from_integer(3.into()));
491 }
492
493 #[test]
494 fn test_duplicate_x_error() {
495 let mut interpolator = PolynomialInterpolator::default_config();
496
497 let points = vec![Point::from_ints(0, 1), Point::from_ints(0, 2)];
498
499 let result = interpolator.lagrange(&points);
500 assert!(matches!(result, Err(InterpolationError::DuplicateXValue)));
501 }
502
503 #[test]
504 fn test_stats() {
505 let mut interpolator = PolynomialInterpolator::default_config();
506 assert_eq!(interpolator.stats().interpolations, 0);
507
508 let points = vec![Point::from_ints(0, 0), Point::from_ints(1, 1)];
509 let _ = interpolator.lagrange(&points);
510
511 assert_eq!(interpolator.stats().interpolations, 1);
512 }
513}