use super::events::EventQueue;
use super::Event;
use crate::{SimState, SimTime};
use std::fmt::{Debug, Formatter};
use std::ops::Add;
#[derive(Debug, Default)]
pub struct Simulation<State, Time>
where
State: SimState<Time>,
Time: SimTime,
{
event_queue: EventQueue<State, Time>,
state: State,
current_time: Time,
}
impl<State, Time> Simulation<State, Time>
where
State: SimState<Time>,
Time: SimTime,
{
pub fn new(initial_state: State, start_time: Time) -> Self {
Self {
event_queue: EventQueue::new(),
state: initial_state,
current_time: start_time,
}
}
#[allow(clippy::missing_panics_doc)]
pub fn run(&mut self) -> crate::Result {
loop {
if self.state.is_complete(self.current_time()) {
return Ok(());
}
let next_event = self.next_event();
if next_event.is_none() {
return Ok(());
}
let mut next_event = next_event.expect("next_event should not be None");
next_event.execute(self)?;
}
}
fn next_event(&mut self) -> Option<Box<dyn Event<State, Time>>> {
if let Some((event, time)) = self.event_queue.next() {
self.current_time = time;
Some(event)
} else {
None
}
}
pub fn schedule<EventType>(&mut self, event: EventType, time: Time) -> crate::Result
where
EventType: Event<State, Time> + 'static,
{
if time < self.current_time {
return Err(crate::Error::BackInTime);
}
unsafe {
self.schedule_unchecked(event, time);
}
Ok(())
}
pub unsafe fn schedule_unchecked<EventType>(&mut self, event: EventType, time: Time)
where
EventType: Event<State, Time> + 'static,
{
self.schedule_unchecked_from_boxed(Box::new(event), time);
}
pub fn schedule_from_boxed(&mut self, event: Box<dyn Event<State, Time>>, time: Time) -> crate::Result {
if time < self.current_time {
return Err(crate::Error::BackInTime);
}
unsafe {
self.schedule_unchecked_from_boxed(event, time);
}
Ok(())
}
pub unsafe fn schedule_unchecked_from_boxed(&mut self, event: Box<dyn Event<State, Time>>, time: Time) {
self.event_queue.schedule_event(event, time);
}
pub fn state(&self) -> &State {
&self.state
}
pub fn state_mut(&mut self) -> &mut State {
&mut self.state
}
pub fn current_time(&self) -> &Time {
&self.current_time
}
}
impl<State, Time> Simulation<State, Time>
where
State: SimState<Time>,
Time: SimTime + Clone,
{
pub fn schedule_now<EventType>(&mut self, event: EventType) -> crate::Result
where
EventType: Event<State, Time> + 'static,
{
let event_time = self.current_time.clone();
self.schedule(event, event_time)
}
pub unsafe fn schedule_now_unchecked<EventType>(&mut self, event: EventType)
where
EventType: Event<State, Time> + 'static,
{
self.schedule_unchecked(event, self.current_time.clone());
}
pub fn schedule_now_from_boxed(&mut self, event: Box<dyn Event<State, Time>>) -> crate::Result {
let event_time = self.current_time.clone();
self.schedule_from_boxed(event, event_time)
}
pub unsafe fn schedule_now_unchecked_from_boxed(&mut self, event: Box<dyn Event<State, Time>>) {
self.schedule_unchecked_from_boxed(event, self.current_time.clone());
}
}
impl<State, Time> Simulation<State, Time>
where
State: SimState<Time>,
Time: SimTime + Clone + Add<Output = Time>,
{
pub fn schedule_with_delay<EventType>(&mut self, event: EventType, delay: Time) -> crate::Result
where
EventType: Event<State, Time> + 'static,
{
let event_time = self.current_time.clone() + delay;
self.schedule(event, event_time)
}
pub unsafe fn schedule_with_delay_unchecked<EventType>(&mut self, event: EventType, delay: Time)
where
EventType: Event<State, Time> + 'static,
{
let event_time = self.current_time.clone() + delay;
self.schedule_unchecked(event, event_time);
}
pub fn schedule_with_delay_from_boxed(&mut self, event: Box<dyn Event<State, Time>>, delay: Time) -> crate::Result {
let event_time = self.current_time.clone() + delay;
self.schedule_from_boxed(event, event_time)
}
pub unsafe fn schedule_with_delay_unchecked_from_boxed(&mut self, event: Box<dyn Event<State, Time>>, delay: Time) {
let event_time = self.current_time.clone() + delay;
self.schedule_unchecked_from_boxed(event, event_time);
}
}
impl<State, Time> std::fmt::Display for Simulation<State, Time>
where
State: SimState<Time>,
Time: SimTime,
{
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "Simulation at time {:?}", self.current_time)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::serial::OkEvent;
#[derive(Debug)]
struct State {
executed_event_values: Vec<i32>,
complete: bool,
}
impl SimState<i32> for State {
fn is_complete(&self, _: &i32) -> bool {
self.complete
}
}
#[derive(Debug)]
struct TestEvent {
value: i32,
}
impl Event<State, i32> for TestEvent {
fn execute(&mut self, simulation: &mut Simulation<State, i32>) -> crate::Result {
simulation.state_mut().executed_event_values.push(self.value);
Ok(())
}
}
#[derive(Debug)]
struct CompletionEvent {}
impl OkEvent<State, i32> for CompletionEvent {
fn execute(&mut self, simulation: &mut Simulation<State, i32>) {
simulation.state_mut().complete = true;
}
}
fn setup() -> Simulation<State, i32> {
let mut sim = Simulation::new(
State {
executed_event_values: Vec::with_capacity(3),
complete: false,
},
0,
);
let events: [TestEvent; 3] = [TestEvent { value: 1 }, TestEvent { value: 3 }, TestEvent { value: 2 }];
for (i, event) in events.into_iter().enumerate() {
sim.schedule(event, 2 * i as i32).unwrap();
}
sim
}
#[test]
fn execution_time_ascends() {
let mut sim = setup();
sim.run().expect("simulation should run to completion");
assert_eq!(
vec![1, 3, 2],
sim.state().executed_event_values,
"events did not execute in expected order"
);
}
#[test]
fn schedule_fails_if_given_invalid_execution_time() {
let mut sim = setup();
let result = sim.schedule(TestEvent { value: 0 }, -1);
assert!(result.is_err(), "sim failed to reject event scheduled for the past");
assert_eq!(
crate::Error::BackInTime,
result.err().unwrap(),
"sim returned unexpected error type"
);
}
#[test]
fn unsafe_schedulers_allow_time_to_reverse() {
let mut sim = setup();
unsafe {
sim.schedule_unchecked(TestEvent { value: 1 }, -1);
}
sim.next_event().expect("event queue should yield a scheduled event");
assert_eq!(
-1,
*sim.current_time(),
"current time did not update when popping event"
);
}
#[test]
fn insertion_sequence_breaks_ties_in_execution_time() {
const NUM_EVENTS: i32 = 10;
let state = State {
executed_event_values: Vec::with_capacity(NUM_EVENTS as usize),
complete: false,
};
let mut sim = Simulation::new(state, 0);
for copy_id in 0..NUM_EVENTS {
sim.schedule(TestEvent { value: copy_id }, 1)
.expect("failed to schedule event");
}
while let Some(mut event) = sim.next_event() {
event.execute(&mut sim).expect("failed to execute event");
}
let expected: Vec<_> = (0..NUM_EVENTS).collect();
assert_eq!(
expected,
sim.state().executed_event_values,
"events executed out of insertion sequence"
);
}
#[test]
fn simulation_executes_events() {
let mut sim = setup();
sim.run().unwrap();
let expected = vec![1, 3, 2];
assert_eq!(
expected, sim.state.executed_event_values,
"events did not execute in correct order"
);
}
#[test]
fn simulation_stops_with_events_still_in_queue() {
let mut sim = setup();
sim.schedule_from_boxed(Box::new(CompletionEvent {}), 3).unwrap();
sim.run().unwrap();
let expected = vec![1, 3];
assert_eq!(
expected, sim.state.executed_event_values,
"simulation did not terminate with completion event"
);
}
#[test]
fn delay_schedulers_choose_expected_times() {
let state = State {
executed_event_values: Vec::with_capacity(3),
complete: false,
};
let mut sim = Simulation::new(state, 0);
sim.schedule(TestEvent { value: 1 }, 1).unwrap();
sim.schedule(TestEvent { value: 2 }, 3).unwrap();
let mut first_event = sim.next_event().expect("should be able to pop scheduled event");
first_event.execute(&mut sim).expect("event should execute normally");
assert_eq!(1, *sim.current_time(), "queue should be at time of last popped event");
assert_eq!(
vec![1],
sim.state().executed_event_values,
"state should match first executed event"
);
sim.schedule_now(TestEvent { value: 3 })
.expect("should be able to schedule new event");
sim.schedule_with_delay(TestEvent { value: 4 }, 1)
.expect("should be able to schedule new event");
let mut next_event = sim.next_event().expect("should be able to pop scheduled event");
next_event.execute(&mut sim).expect("event should execute normally");
assert_eq!(1, *sim.current_time(), "queue should be at time of last popped event");
assert_eq!(
vec![1, 3],
sim.state().executed_event_values,
"state should match first executed event"
);
next_event = sim.next_event().expect("should be able to pop scheduled event");
next_event.execute(&mut sim).expect("event should execute normally");
assert_eq!(2, *sim.current_time(), "queue should be at time of last popped event");
assert_eq!(
vec![1, 3, 4],
sim.state().executed_event_values,
"state should match first executed event"
);
}
}