use crate::algorithm::{MinimumCostBound, QueueLength};
use std::{ops::Fn, sync::Arc};
pub trait Halt<Mem>: Clone {
fn halt(&mut self, memory: &Mem) -> bool;
}
impl<Mem> Halt<Mem> for () {
fn halt(&mut self, _: &Mem) -> bool {
false
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Interruption {
Continue,
Stop,
}
pub type Interrupter = Arc<dyn Fn() -> Interruption>;
#[derive(Clone)]
pub struct Interruptible(pub Option<Interrupter>);
impl Interruptible {
pub fn new<F: Fn() -> Interruption + 'static>(f: F) -> Self {
Self(Some(Arc::new(f)))
}
pub fn none() -> Self {
Self(None)
}
}
impl<Mem> Halt<Mem> for Interruptible {
fn halt(&mut self, _: &Mem) -> bool {
if let Some(interrupter) = &self.0 {
return Interruption::Stop == interrupter();
}
return false;
}
}
#[derive(Debug, Clone, Copy)]
pub struct StepLimit {
steps: usize,
pub limit: Option<usize>,
}
impl StepLimit {
pub fn new(limit: Option<usize>) -> Self {
Self { steps: 0, limit }
}
pub fn reset(&mut self) {
self.steps = 0;
}
}
impl<Mem> Halt<Mem> for StepLimit {
fn halt(&mut self, _: &Mem) -> bool {
self.steps += 1;
if let Some(limit) = self.limit {
return self.steps > limit;
}
false
}
}
#[derive(Default, Clone)]
pub struct QueueLengthLimit(pub Option<usize>);
impl<Mem> Halt<Mem> for QueueLengthLimit
where
Mem: QueueLength,
{
fn halt(&mut self, memory: &Mem) -> bool {
if let Some(limit) = self.0 {
return memory.queue_length() > limit;
}
false
}
}
#[derive(Default, Clone)]
pub struct CostLimit<C>(pub Option<C>);
impl<Mem: MinimumCostBound> Halt<Mem> for CostLimit<Mem::Cost>
where
Mem::Cost: Clone + PartialOrd,
{
fn halt(&mut self, memory: &Mem) -> bool {
if let Some(limit) = &self.0 {
match memory.minimum_cost_bound() {
Some(bound) => return bound > *limit,
None => return true,
}
}
false
}
}
use paste;
macro_rules! and_tuple_halt {
( $( $name:ident )+ ) => {
paste::item! {
impl<Mem, $($name: Halt<Mem>),+> Halt<Mem> for ($($name,)+) {
fn halt(
&mut self,
memory: &Mem,
) -> bool {
let ($([<$name:lower>],)+) = self;
false $(|| [<$name:lower>].halt(memory))+
}
}
}
};
}
and_tuple_halt! { A }
and_tuple_halt! { A B }
and_tuple_halt! { A B C }
and_tuple_halt! { A B C D }
and_tuple_halt! { A B C D E }
and_tuple_halt! { A B C D E F }
and_tuple_halt! { A B C D E F G }
and_tuple_halt! { A B C D E F G H }
#[cfg(test)]
mod tests {
use super::*;
struct FakeMem;
impl QueueLength for FakeMem {
fn queue_length(&self) -> usize {
0
}
}
#[test]
fn test_tuple_options() {
let mut halting = (
Interruptible::new(|| Interruption::Continue),
StepLimit::new(Some(10)),
);
assert!(!halting.halt(&FakeMem));
let mut halting = (
Interruptible::new(|| Interruption::Continue),
StepLimit::new(Some(5)),
QueueLengthLimit(Some(100)),
);
for _ in 0..5 {
assert!(!halting.halt(&FakeMem));
}
assert!(halting.halt(&FakeMem));
}
}