extern crate alloc;
use crate::RngType;
use alloc::collections::VecDeque;
use alloc::vec::Vec;
#[cfg(not(feature = "std"))]
use num_traits::Float;
use rand::{Rng, SeedableRng};
#[derive(Clone, Debug)]
pub struct MackeyGlassParams {
pub a: f64,
pub b: f64,
pub n: u32,
pub tau: usize,
pub x0: f64,
pub h: f64,
pub steps: usize,
pub seed: Option<u64>,
pub history: Option<Vec<f64>>,
}
pub struct MackeyGlass {
params: MackeyGlassParams,
history: VecDeque<f64>,
current_x: f64,
}
impl MackeyGlass {
pub fn new(params: MackeyGlassParams) -> Self {
let history_len = (params.tau as f64 / params.h).ceil() as usize;
let mut history = VecDeque::with_capacity(history_len);
if let Some(hist) = ¶ms.history {
history.extend(hist[hist.len() - history_len..].iter().cloned());
} else {
let mut rng = match params.seed {
Some(s) => RngType::seed_from_u64(s),
None => {
#[cfg(feature = "std")]
{
RngType::from_entropy()
}
#[cfg(not(feature = "std"))]
{
RngType::seed_from_u64(42)
}
}
};
for _ in 0..history_len {
history.push_back(params.x0 + rng.gen::<f64>() * 0.1);
}
}
Self {
current_x: params.x0,
params,
history,
}
}
pub fn step(&mut self) -> f64 {
let xt = self.current_x;
let xtau = self.history.front().copied().unwrap();
let f = |x: f64, x_tau: f64, p: &MackeyGlassParams| {
-p.b * x + p.a * x_tau / (1. + x_tau.powi(p.n as i32))
};
let h = self.params.h;
let k1 = h * f(xt, xtau, &self.params);
let k2 = h * f(xt + 0.5 * k1, xtau, &self.params);
let k3 = h * f(xt + 0.5 * k2, xtau, &self.params);
let k4 = h * f(xt + k3, xtau, &self.params);
let new_x = xt + (k1 + 2. * k2 + 2. * k3 + k4) / 6.;
self.history.pop_front();
self.history.push_back(new_x);
self.current_x = new_x;
new_x
}
pub fn generate(&mut self) -> Vec<f64> {
(0..self.params.steps).map(|_| self.step()).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "std")]
#[test]
fn test_mackey_glass_generate_size() {
let params = MackeyGlassParams {
a: 0.2,
b: 0.1,
n: 10,
tau: 17,
x0: 1.2,
h: 0.1,
steps: 100,
seed: Some(42),
history: None,
};
let mut mg = MackeyGlass::new(params);
let data = mg.generate();
assert_eq!(data.len(), 100);
}
#[cfg(feature = "std")]
#[test]
fn test_mackey_glass_history_size() {
let params = MackeyGlassParams {
a: 0.2,
b: 0.1,
n: 10,
tau: 17,
x0: 1.2,
h: 0.1,
steps: 100,
seed: Some(42),
history: None,
};
let mg = MackeyGlass::new(params);
assert_eq!(mg.history.len(), 170);
}
}