rant_simulator/simulate/period/
mod.rs

1#[cfg(test)]
2mod test;
3
4pub struct SimulationOptions {
5    pub iterations: usize,
6    pub max_period: usize,
7    pub delta: f64,
8}
9
10#[derive(Debug, PartialEq)]
11pub enum Cycle<S> {
12    FixedPoint(S),
13    Cycle(Vec<S>),
14    Divergence,
15}
16
17impl<S> Cycle<S>
18where
19    S: Copy,
20{
21    fn from_history(history: &[S], last_encounter: usize) -> Self {
22        let cycle_length = history.len() - last_encounter - 1;
23
24        match cycle_length {
25            0 => Cycle::FixedPoint(history[last_encounter]),
26            _ => Cycle::Cycle(history.iter().skip(last_encounter).copied().collect()),
27        }
28    }
29}
30
31pub fn simulate<State, Parameters>(
32    initial_state: State,
33    parameters: &Parameters,
34    function: impl Fn(State, &Parameters) -> State,
35    distance: impl Fn(&State, &State) -> f64,
36    options: SimulationOptions,
37) -> Cycle<State>
38where
39    State: Default + Copy,
40{
41    let history_length = options.max_period;
42    let mut history = vec![State::default(); history_length];
43    let mut x = initial_state;
44
45    for _ in 0..options.iterations - history_length {
46        x = function(x, parameters);
47    }
48
49    for item in history.iter_mut() {
50        // TODO: earlier checks for cycles
51        *item = x;
52        x = function(x, parameters);
53    }
54
55    let last_encounter = history
56        .iter()
57        .rev()
58        .take(options.max_period)
59        .position(|h| distance(h, &x) < options.delta)
60        .map(|pos| history_length - pos - 1);
61
62    match last_encounter {
63        Some(last_encounter) => Cycle::from_history(&history, last_encounter),
64        None => Cycle::Divergence, // TODO: rotate history and return it also
65    }
66}