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