rgsl/types/
interpolation.rs

1//
2// A rust binding for the GSL library by Guillaume Gomez (guillaume1.gomez@gmail.com)
3//
4
5/*!
6# Interpolation
7
8This chapter describes functions for performing interpolation. The library provides a variety of interpolation methods, including Cubic splines
9and Akima splines. The interpolation types are interchangeable, allowing different methods to be used without recompiling. Interpolations can
10be defined for both normal and periodic boundary conditions. Additional functions are available for computing derivatives and integrals of
11interpolating functions.
12
13These interpolation methods produce curves that pass through each datapoint. To interpolate noisy data with a smoothing curve see Basis Splines.
14
15## Introduction
16
17Given a set of data points (x_1, y_1) \dots (x_n, y_n) the routines described in this section compute a continuous interpolating function
18y(x) such that y(x_i) = y_i. The interpolation is piecewise smooth, and its behavior at the end-points is determined by the type of
19interpolation used.
20
21## Index Look-up and Acceleration
22
23The state of searches can be stored in a gsl_interp_accel object, which is a kind of iterator for interpolation lookups. It caches the previous
24value of an index lookup. When the subsequent interpolation point falls in the same interval its index value can be returned immediately.
25
26## Higher-level Interface
27
28The functions described in the previous sections required the user to supply pointers to the x and y arrays on each call. The following
29functions are equivalent to the corresponding gsl_interp functions but maintain a copy of this data in the gsl_spline object. This removes
30the need to pass both xa and ya as arguments on each evaluation.
31
32## References and Further Reading
33
34Descriptions of the interpolation algorithms and further references can be found in the following books:
35
36C.W. Ueberhuber, Numerical Computation (Volume 1), Chapter 9 “Interpolation”, Springer (1997), ISBN 3-540-62058-3.
37D.M. Young, R.T. Gregory A Survey of Numerical Mathematics (Volume 1), Chapter 6.8, Dover (1988), ISBN 0-486-65691-8.
38!*/
39
40use crate::Value;
41use ffi::FFI;
42
43/// Evaluation accelerator.
44#[derive(Clone)]
45pub struct InterpAccel(pub sys::gsl_interp_accel);
46
47impl InterpAccel {
48    /// This function returns a pointer to an accelerator object, which is a kind of iterator for
49    /// interpolation lookups. It tracks the state of lookups, thus allowing for application of
50    /// various acceleration strategies.
51    #[allow(clippy::new_without_default)]
52    pub fn new() -> InterpAccel {
53        InterpAccel(sys::gsl_interp_accel {
54            cache: 0,
55            miss_count: 0,
56            hit_count: 0,
57        })
58    }
59
60    /// This function reinitializes the accelerator object acc. It should be used when the cached
61    /// information is no longer applicable-for example, when switching to a new dataset.
62    pub fn reset(&mut self) {
63        self.0.cache = 0;
64        self.0.miss_count = 0;
65        self.0.hit_count = 0;
66    }
67
68    /// This function performs a lookup action on the data array x_array of size size, using the
69    /// given accelerator a. This is how lookups are performed during evaluation of an
70    /// interpolation. The function returns an index i such that `x_array[i] <= x < x_array[i+1]`.
71    #[doc(alias = "gsl_interp_accel_find")]
72    pub fn find(&mut self, x_array: &[f64], x: f64) -> usize {
73        unsafe { sys::gsl_interp_accel_find(&mut self.0, x_array.as_ptr(), x_array.len() as _, x) }
74    }
75}
76
77ffi_wrapper!(Interp, *mut sys::gsl_interp, gsl_interp_free);
78
79impl Interp {
80    /// This function returns a pointer to a newly allocated interpolation object of type T for
81    /// size data-points.
82    ///
83    /// ```
84    /// use rgsl::{Interp, InterpType};
85    ///
86    /// let interp_type = InterpType::linear();
87    /// let interp = Interp::new(interp_type, 2).expect("Failed to initialize `Interp`...");
88    /// ```
89    #[doc(alias = "gsl_interp_alloc")]
90    pub fn new(t: InterpType, size: usize) -> Option<Interp> {
91        let tmp = unsafe { sys::gsl_interp_alloc(t.unwrap_shared(), size) };
92
93        if tmp.is_null() {
94            None
95        } else {
96            Some(Self::wrap(tmp))
97        }
98    }
99
100    /// This function initializes the interpolation object interp for the data (xa,ya) where xa and
101    /// ya are arrays of size size. The interpolation object (gsl_interp) does not save the data
102    /// arrays xa and ya and only stores the static state computed from the data. The xa data array
103    /// is always assumed to be strictly ordered, with increasing x values; the behavior for other
104    /// arrangements is not defined.
105    ///
106    /// Asserts that `ya.len() >= xa.len()`.
107    #[doc(alias = "gsl_interp_init")]
108    pub fn init(&mut self, xa: &[f64], ya: &[f64]) -> Result<(), Value> {
109        assert!(ya.len() >= xa.len());
110        let ret = unsafe {
111            sys::gsl_interp_init(
112                self.unwrap_unique(),
113                xa.as_ptr(),
114                ya.as_ptr(),
115                xa.len() as _,
116            )
117        };
118        result_handler!(ret, ())
119    }
120
121    /// This function returns the name of the interpolation type used by interp. For example,
122    ///
123    /// ```
124    /// use rgsl::{Interp, InterpType};
125    ///
126    /// let interp_type = InterpType::linear();
127    /// let interp = Interp::new(interp_type, 2).expect("Failed to initialize `Interp`...");
128    /// println!("interp uses '{}' interpolation.", interp.name());
129    /// ```
130    ///
131    /// would print something like :
132    ///
133    /// ```Shell
134    /// interp uses 'cspline' interpolation.
135    /// ```
136    #[doc(alias = "gsl_interp_name")]
137    pub fn name(&self) -> String {
138        let tmp = unsafe { sys::gsl_interp_name(self.unwrap_shared()) };
139
140        if tmp.is_null() {
141            String::new()
142        } else {
143            unsafe {
144                String::from_utf8_lossy(::std::ffi::CStr::from_ptr(tmp).to_bytes()).to_string()
145            }
146        }
147    }
148
149    /// This function returns the minimum number of points required by the interpolation object
150    /// interp or interpolation type T. For example, Akima spline interpolation requires a minimum
151    /// of 5 points.
152    #[doc(alias = "gsl_interp_min_size")]
153    pub fn min_size(&self) -> u32 {
154        unsafe { sys::gsl_interp_min_size(self.unwrap_shared()) }
155    }
156}
157
158ffi_wrapper!(InterpType, *const sys::gsl_interp_type);
159
160impl InterpType {
161    /// This function returns the minimum number of points required by the interpolation object
162    /// interp or interpolation type T. For example, Akima spline interpolation requires a minimum
163    /// of 5 points.
164    #[doc(alias = "gsl_interp_type_min_size")]
165    pub fn min_size(&self) -> u32 {
166        unsafe { sys::gsl_interp_type_min_size(self.unwrap_shared()) }
167    }
168
169    /// Linear interpolation. This interpolation method does not require any additional memory.
170    #[doc(alias = "gsl_interp_linear")]
171    pub fn linear() -> InterpType {
172        ffi_wrap!(gsl_interp_linear)
173    }
174
175    /// Polynomial interpolation. This method should only be used for interpolating small numbers
176    /// of points because polynomial interpolation introduces large oscillations, even for
177    /// well-behaved datasets. The number of terms in the interpolating polynomial is equal to the
178    /// number of points.
179    #[doc(alias = "gsl_interp_polynomial")]
180    pub fn polynomial() -> InterpType {
181        ffi_wrap!(gsl_interp_polynomial)
182    }
183
184    /// Cubic spline with natural boundary conditions. The resulting curve is piecewise cubic on
185    /// each interval, with matching first and second derivatives at the supplied data-points. The
186    /// second derivative is chosen to be zero at the first point and last point.
187    #[doc(alias = "gsl_interp_cspline")]
188    pub fn cspline() -> InterpType {
189        ffi_wrap!(gsl_interp_cspline)
190    }
191
192    /// Cubic spline with periodic boundary conditions. The resulting curve is piecewise cubic on
193    /// each interval, with matching first and second derivatives at the supplied data-points. The
194    /// derivatives at the first and last points are also matched. Note that the last point in the
195    /// data must have the same y-value as the first point, otherwise the resulting periodic
196    /// interpolation will have a discontinuity at the boundary.
197    #[doc(alias = "gsl_interp_cspline_periodic")]
198    pub fn cspline_periodic() -> InterpType {
199        ffi_wrap!(gsl_interp_cspline_periodic)
200    }
201
202    /// Non-rounded Akima spline with natural boundary conditions. This method uses the non-rounded
203    /// corner algorithm of Wodicka.
204    #[doc(alias = "gsl_interp_akima")]
205    pub fn akima() -> InterpType {
206        ffi_wrap!(gsl_interp_akima)
207    }
208
209    /// Non-rounded Akima spline with periodic boundary conditions. This method uses the non-rounded
210    /// corner algorithm of Wodicka.
211    #[doc(alias = "gsl_interp_akima_periodic")]
212    pub fn akima_periodic() -> InterpType {
213        ffi_wrap!(gsl_interp_akima_periodic)
214    }
215}
216
217ffi_wrapper!(
218    Spline,
219    *mut sys::gsl_spline,
220    gsl_spline_free,
221    "General interpolation object."
222);
223
224impl Spline {
225    #[doc(alias = "gsl_spline_alloc")]
226    pub fn new(t: InterpType, size: usize) -> Option<Spline> {
227        let tmp = unsafe { sys::gsl_spline_alloc(t.unwrap_shared(), size) };
228
229        if tmp.is_null() {
230            None
231        } else {
232            Some(Self::wrap(tmp))
233        }
234    }
235
236    #[doc(alias = "gsl_spline_init")]
237    pub fn init(&mut self, xa: &[f64], ya: &[f64]) -> Result<(), Value> {
238        let ret = unsafe {
239            sys::gsl_spline_init(
240                self.unwrap_unique(),
241                xa.as_ptr(),
242                ya.as_ptr(),
243                xa.len() as _,
244            )
245        };
246        result_handler!(ret, ())
247    }
248
249    #[doc(alias = "gsl_spline_name")]
250    pub fn name(&self) -> String {
251        let tmp = unsafe { sys::gsl_spline_name(self.unwrap_shared()) };
252
253        if tmp.is_null() {
254            String::new()
255        } else {
256            unsafe {
257                String::from_utf8_lossy(::std::ffi::CStr::from_ptr(tmp).to_bytes()).to_string()
258            }
259        }
260    }
261
262    #[doc(alias = "gsl_spline_min_size")]
263    pub fn min_size(&self) -> u32 {
264        unsafe { sys::gsl_spline_min_size(self.unwrap_shared()) }
265    }
266
267    #[doc(alias = "gsl_spline_eval")]
268    pub fn eval(&self, x: f64, acc: &mut InterpAccel) -> f64 {
269        unsafe { sys::gsl_spline_eval(self.unwrap_shared(), x, &mut acc.0) }
270    }
271
272    /// Returns `y`.
273    #[doc(alias = "gsl_spline_eval_e")]
274    pub fn eval_e(&self, x: f64, acc: &mut InterpAccel) -> Result<f64, Value> {
275        let mut y = 0.;
276        let ret = unsafe { sys::gsl_spline_eval_e(self.unwrap_shared(), x, &mut acc.0, &mut y) };
277        result_handler!(ret, y)
278    }
279
280    #[doc(alias = "gsl_spline_eval_deriv")]
281    pub fn eval_deriv(&self, x: f64, acc: &mut InterpAccel) -> f64 {
282        unsafe { sys::gsl_spline_eval_deriv(self.unwrap_shared(), x, &mut acc.0) }
283    }
284
285    /// Returns `d`.
286    #[doc(alias = "gsl_spline_eval_deriv_e")]
287    pub fn eval_deriv_e(&self, x: f64, acc: &mut InterpAccel) -> Result<f64, Value> {
288        let mut d = 0.;
289        let ret =
290            unsafe { sys::gsl_spline_eval_deriv_e(self.unwrap_shared(), x, &mut acc.0, &mut d) };
291        result_handler!(ret, d)
292    }
293
294    #[doc(alias = "gsl_spline_eval_deriv2")]
295    pub fn eval_deriv2(&self, x: f64, acc: &mut InterpAccel) -> f64 {
296        unsafe { sys::gsl_spline_eval_deriv2(self.unwrap_shared(), x, &mut acc.0) }
297    }
298
299    /// Returns `d2`.
300    #[doc(alias = "gsl_spline_eval_deriv2_e")]
301    pub fn eval_deriv2_e(&self, x: f64, acc: &mut InterpAccel) -> Result<f64, Value> {
302        let mut d2 = 0.;
303        let ret =
304            unsafe { sys::gsl_spline_eval_deriv2_e(self.unwrap_shared(), x, &mut acc.0, &mut d2) };
305        result_handler!(ret, d2)
306    }
307
308    #[doc(alias = "gsl_spline_eval_integ")]
309    pub fn eval_integ(&self, a: f64, b: f64, acc: &mut InterpAccel) -> f64 {
310        unsafe { sys::gsl_spline_eval_integ(self.unwrap_shared(), a, b, &mut acc.0) }
311    }
312
313    /// Returns `d2`.
314    #[doc(alias = "gsl_spline_eval_integ_e")]
315    pub fn eval_integ_e(&self, a: f64, b: f64, acc: &mut InterpAccel) -> Result<f64, Value> {
316        let mut result = 0.;
317        let ret = unsafe {
318            sys::gsl_spline_eval_integ_e(self.unwrap_shared(), a, b, &mut acc.0, &mut result)
319        };
320        result_handler!(ret, result)
321    }
322}