use radiate_error::Result;
pub trait Engine {
type Epoch;
fn next(&mut self) -> Result<Self::Epoch>;
}
pub trait EngineExt<E: Engine> {
fn run<F>(&mut self, limit: F) -> E::Epoch
where
F: Fn(&E::Epoch) -> bool;
}
impl<E> EngineExt<E> for E
where
E: Engine,
{
fn run<F>(&mut self, limit: F) -> E::Epoch
where
F: Fn(&E::Epoch) -> bool,
{
loop {
match self.next() {
Ok(epoch) => {
if limit(&epoch) {
return epoch;
}
}
Err(e) => {
panic!("{e}");
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
struct MockEpoch {
generation: usize,
fitness: f32,
}
#[derive(Default)]
struct MockEngine {
generation: usize,
}
impl Engine for MockEngine {
type Epoch = MockEpoch;
fn next(&mut self) -> Result<Self::Epoch> {
self.generation += 1;
Ok(MockEpoch {
generation: self.generation,
fitness: 1.0 / (self.generation as f32),
})
}
}
#[test]
fn test_engine_next() {
let mut engine = MockEngine::default();
let epoch1 = engine.next().unwrap();
assert_eq!(epoch1.generation, 1);
assert_eq!(epoch1.fitness, 1.0);
let epoch2 = engine.next().unwrap();
assert_eq!(epoch2.generation, 2);
assert_eq!(epoch2.fitness, 0.5);
}
#[test]
fn test_engine_ext_run_generation_limit() {
let mut engine = MockEngine::default();
let final_epoch = engine.run(|epoch| epoch.generation >= 3);
assert_eq!(final_epoch.generation, 3);
assert_eq!(final_epoch.fitness, 1.0 / 3.0);
}
#[test]
fn test_engine_ext_run_fitness_limit() {
let mut engine = MockEngine::default();
let final_epoch = engine.run(|epoch| epoch.fitness < 0.3);
assert_eq!(final_epoch.generation, 4);
assert_eq!(final_epoch.fitness, 0.25);
}
#[test]
fn test_engine_ext_run_complex_condition() {
let mut engine = MockEngine::default();
let final_epoch = engine.run(|epoch| epoch.generation >= 5 || epoch.fitness < 0.2);
assert_eq!(final_epoch.generation, 5);
assert_eq!(final_epoch.fitness, 0.2);
}
#[test]
fn test_engine_ext_run_immediate_termination() {
let mut engine = MockEngine::default();
let final_epoch = engine.run(|_| true);
assert_eq!(final_epoch.generation, 1);
assert_eq!(final_epoch.fitness, 1.0);
}
#[test]
fn test_engine_ext_run_zero_generations() {
let mut engine = MockEngine::default();
let final_epoch = engine.run(|epoch| epoch.generation > 0);
assert_eq!(final_epoch.generation, 1);
}
#[test]
fn test_engine_ext_method_chaining() {
let mut engine = MockEngine::default();
let epoch1 = engine.run(|epoch| epoch.generation >= 2);
assert_eq!(epoch1.generation, 2);
let epoch2 = engine.run(|epoch| epoch.generation >= 4);
assert_eq!(epoch2.generation, 4);
}
}