optionstratlib_math/geometrics/interpolation/
traits.rs1use crate::error::InterpolationError;
2use crate::geometrics::{
3 BiLinearInterpolation, CubicInterpolation, GeometricObject, InterpolationType,
4 LinearInterpolation, SplineInterpolation,
5};
6use rust_decimal::Decimal;
7
8pub trait Interpolate<Point, Input>:
59 LinearInterpolation<Point, Input>
60 + BiLinearInterpolation<Point, Input>
61 + CubicInterpolation<Point, Input>
62 + SplineInterpolation<Point, Input>
63 + GeometricObject<Point, Input>
64where
65 Input: HasX,
66 Point: HasX + Clone,
67{
68 fn interpolate(
90 &self,
91 x: Input,
92 interpolation_type: InterpolationType,
93 ) -> Result<Point, InterpolationError> {
94 match interpolation_type {
95 InterpolationType::Linear => self.linear_interpolate(x),
96 InterpolationType::Cubic => self.cubic_interpolate(x),
97 InterpolationType::Spline => self.spline_interpolate(x),
98 InterpolationType::Bilinear => self.bilinear_interpolate(x),
99 }
100 }
101
102 fn find_bracket_points(&self, x: Input) -> Result<(usize, usize), InterpolationError> {
131 let points: Vec<&Point> = self.get_points().into_iter().collect();
132 if points.len() < 2 {
134 return Err(InterpolationError::EmptyData);
135 }
136 let (Some(first), Some(last)) = (points.first(), points.last()) else {
137 return Err(InterpolationError::EmptyData);
138 };
139 if x.get_x() < first.get_x() || x.get_x() > last.get_x() {
140 return Err(InterpolationError::OutOfRange {
141 target: x.get_x().to_string(),
142 });
143 }
144 for (i, pair) in points.windows(2).enumerate() {
146 let (Some(left), Some(right)) = (pair.first(), pair.get(1)) else {
147 return Err(InterpolationError::DegenerateInterval);
148 };
149 if left.get_x() <= x.get_x() && x.get_x() <= right.get_x() {
150 return i
152 .checked_add(1)
153 .map(|j| (i, j))
154 .ok_or(InterpolationError::DegenerateInterval);
155 }
156 }
157 Err(InterpolationError::DegenerateInterval)
158 }
159}
160
161pub trait HasX {
198 fn get_x(&self) -> Decimal;
203}
204
205impl HasX for Decimal {
208 fn get_x(&self) -> Decimal {
209 *self
210 }
211}
212
213#[cfg(test)]
214mod tests_interpolate {
215 use super::*;
216 use crate::curves::Point2D;
217 use crate::error::InterpolationError;
218 use rust_decimal_macros::dec;
219 use std::collections::BTreeSet;
220
221 struct MockInterpolator {
222 points: BTreeSet<Point2D>,
223 }
224
225 impl LinearInterpolation<Point2D, Decimal> for MockInterpolator {
226 fn linear_interpolate(&self, x: Decimal) -> Result<Point2D, InterpolationError> {
227 if self.points.len() < 2 {
229 return Err(InterpolationError::Linear(
230 "Need at least two points for interpolation".to_string(),
231 ));
232 }
233 Ok(Point2D::new(x, x))
234 }
235 }
236
237 impl BiLinearInterpolation<Point2D, Decimal> for MockInterpolator {
238 fn bilinear_interpolate(&self, x: Decimal) -> Result<Point2D, InterpolationError> {
239 if self.points.len() < 4 {
240 return Err(InterpolationError::Bilinear(
241 "Need at least four points for bilinear interpolation".to_string(),
242 ));
243 }
244 Ok(Point2D::new(x, x))
245 }
246 }
247
248 impl CubicInterpolation<Point2D, Decimal> for MockInterpolator {
249 fn cubic_interpolate(&self, x: Decimal) -> Result<Point2D, InterpolationError> {
250 if self.points.len() < 4 {
251 return Err(InterpolationError::Cubic(
252 "Need at least four points for cubic interpolation".to_string(),
253 ));
254 }
255 Ok(Point2D::new(x, x))
256 }
257 }
258
259 impl SplineInterpolation<Point2D, Decimal> for MockInterpolator {
260 fn spline_interpolate(&self, x: Decimal) -> Result<Point2D, InterpolationError> {
261 if self.points.len() < 3 {
262 return Err(InterpolationError::Spline(
263 "Need at least three points for spline interpolation".to_string(),
264 ));
265 }
266 Ok(Point2D::new(x, x))
267 }
268 }
269
270 impl GeometricObject<Point2D, Decimal> for MockInterpolator {
271 type Error = ();
272
273 fn get_points(&self) -> BTreeSet<&Point2D> {
274 self.points.iter().collect()
275 }
276
277 fn from_vector<T>(_points: Vec<T>) -> Self
278 where
279 Self: Sized,
280 T: Into<Point2D> + Clone,
281 {
282 unreachable!()
283 }
284
285 fn construct<T>(_method: T) -> Result<Self, Self::Error>
286 where
287 Self: Sized,
288 {
289 unreachable!()
290 }
291 }
292
293 impl Interpolate<Point2D, Decimal> for MockInterpolator {}
294
295 fn create_mock_interpolator(points: BTreeSet<Point2D>) -> MockInterpolator {
296 MockInterpolator { points }
297 }
298
299 #[test]
300 fn test_interpolate_empty_points() {
301 let interpolator = create_mock_interpolator(BTreeSet::new());
302
303 let linear = interpolator.interpolate(dec!(0.5), InterpolationType::Linear);
304 assert!(linear.is_err());
305
306 let bilinear = interpolator.interpolate(dec!(0.5), InterpolationType::Bilinear);
307 assert!(bilinear.is_err());
308
309 let cubic = interpolator.interpolate(dec!(0.5), InterpolationType::Cubic);
310 assert!(cubic.is_err());
311
312 let spline = interpolator.interpolate(dec!(0.5), InterpolationType::Spline);
313 assert!(spline.is_err());
314 }
315
316 #[test]
317 fn test_interpolate_insufficient_points() {
318 let interpolator =
320 create_mock_interpolator(BTreeSet::from_iter(vec![Point2D::new(dec!(0), dec!(0))]));
321
322 let linear = interpolator.interpolate(dec!(0.5), InterpolationType::Linear);
323 assert!(linear.is_err());
324
325 let interpolator = create_mock_interpolator(BTreeSet::from_iter(vec![
327 Point2D::new(dec!(0), dec!(0)),
328 Point2D::new(dec!(1), dec!(1)),
329 ]));
330
331 assert!(
332 interpolator
333 .interpolate(dec!(0.5), InterpolationType::Bilinear)
334 .is_err()
335 );
336 assert!(
337 interpolator
338 .interpolate(dec!(0.5), InterpolationType::Cubic)
339 .is_err()
340 );
341 assert!(
342 interpolator
343 .interpolate(dec!(0.5), InterpolationType::Spline)
344 .is_err()
345 );
346 }
347
348 #[test]
349 fn test_get_points() {
350 let points = BTreeSet::from_iter(vec![
351 Point2D::new(dec!(0), dec!(0)),
352 Point2D::new(dec!(1), dec!(1)),
353 ]);
354 let interpolator = create_mock_interpolator(points.clone());
355
356 assert_eq!(
357 interpolator.vector(),
358 points.iter().collect::<Vec<_>>().as_slice()
359 );
360 }
361
362 #[test]
363 fn test_interpolate_routing() {
364 let points = BTreeSet::from_iter(vec![
365 Point2D::new(dec!(0), dec!(0)), Point2D::new(dec!(1), dec!(1)), Point2D::new(dec!(2), dec!(2)), Point2D::new(dec!(3), dec!(3)), ]);
370 let interpolator = create_mock_interpolator(points);
371 let x = dec!(0.5);
372
373 let linear = interpolator
375 .interpolate(x, InterpolationType::Linear)
376 .unwrap();
377 let bilinear = interpolator
378 .interpolate(x, InterpolationType::Bilinear)
379 .unwrap();
380 let cubic = interpolator
381 .interpolate(x, InterpolationType::Cubic)
382 .unwrap();
383 let spline = interpolator
384 .interpolate(x, InterpolationType::Spline)
385 .unwrap();
386
387 assert_eq!(linear.x, x);
389 assert_eq!(bilinear.x, x);
390 assert_eq!(cubic.x, x);
391 assert_eq!(spline.x, x);
392 }
393
394 #[test]
395 fn test_find_bracket_points_success() {
396 let points = BTreeSet::from_iter(vec![
397 Point2D::new(dec!(0), dec!(0)),
398 Point2D::new(dec!(1), dec!(1)),
399 Point2D::new(dec!(2), dec!(2)),
400 ]);
401 let interpolator = create_mock_interpolator(points);
402
403 let (i, j) = interpolator.find_bracket_points(dec!(0.5)).unwrap();
405 assert_eq!(i, 0);
406 assert_eq!(j, 1);
407
408 let (i, j) = interpolator.find_bracket_points(dec!(1.0)).unwrap();
410 assert_eq!(i, 0);
411 assert_eq!(j, 1);
412 }
413
414 #[test]
415 fn test_find_bracket_points_insufficient_points() {
416 let points = BTreeSet::from_iter(vec![Point2D::new(dec!(0), dec!(0))]);
417 let interpolator = create_mock_interpolator(points);
418
419 assert!(interpolator.find_bracket_points(dec!(0.5)).is_err());
420 }
421
422 #[test]
423 fn test_find_bracket_points_out_of_range() {
424 let points = BTreeSet::from_iter(vec![
425 Point2D::new(dec!(0), dec!(0)),
426 Point2D::new(dec!(1), dec!(1)),
427 ]);
428 let interpolator = create_mock_interpolator(points);
429
430 assert!(interpolator.find_bracket_points(dec!(-0.5)).is_err());
432
433 assert!(interpolator.find_bracket_points(dec!(1.5)).is_err());
435 }
436
437 #[test]
438 fn test_find_bracket_points_edge_cases() {
439 let points = BTreeSet::from_iter(vec![
440 Point2D::new(dec!(0), dec!(0)),
441 Point2D::new(dec!(1), dec!(1)),
442 Point2D::new(dec!(2), dec!(2)),
443 ]);
444 let interpolator = create_mock_interpolator(points);
445
446 let (i, j) = interpolator.find_bracket_points(dec!(0)).unwrap();
448 assert_eq!(i, 0);
449 assert_eq!(j, 1);
450
451 let (i, j) = interpolator.find_bracket_points(dec!(2)).unwrap();
453 assert_eq!(i, 1);
454 assert_eq!(j, 2);
455 }
456
457 #[test]
458 fn test_interpolate_with_empty_points() {
459 let interpolator = create_mock_interpolator(BTreeSet::from_iter(vec![]));
460 let result = interpolator.interpolate(dec!(0.5), InterpolationType::Linear);
461 assert!(result.is_err());
462 }
463
464 #[test]
465 fn test_interpolate_routing_minimum_points() {
466 let linear_points = BTreeSet::from_iter(vec![
470 Point2D::new(dec!(0), dec!(0)),
471 Point2D::new(dec!(1), dec!(1)),
472 ]);
473 let interpolator = create_mock_interpolator(linear_points);
474 let linear = interpolator.interpolate(dec!(0.5), InterpolationType::Linear);
475 assert!(linear.is_ok());
476
477 let spline_points = BTreeSet::from_iter(vec![
479 Point2D::new(dec!(0), dec!(0)),
480 Point2D::new(dec!(1), dec!(1)),
481 Point2D::new(dec!(2), dec!(2)),
482 ]);
483 let interpolator = create_mock_interpolator(spline_points);
484 let spline = interpolator.interpolate(dec!(0.5), InterpolationType::Spline);
485 assert!(spline.is_ok());
486
487 let four_points = BTreeSet::from_iter(vec![
489 Point2D::new(dec!(0), dec!(0)),
490 Point2D::new(dec!(1), dec!(1)),
491 Point2D::new(dec!(2), dec!(2)),
492 Point2D::new(dec!(3), dec!(3)),
493 ]);
494 let interpolator = create_mock_interpolator(four_points);
495 let bilinear = interpolator.interpolate(dec!(0.5), InterpolationType::Bilinear);
496 let cubic = interpolator.interpolate(dec!(0.5), InterpolationType::Cubic);
497 assert!(bilinear.is_ok());
498 assert!(cubic.is_ok());
499 }
500}