1use crate::{SimTrace, TraceEvent};
10
11pub struct ReplayRunner<S, F>
36where
37 F: Fn(u64, u64) -> (S, SimTrace),
38{
39 seed: u64,
40 sim_fn: F,
41 _phantom: std::marker::PhantomData<S>,
42}
43
44impl<S, F> ReplayRunner<S, F>
45where
46 F: Fn(u64, u64) -> (S, SimTrace),
47{
48 pub fn new(seed: u64, sim_fn: F) -> Self {
53 Self {
54 seed,
55 sim_fn,
56 _phantom: std::marker::PhantomData,
57 }
58 }
59
60 pub fn replay_to_tick(&self, target_tick: u64) -> (S, SimTrace) {
64 (self.sim_fn)(self.seed, target_tick)
65 }
66
67 pub fn replay_window(&self, start_tick: u64, end_tick: u64) -> (S, Vec<TraceEvent>) {
69 let (state, trace) = (self.sim_fn)(self.seed, end_tick);
70 let window_events: Vec<TraceEvent> = trace
71 .events()
72 .iter()
73 .filter(|e| e.tick >= start_tick && e.tick <= end_tick)
74 .cloned()
75 .collect();
76 (state, window_events)
77 }
78
79 pub fn seed(&self) -> u64 {
81 self.seed
82 }
83}
84
85pub fn replay_until<S, F, P>(
94 seed: u64,
95 max_ticks: u64,
96 step: u64,
97 sim_fn: F,
98 predicate: P,
99) -> Option<u64>
100where
101 F: Fn(u64, u64) -> (S, SimTrace),
102 P: Fn(&S, &SimTrace) -> bool,
103{
104 let mut tick = step;
105 while tick <= max_ticks {
106 let (state, trace) = sim_fn(seed, tick);
107 if predicate(&state, &trace) {
108 if tick <= step {
110 return Some(tick);
111 }
112 let mut lo = tick - step;
113 let mut hi = tick;
114 while lo + 1 < hi {
115 let mid = lo + (hi - lo) / 2;
116 let (s, t) = sim_fn(seed, mid);
117 if predicate(&s, &t) {
118 hi = mid;
119 } else {
120 lo = mid;
121 }
122 }
123 return Some(hi);
124 }
125 tick += step;
126 }
127 None
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133 use crate::TraceEventKind;
134
135 fn counter_sim(_seed: u64, ticks: u64) -> (u64, SimTrace) {
136 let mut trace = SimTrace::new();
137 let mut counter = 0u64;
138 for t in 1..=ticks {
139 counter += 1;
140 if t % 10 == 0 {
141 trace.record(
142 t,
143 1,
144 TraceEventKind::Custom {
145 tag: "counter".into(),
146 data: format!("{counter}"),
147 },
148 );
149 }
150 }
151 (counter, trace)
152 }
153
154 #[test]
155 fn test_replay_to_tick() {
156 let runner = ReplayRunner::new(42, counter_sim);
157 let (state, _trace) = runner.replay_to_tick(100);
158 assert_eq!(state, 100);
159 }
160
161 #[test]
162 fn test_replay_deterministic() {
163 let runner = ReplayRunner::new(42, counter_sim);
164 let (s1, t1) = runner.replay_to_tick(500);
165 let (s2, t2) = runner.replay_to_tick(500);
166 assert_eq!(s1, s2);
167 assert_eq!(t1.len(), t2.len());
168 }
169
170 #[test]
171 fn test_replay_window() {
172 let runner = ReplayRunner::new(42, counter_sim);
173 let (state, events) = runner.replay_window(50, 100);
174 assert_eq!(state, 100);
175 assert_eq!(events.len(), 6);
177 assert!(events.iter().all(|e| e.tick >= 50 && e.tick <= 100));
178 }
179
180 #[test]
181 fn test_replay_until_found() {
182 let tick = replay_until(42, 1000, 10, counter_sim, |state, _trace| *state >= 75);
183 assert!(tick.is_some());
184 assert_eq!(tick.unwrap(), 75);
185 }
186
187 #[test]
188 fn test_replay_until_not_found() {
189 let tick = replay_until(42, 50, 10, counter_sim, |state, _trace| *state >= 100);
190 assert!(tick.is_none());
191 }
192
193 #[test]
194 fn test_replay_seed() {
195 let runner = ReplayRunner::new(123, counter_sim);
196 assert_eq!(runner.seed(), 123);
197 }
198}