use super::StopCriterion;
use crate::core::Problem;
use std::marker::PhantomData;
#[derive(Debug, Clone)]
pub struct CriterionCombiner<P, A, B> {
a: A,
b: B,
_p: PhantomData<P>,
}
impl<A, B, P> CriterionCombiner<P, A, B>
where
A: StopCriterion<P>,
B: StopCriterion<P>,
P: Problem,
{
pub fn new(a: A, b: B) -> Self {
Self {
a,
b,
_p: PhantomData,
}
}
}
impl<A, B, P> StopCriterion<P> for CriterionCombiner<P, A, B>
where
A: StopCriterion<P>,
B: StopCriterion<P>,
P: Problem,
{
fn progress(&self) -> f64 {
let a = self.a.progress();
let b = self.b.progress();
if a > b {
a
} else {
b
}
}
fn update(&mut self, new_value: P::Value) {
self.a.update(new_value);
self.b.update(new_value);
}
fn current_iter(&self) -> usize {
self.a.current_iter()
}
}
#[cfg(test)]
mod tests {
use std::{thread::sleep, time::Duration};
use crate::core::stop_criterion::*;
use super::*;
fn setup<P: Problem>(iter: usize, time: Duration) -> impl StopCriterion<P> {
let iter = IterCriterion::<P>::new(iter);
let time = TimeCriterion::<P>::new(time);
CriterionCombiner::new(iter, time)
}
#[test]
fn iter_works() {
let max_iters = 5;
let mut stop_criterion = setup::<()>(max_iters, Duration::MAX);
(0..max_iters).for_each(|i| {
assert!(!stop_criterion.should_stop());
stop_criterion.update(i);
});
assert!(stop_criterion.should_stop());
}
#[test]
#[ignore]
fn time_works() {
let stop_criterion = setup::<()>(usize::MAX, Duration::from_nanos(1));
sleep(Duration::from_millis(10));
assert!(stop_criterion.should_stop());
}
}