1use serde::{Deserialize, Serialize};
9use std::cmp::Reverse;
10use std::collections::BinaryHeap;
11
12use crate::engine::state::SimEvent;
13use crate::engine::SimTime;
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct ScheduledEvent {
18 pub time: SimTime,
20 pub sequence: u64,
22 pub event: SimEvent,
24}
25
26impl ScheduledEvent {
27 #[must_use]
29 pub const fn new(time: SimTime, sequence: u64, event: SimEvent) -> Self {
30 Self {
31 time,
32 sequence,
33 event,
34 }
35 }
36}
37
38impl PartialEq for ScheduledEvent {
40 fn eq(&self, other: &Self) -> bool {
41 self.time == other.time && self.sequence == other.sequence
42 }
43}
44
45impl Eq for ScheduledEvent {}
46
47impl PartialOrd for ScheduledEvent {
48 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
49 Some(self.cmp(other))
50 }
51}
52
53impl Ord for ScheduledEvent {
54 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
55 match self.time.cmp(&other.time) {
57 std::cmp::Ordering::Equal => self.sequence.cmp(&other.sequence),
58 ord => ord,
59 }
60 }
61}
62
63#[derive(Debug, Default)]
88pub struct EventScheduler {
89 queue: BinaryHeap<Reverse<ScheduledEvent>>,
91 sequence: u64,
93}
94
95impl EventScheduler {
96 #[must_use]
98 pub fn new() -> Self {
99 Self::default()
100 }
101
102 pub fn schedule(&mut self, time: SimTime, event: SimEvent) {
104 let seq = self.sequence;
105 self.sequence += 1;
106
107 self.queue
108 .push(Reverse(ScheduledEvent::new(time, seq, event)));
109 }
110
111 pub fn schedule_all(&mut self, time: SimTime, events: &[SimEvent]) {
115 for event in events {
116 self.schedule(time, event.clone());
117 }
118 }
119
120 #[must_use]
122 #[allow(clippy::should_implement_trait)] pub fn next(&mut self) -> Option<ScheduledEvent> {
124 self.queue.pop().map(|Reverse(e)| e)
125 }
126
127 #[must_use]
129 pub fn peek(&self) -> Option<&ScheduledEvent> {
130 self.queue.peek().map(|Reverse(e)| e)
131 }
132
133 #[must_use]
135 pub fn next_before(&mut self, time: SimTime) -> Option<ScheduledEvent> {
136 if let Some(Reverse(e)) = self.queue.peek() {
137 if e.time <= time {
138 return self.next();
139 }
140 }
141 None
142 }
143
144 #[must_use]
146 pub fn drain_until(&mut self, time: SimTime) -> Vec<ScheduledEvent> {
147 let mut events = Vec::new();
148
149 while let Some(event) = self.next_before(time) {
150 events.push(event);
151 }
152
153 events
154 }
155
156 #[must_use]
158 pub fn is_empty(&self) -> bool {
159 self.queue.is_empty()
160 }
161
162 #[must_use]
164 pub fn len(&self) -> usize {
165 self.queue.len()
166 }
167
168 pub fn clear(&mut self) {
170 self.queue.clear();
171 }
172
173 #[must_use]
175 pub fn next_event_time(&self) -> Option<SimTime> {
176 self.peek().map(|e| e.time)
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183 use crate::engine::state::Vec3;
184
185 fn make_add_body_event(mass: f64) -> SimEvent {
186 SimEvent::AddBody {
187 mass,
188 position: Vec3::zero(),
189 velocity: Vec3::zero(),
190 }
191 }
192
193 #[test]
194 fn test_scheduler_time_ordering() {
195 let mut scheduler = EventScheduler::new();
196
197 scheduler.schedule(SimTime::from_secs(3.0), make_add_body_event(3.0));
199 scheduler.schedule(SimTime::from_secs(1.0), make_add_body_event(1.0));
200 scheduler.schedule(SimTime::from_secs(2.0), make_add_body_event(2.0));
201
202 let e1 = scheduler.next();
204 assert!(e1.is_some());
205 assert!(
206 (e1.as_ref().map(|e| e.time.as_secs_f64()).unwrap_or(0.0) - 1.0).abs() < f64::EPSILON
207 );
208
209 let e2 = scheduler.next();
210 assert!(
211 (e2.as_ref().map(|e| e.time.as_secs_f64()).unwrap_or(0.0) - 2.0).abs() < f64::EPSILON
212 );
213
214 let e3 = scheduler.next();
215 assert!(
216 (e3.as_ref().map(|e| e.time.as_secs_f64()).unwrap_or(0.0) - 3.0).abs() < f64::EPSILON
217 );
218
219 assert!(scheduler.is_empty());
220 }
221
222 #[test]
223 fn test_scheduler_sequence_ordering() {
224 let mut scheduler = EventScheduler::new();
225
226 let time = SimTime::from_secs(1.0);
228 scheduler.schedule(time, make_add_body_event(1.0));
229 scheduler.schedule(time, make_add_body_event(2.0));
230 scheduler.schedule(time, make_add_body_event(3.0));
231
232 if let Some(e) = scheduler.next() {
234 if let SimEvent::AddBody { mass, .. } = e.event {
235 assert!((mass - 1.0).abs() < f64::EPSILON);
236 }
237 }
238
239 if let Some(e) = scheduler.next() {
240 if let SimEvent::AddBody { mass, .. } = e.event {
241 assert!((mass - 2.0).abs() < f64::EPSILON);
242 }
243 }
244
245 if let Some(e) = scheduler.next() {
246 if let SimEvent::AddBody { mass, .. } = e.event {
247 assert!((mass - 3.0).abs() < f64::EPSILON);
248 }
249 }
250 }
251
252 #[test]
253 fn test_scheduler_next_before() {
254 let mut scheduler = EventScheduler::new();
255
256 scheduler.schedule(SimTime::from_secs(1.0), make_add_body_event(1.0));
257 scheduler.schedule(SimTime::from_secs(2.0), make_add_body_event(2.0));
258 scheduler.schedule(SimTime::from_secs(3.0), make_add_body_event(3.0));
259
260 let e1 = scheduler.next_before(SimTime::from_secs(1.5));
262 assert!(e1.is_some());
263 assert!((e1.map(|e| e.time.as_secs_f64()).unwrap_or(0.0) - 1.0).abs() < f64::EPSILON);
264
265 let e2 = scheduler.next_before(SimTime::from_secs(1.5));
267 assert!(e2.is_none());
268
269 let e3 = scheduler.next_before(SimTime::from_secs(2.0));
271 assert!(e3.is_some());
272 }
273
274 #[test]
275 fn test_scheduler_drain_until() {
276 let mut scheduler = EventScheduler::new();
277
278 for i in 1..=5 {
279 scheduler.schedule(SimTime::from_secs(i as f64), make_add_body_event(i as f64));
280 }
281
282 let events = scheduler.drain_until(SimTime::from_secs(3.0));
283 assert_eq!(events.len(), 3);
284 assert_eq!(scheduler.len(), 2);
285 }
286
287 #[test]
288 fn test_scheduler_peek() {
289 let mut scheduler = EventScheduler::new();
290
291 assert!(scheduler.peek().is_none());
292
293 scheduler.schedule(SimTime::from_secs(1.0), make_add_body_event(1.0));
294
295 assert!(scheduler.peek().is_some());
297 assert!(scheduler.peek().is_some());
298 assert_eq!(scheduler.len(), 1);
299
300 let _ = scheduler.next();
302 assert!(scheduler.peek().is_none());
303 }
304
305 #[test]
306 fn test_scheduler_clear() {
307 let mut scheduler = EventScheduler::new();
308
309 for i in 1..=10 {
310 scheduler.schedule(SimTime::from_secs(i as f64), make_add_body_event(i as f64));
311 }
312
313 assert_eq!(scheduler.len(), 10);
314
315 scheduler.clear();
316
317 assert!(scheduler.is_empty());
318 assert_eq!(scheduler.len(), 0);
319 }
320
321 #[test]
322 fn test_scheduler_schedule_all() {
323 let mut scheduler = EventScheduler::new();
324 let events = vec![
325 make_add_body_event(1.0),
326 make_add_body_event(2.0),
327 make_add_body_event(3.0),
328 ];
329
330 scheduler.schedule_all(SimTime::from_secs(1.0), &events);
331 assert_eq!(scheduler.len(), 3);
332
333 let mut masses = Vec::new();
335 while let Some(e) = scheduler.next() {
336 if let SimEvent::AddBody { mass, .. } = e.event {
337 masses.push(mass);
338 }
339 }
340 assert_eq!(masses, vec![1.0, 2.0, 3.0]);
341 }
342
343 #[test]
344 fn test_scheduler_next_event_time() {
345 let mut scheduler = EventScheduler::new();
346
347 assert!(scheduler.next_event_time().is_none());
348
349 scheduler.schedule(SimTime::from_secs(2.5), make_add_body_event(1.0));
350 scheduler.schedule(SimTime::from_secs(1.0), make_add_body_event(2.0));
351
352 let next_time = scheduler.next_event_time();
354 assert!(next_time.is_some());
355 assert!((next_time.map_or(0.0, |t| t.as_secs_f64()) - 1.0).abs() < f64::EPSILON);
356 }
357
358 #[test]
359 fn test_scheduled_event_new() {
360 let event = ScheduledEvent::new(SimTime::from_secs(1.0), 42, make_add_body_event(5.0));
361
362 assert!((event.time.as_secs_f64() - 1.0).abs() < f64::EPSILON);
363 assert_eq!(event.sequence, 42);
364 }
365
366 #[test]
367 fn test_scheduled_event_eq() {
368 let e1 = ScheduledEvent::new(SimTime::from_secs(1.0), 1, make_add_body_event(1.0));
369 let e2 = ScheduledEvent::new(SimTime::from_secs(1.0), 1, make_add_body_event(2.0));
370 let e3 = ScheduledEvent::new(SimTime::from_secs(1.0), 2, make_add_body_event(1.0));
371 let e4 = ScheduledEvent::new(SimTime::from_secs(2.0), 1, make_add_body_event(1.0));
372
373 assert_eq!(e1, e2);
375 assert_ne!(e1, e3);
377 assert_ne!(e1, e4);
379 }
380
381 #[test]
382 fn test_scheduled_event_ord() {
383 let earlier = ScheduledEvent::new(SimTime::from_secs(1.0), 1, make_add_body_event(1.0));
384 let later = ScheduledEvent::new(SimTime::from_secs(2.0), 1, make_add_body_event(1.0));
385 let same_time_seq1 =
386 ScheduledEvent::new(SimTime::from_secs(1.0), 1, make_add_body_event(1.0));
387 let same_time_seq2 =
388 ScheduledEvent::new(SimTime::from_secs(1.0), 2, make_add_body_event(1.0));
389
390 assert!(earlier < later);
391 assert!(same_time_seq1 < same_time_seq2);
392 }
393
394 #[test]
395 fn test_scheduled_event_partial_ord() {
396 let e1 = ScheduledEvent::new(SimTime::from_secs(1.0), 1, make_add_body_event(1.0));
397 let e2 = ScheduledEvent::new(SimTime::from_secs(2.0), 1, make_add_body_event(1.0));
398
399 assert!(e1.partial_cmp(&e2).is_some());
400 assert!(e1 < e2);
401 }
402
403 #[test]
404 fn test_scheduled_event_clone() {
405 let event = ScheduledEvent::new(SimTime::from_secs(1.0), 5, make_add_body_event(3.0));
406 let cloned = event.clone();
407
408 assert_eq!(event.time, cloned.time);
409 assert_eq!(event.sequence, cloned.sequence);
410 }
411
412 #[test]
413 fn test_scheduled_event_debug() {
414 let event = ScheduledEvent::new(SimTime::from_secs(1.0), 5, make_add_body_event(3.0));
415 let debug = format!("{:?}", event);
416 assert!(debug.contains("ScheduledEvent"));
417 }
418
419 #[test]
420 fn test_scheduler_default() {
421 let scheduler: EventScheduler = Default::default();
422 assert!(scheduler.is_empty());
423 assert_eq!(scheduler.len(), 0);
424 }
425
426 #[test]
427 fn test_scheduler_debug() {
428 let scheduler = EventScheduler::new();
429 let debug = format!("{:?}", scheduler);
430 assert!(debug.contains("EventScheduler"));
431 }
432
433 #[test]
434 fn test_scheduler_next_before_empty() {
435 let mut scheduler = EventScheduler::new();
436 assert!(scheduler.next_before(SimTime::from_secs(1.0)).is_none());
437 }
438
439 #[test]
440 fn test_scheduler_drain_until_empty() {
441 let mut scheduler = EventScheduler::new();
442 let events = scheduler.drain_until(SimTime::from_secs(1.0));
443 assert!(events.is_empty());
444 }
445}
446
447#[cfg(test)]
448mod proptests {
449 use super::*;
450 use crate::engine::state::Vec3;
451 use proptest::prelude::*;
452
453 fn make_event(mass: f64) -> SimEvent {
454 SimEvent::AddBody {
455 mass,
456 position: Vec3::zero(),
457 velocity: Vec3::zero(),
458 }
459 }
460
461 proptest! {
462 #[test]
464 fn prop_time_ordering(times in prop::collection::vec(0.0f64..1000.0, 1..100)) {
465 let mut scheduler = EventScheduler::new();
466
467 for (i, &t) in times.iter().enumerate() {
468 scheduler.schedule(SimTime::from_secs(t), make_event(i as f64));
469 }
470
471 let mut last_time = 0.0;
472 while let Some(event) = scheduler.next() {
473 let current_time = event.time.as_secs_f64();
474 prop_assert!(current_time >= last_time, "Events not in time order");
475 last_time = current_time;
476 }
477 }
478
479 #[test]
481 fn prop_drain_count(
482 times in prop::collection::vec(0.0f64..100.0, 1..50),
483 threshold in 0.0f64..100.0,
484 ) {
485 let mut scheduler = EventScheduler::new();
486
487 for (i, &t) in times.iter().enumerate() {
488 scheduler.schedule(SimTime::from_secs(t), make_event(i as f64));
489 }
490
491 let expected_count = times.iter().filter(|&&t| t <= threshold).count();
492 let events = scheduler.drain_until(SimTime::from_secs(threshold));
493
494 prop_assert_eq!(events.len(), expected_count);
495 }
496 }
497}