1#![forbid(unsafe_code)]
2#[derive(Debug, Clone, Copy, PartialEq)]
20pub struct StateStep {
21 pub step_index: usize,
22 pub previous_state: f64,
23 pub next_state: f64,
24 pub delta: f64,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum StateStepError {
29 NonFiniteState,
30 NonFiniteDelta,
31}
32
33pub fn step_state(
34 previous_state: f64,
35 delta: f64,
36 step_index: usize,
37) -> Result<StateStep, StateStepError> {
38 if !previous_state.is_finite() {
39 return Err(StateStepError::NonFiniteState);
40 }
41
42 if !delta.is_finite() {
43 return Err(StateStepError::NonFiniteDelta);
44 }
45
46 let next_state = previous_state + delta;
47 if !next_state.is_finite() {
48 return Err(StateStepError::NonFiniteState);
49 }
50
51 Ok(StateStep {
52 step_index,
53 previous_state,
54 next_state,
55 delta,
56 })
57}
58
59pub fn apply_steps(initial_state: f64, deltas: &[f64]) -> Result<Vec<StateStep>, StateStepError> {
60 if !initial_state.is_finite() {
61 return Err(StateStepError::NonFiniteState);
62 }
63
64 let mut current_state = initial_state;
65 let mut steps = Vec::with_capacity(deltas.len());
66
67 for (step_index, delta) in deltas.iter().copied().enumerate() {
68 let step = step_state(current_state, delta, step_index)?;
69 current_state = step.next_state;
70 steps.push(step);
71 }
72
73 Ok(steps)
74}
75
76pub fn states(initial_state: f64, deltas: &[f64]) -> Result<Vec<f64>, StateStepError> {
77 let steps = apply_steps(initial_state, deltas)?;
78 let mut values = Vec::with_capacity(steps.len() + 1);
79 values.push(initial_state);
80 values.extend(steps.iter().map(|step| step.next_state));
81 Ok(values)
82}
83
84#[cfg(test)]
85mod tests {
86 use super::{StateStepError, apply_steps, states, step_state};
87
88 #[test]
89 fn steps_state_forward() {
90 let step = step_state(1.0, 0.5, 0).unwrap();
91
92 assert_eq!(step.previous_state, 1.0);
93 assert_eq!(step.next_state, 1.5);
94 assert_eq!(step.delta, 0.5);
95 }
96
97 #[test]
98 fn applies_multiple_steps() {
99 let steps = apply_steps(1.0, &[0.5, -0.25, 1.0]).unwrap();
100
101 assert_eq!(steps.len(), 3);
102 assert_eq!(steps[2].next_state, 2.25);
103 assert_eq!(
104 states(1.0, &[0.5, -0.25, 1.0]).unwrap(),
105 vec![1.0, 1.5, 1.25, 2.25]
106 );
107 }
108
109 #[test]
110 fn allows_empty_step_lists() {
111 assert_eq!(apply_steps(3.0, &[]).unwrap(), Vec::new());
112 assert_eq!(states(3.0, &[]).unwrap(), vec![3.0]);
113 }
114
115 #[test]
116 fn rejects_invalid_inputs() {
117 assert_eq!(
118 step_state(f64::NAN, 1.0, 0),
119 Err(StateStepError::NonFiniteState)
120 );
121 assert_eq!(
122 step_state(1.0, f64::NAN, 0),
123 Err(StateStepError::NonFiniteDelta)
124 );
125 assert_eq!(
126 apply_steps(f64::INFINITY, &[1.0]),
127 Err(StateStepError::NonFiniteState)
128 );
129 }
130}