statrs/
generate.rs

1//! Provides utility functions for generating data sequences
2
3use crate::euclid::Modulus;
4use std::f64::consts;
5/// Generates a base 10 log spaced vector of the given length between the
6/// specified decade exponents (inclusive). Equivalent to MATLAB logspace
7///
8/// # Examples
9///
10/// ```
11/// use statrs::generate;
12///
13/// let x = generate::log_spaced(5, 0.0, 4.0);
14/// assert_eq!(x, [1.0, 10.0, 100.0, 1000.0, 10000.0]);
15/// ```
16pub fn log_spaced(length: usize, start_exp: f64, stop_exp: f64) -> Vec<f64> {
17    match length {
18        0 => Vec::new(),
19        1 => vec![10f64.powf(stop_exp)],
20        _ => {
21            let step = (stop_exp - start_exp) / (length - 1) as f64;
22            let mut vec = (0..length)
23                .map(|x| 10f64.powf(start_exp + (x as f64) * step))
24                .collect::<Vec<f64>>();
25            vec[length - 1] = 10f64.powf(stop_exp);
26            vec
27        }
28    }
29}
30
31/// Infinite iterator returning floats that form a periodic wave
32pub struct InfinitePeriodic {
33    amplitude: f64,
34    step: f64,
35    phase: f64,
36    k: f64,
37}
38
39impl InfinitePeriodic {
40    /// Constructs a new infinite periodic wave generator
41    ///
42    /// # Examples
43    ///
44    /// ```
45    /// use statrs::generate::InfinitePeriodic;
46    ///
47    /// let x = InfinitePeriodic::new(8.0, 2.0, 10.0, 1.0,
48    /// 2).take(10).collect::<Vec<f64>>();
49    /// assert_eq!(x, [6.0, 8.5, 1.0, 3.5, 6.0, 8.5, 1.0, 3.5, 6.0, 8.5]);
50    /// ```
51    pub fn new(
52        sampling_rate: f64,
53        frequency: f64,
54        amplitude: f64,
55        phase: f64,
56        delay: i64,
57    ) -> InfinitePeriodic {
58        let step = frequency / sampling_rate * amplitude;
59        InfinitePeriodic {
60            amplitude,
61            step,
62            phase: (phase - delay as f64 * step).modulus(amplitude),
63            k: 0.0,
64        }
65    }
66
67    /// Constructs a default infinite periodic wave generator
68    ///
69    /// # Examples
70    ///
71    /// ```
72    /// use statrs::generate::InfinitePeriodic;
73    ///
74    /// let x = InfinitePeriodic::default(8.0,
75    /// 2.0).take(10).collect::<Vec<f64>>();
76    /// assert_eq!(x, [0.0, 0.25, 0.5, 0.75, 0.0, 0.25, 0.5, 0.75, 0.0, 0.25]);
77    /// ```
78    pub fn default(sampling_rate: f64, frequency: f64) -> InfinitePeriodic {
79        Self::new(sampling_rate, frequency, 1.0, 0.0, 0)
80    }
81}
82
83impl Iterator for InfinitePeriodic {
84    type Item = f64;
85
86    fn next(&mut self) -> Option<f64> {
87        let mut x = self.phase + self.k * self.step;
88        if x >= self.amplitude {
89            x %= self.amplitude;
90            self.phase = x;
91            self.k = 0.0;
92        }
93        self.k += 1.0;
94        Some(x)
95    }
96}
97
98/// Infinite iterator returning floats that form a sinusoidal wave
99pub struct InfiniteSinusoidal {
100    amplitude: f64,
101    mean: f64,
102    step: f64,
103    phase: f64,
104    i: usize,
105}
106
107impl InfiniteSinusoidal {
108    /// Constructs a new infinite sinusoidal wave generator
109    ///
110    /// # Examples
111    ///
112    /// ```
113    /// use statrs::generate::InfiniteSinusoidal;
114    ///
115    /// let x = InfiniteSinusoidal::new(8.0, 2.0, 1.0, 5.0, 2.0,
116    /// 1).take(10).collect::<Vec<f64>>();
117    /// assert_eq!(x,
118    ///     [5.416146836547142, 5.909297426825682, 4.583853163452858,
119    ///     4.090702573174318, 5.416146836547142, 5.909297426825682,
120    ///     4.583853163452858, 4.090702573174318, 5.416146836547142,
121    ///     5.909297426825682]);
122    /// ```
123    pub fn new(
124        sampling_rate: f64,
125        frequency: f64,
126        amplitude: f64,
127        mean: f64,
128        phase: f64,
129        delay: i64,
130    ) -> InfiniteSinusoidal {
131        let pi2 = consts::PI * 2.0;
132        let step = frequency / sampling_rate * pi2;
133        InfiniteSinusoidal {
134            amplitude,
135            mean,
136            step,
137            phase: (phase - delay as f64 * step) % pi2,
138            i: 0,
139        }
140    }
141
142    /// Constructs a default infinite sinusoidal wave generator
143    ///
144    /// # Examples
145    ///
146    /// ```
147    /// use statrs::generate::InfiniteSinusoidal;
148    ///
149    /// let x = InfiniteSinusoidal::default(8.0, 2.0,
150    /// 1.0).take(10).collect::<Vec<f64>>();
151    /// assert_eq!(x,
152    ///     [0.0, 1.0, 0.00000000000000012246467991473532,
153    ///     -1.0, -0.00000000000000024492935982947064, 1.0,
154    ///     0.00000000000000036739403974420594, -1.0,
155    ///     -0.0000000000000004898587196589413, 1.0]);
156    /// ```
157    pub fn default(sampling_rate: f64, frequency: f64, amplitude: f64) -> InfiniteSinusoidal {
158        Self::new(sampling_rate, frequency, amplitude, 0.0, 0.0, 0)
159    }
160}
161
162impl Iterator for InfiniteSinusoidal {
163    type Item = f64;
164
165    fn next(&mut self) -> Option<f64> {
166        let x = self.mean + self.amplitude * (self.phase + self.i as f64 * self.step).sin();
167        self.i += 1;
168        if self.i == 1000 {
169            self.i = 0;
170            self.phase = (self.phase + 1000.0 * self.step) % (consts::PI * 2.0);
171        }
172        Some(x)
173    }
174}
175
176/// Infinite iterator returning floats forming a square wave starting
177/// with the high phase
178pub struct InfiniteSquare {
179    periodic: InfinitePeriodic,
180    high_duration: f64,
181    high_value: f64,
182    low_value: f64,
183}
184
185impl InfiniteSquare {
186    /// Constructs a new infinite square wave generator
187    ///
188    /// # Examples
189    ///
190    /// ```
191    /// use statrs::generate::InfiniteSquare;
192    ///
193    /// let x = InfiniteSquare::new(3, 7, 1.0, -1.0,
194    /// 1).take(12).collect::<Vec<f64>>();
195    /// assert_eq!(x, [-1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0,
196    /// -1.0, 1.0])
197    /// ```
198    pub fn new(
199        high_duration: i64,
200        low_duration: i64,
201        high_value: f64,
202        low_value: f64,
203        delay: i64,
204    ) -> InfiniteSquare {
205        let duration = (high_duration + low_duration) as f64;
206        InfiniteSquare {
207            periodic: InfinitePeriodic::new(1.0, 1.0 / duration, duration, 0.0, delay),
208            high_duration: high_duration as f64,
209            high_value,
210            low_value,
211        }
212    }
213}
214
215impl Iterator for InfiniteSquare {
216    type Item = f64;
217
218    fn next(&mut self) -> Option<f64> {
219        self.periodic.next().map(|x| {
220            if x < self.high_duration {
221                self.high_value
222            } else {
223                self.low_value
224            }
225        })
226    }
227}
228
229/// Infinite iterator returning floats forming a triangle wave starting with
230/// the raise phase from the lowest sample
231pub struct InfiniteTriangle {
232    periodic: InfinitePeriodic,
233    raise_duration: f64,
234    raise: f64,
235    fall: f64,
236    high_value: f64,
237    low_value: f64,
238}
239
240impl InfiniteTriangle {
241    /// Constructs a new infinite triangle wave generator
242    ///
243    /// # Examples
244    ///
245    /// ```
246    /// #[macro_use]
247    /// extern crate statrs;
248    ///
249    /// use statrs::generate::InfiniteTriangle;
250    ///
251    /// # fn main() {
252    /// let x = InfiniteTriangle::new(4, 7, 1.0, -1.0,
253    /// 1).take(12).collect::<Vec<f64>>();
254    /// let expected: [f64; 12] = [-0.714, -1.0, -0.5, 0.0, 0.5, 1.0, 0.714,
255    /// 0.429, 0.143, -0.143, -0.429, -0.714];
256    /// for (&left, &right) in x.iter().zip(expected.iter()) {
257    ///     assert_almost_eq!(left, right, 1e-3);
258    /// }
259    /// # }
260    /// ```
261    pub fn new(
262        raise_duration: i64,
263        fall_duration: i64,
264        high_value: f64,
265        low_value: f64,
266        delay: i64,
267    ) -> InfiniteTriangle {
268        let duration = (raise_duration + fall_duration) as f64;
269        let height = high_value - low_value;
270        InfiniteTriangle {
271            periodic: InfinitePeriodic::new(1.0, 1.0 / duration, duration, 0.0, delay),
272            raise_duration: raise_duration as f64,
273            raise: height / raise_duration as f64,
274            fall: height / fall_duration as f64,
275            high_value,
276            low_value,
277        }
278    }
279}
280
281impl Iterator for InfiniteTriangle {
282    type Item = f64;
283
284    fn next(&mut self) -> Option<f64> {
285        self.periodic.next().map(|x| {
286            if x < self.raise_duration {
287                self.low_value + x * self.raise
288            } else {
289                self.high_value - (x - self.raise_duration) * self.fall
290            }
291        })
292    }
293}
294
295/// Infinite iterator returning floats forming a sawtooth wave
296/// starting with the lowest sample
297pub struct InfiniteSawtooth {
298    periodic: InfinitePeriodic,
299    low_value: f64,
300}
301
302impl InfiniteSawtooth {
303    /// Constructs a new infinite sawtooth wave generator
304    ///
305    /// # Examples
306    ///
307    /// ```
308    /// use statrs::generate::InfiniteSawtooth;
309    ///
310    /// let x = InfiniteSawtooth::new(5, 1.0, -1.0,
311    /// 1).take(12).collect::<Vec<f64>>();
312    /// assert_eq!(x, [1.0, -1.0, -0.5, 0.0, 0.5, 1.0, -1.0, -0.5, 0.0, 0.5,
313    /// 1.0, -1.0]);
314    /// ```
315    pub fn new(period: i64, high_value: f64, low_value: f64, delay: i64) -> InfiniteSawtooth {
316        let height = high_value - low_value;
317        let period = period as f64;
318        InfiniteSawtooth {
319            periodic: InfinitePeriodic::new(
320                1.0,
321                1.0 / period,
322                height * period / (period - 1.0),
323                0.0,
324                delay,
325            ),
326            low_value: low_value as f64,
327        }
328    }
329}
330
331impl Iterator for InfiniteSawtooth {
332    type Item = f64;
333
334    fn next(&mut self) -> Option<f64> {
335        self.periodic.next().map(|x| x + self.low_value)
336    }
337}