rsl_interpolation/
accel.rs

1/// Index Look-up Acceration
2///
3/// This object caches the previous value of an index lookup. When the subsequent interpolation
4/// point falls in the same interval, its index value can be returned immediately.
5///
6/// The performance boost can be significant when continuously evaluating splines around the same
7/// area as the previous point. Moreover, the same Accelerator can be shared across multiple
8/// Splines, if they are defined over the same x points. This is especially useful in ODE solvers,
9/// as the solver's step size is usually much smaller that the xarray spacing.
10///
11/// See [`GSL's Acceleration section`].
12///
13/// ## Example
14///
15/// ```
16/// # use rsl_interpolation::*;
17/// #
18/// # fn main() {
19/// let mut acc = Accelerator::new();
20/// # }
21/// ```
22/// [`GSL's Acceleration section`]: https://www.gnu.org/software/gsl/doc/html/interp.html#d-index-look-up-and-acceleration
23#[doc(alias = "gsl_interp_accel")]
24pub struct Accelerator {
25    /// The current cached index.
26    pub(crate) cache: usize,
27    /// The total cache hits.
28    pub(crate) hits: usize,
29    /// The total cache misses.
30    pub(crate) misses: usize,
31}
32
33impl Accelerator {
34    /// Creates a new Accelerator.
35    pub fn new() -> Self {
36        Accelerator {
37            cache: 0,
38            hits: 0,
39            misses: 0,
40        }
41    }
42
43    /// This function returns the index i of the array `xarray` such that
44    /// `xarray[i] <= x <= xarray[i+1]`. The index is searched for in the range [ilo, ihi].
45    #[doc(alias = "gsl_interp_bsearch")]
46    pub(crate) fn bsearch<T>(&self, xarray: &[T], x: T, ilo: usize, ihi: usize) -> usize
47    where
48        T: num::Float,
49    {
50        let mut ilo = ilo;
51        let mut ihi = ihi;
52        while ihi > ilo + 1 {
53            let i = (ihi + ilo) / 2;
54            if xarray[i] > x {
55                ihi = i;
56            } else {
57                ilo = i;
58            }
59        }
60        ilo
61    }
62
63    /// Performs a lookup action on the data array. Returns an index i such that
64    /// xarray[i] <= x < xarray[i+1].
65    #[doc(alias = "gsl_interp_accel_find")]
66    pub(crate) fn find<T>(&mut self, xarray: &[T], x: T) -> usize
67    where
68        T: num::Float,
69    {
70        if x < xarray[self.cache] {
71            self.misses += 1;
72            self.cache = self.bsearch(xarray, x, 0, self.cache);
73        } else if x >= xarray[self.cache + 1] {
74            self.misses += 1;
75            self.cache = self.bsearch(xarray, x, self.cache, xarray.len() - 1);
76        } else {
77            self.hits += 1;
78        }
79        self.cache
80    }
81
82    /// Resets the Accelerator's stats to 0.
83    #[doc(alias = "gsl_interp_accel_reset")]
84    pub fn reset(&mut self) {
85        self.cache = 0;
86        self.hits = 0;
87        self.misses = 0;
88    }
89}
90
91impl std::fmt::Debug for Accelerator {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        f.debug_struct("Accelerator")
94            .field("cache", &self.cache)
95            .field("hits  ", &self.hits)
96            .field("misses", &self.misses)
97            .finish()
98    }
99}
100
101impl Default for Accelerator {
102    fn default() -> Self {
103        Self::new()
104    }
105}
106
107#[cfg(test)]
108mod test {
109    use super::*;
110
111    #[test]
112    fn test_instantiation() {
113        let _ = Accelerator::new();
114    }
115
116    #[test]
117    fn test_reset() {
118        let mut acc = Accelerator::new();
119        acc.cache = 1;
120        acc.hits = 1;
121        acc.misses = 1;
122        acc.reset();
123
124        assert_eq!(acc.cache, 0);
125        assert_eq!(acc.hits, 0);
126        assert_eq!(acc.misses, 0);
127    }
128}