use super::{
geometric_traits::{IterateNeighbours, IterateNeighboursContext},
sketch::Bag,
};
use std::{
cmp::Ordering,
collections::{HashMap, HashSet},
hash::Hash,
};
pub enum ExploreSignals {
ReachedGoal,
Explore,
Skip,
}
pub trait PointKeyValue {
type K: Eq + Clone + Copy + Hash;
type V: Eq + Clone + Copy + Hash;
fn get_key(&self) -> Self::K;
fn get_value(&self) -> Self::V;
fn compare_values(k: &Self::K, a: &Self::V, b: &Self::V) -> Option<Ordering>;
}
#[derive(Debug)]
pub struct Exploration<P: IterateNeighbours<S>, S: IterateNeighboursContext, D> {
pub context: S,
phantom: std::marker::PhantomData<P>,
pub extra_data: D,
}
impl<P: Clone + Copy, S: IterateNeighboursContext, D> Exploration<P, S, D>
where
P: IterateNeighbours<S> + Hash + Eq,
{
pub fn new(context: S, extra_data: D) -> Self {
Self {
context,
phantom: std::marker::PhantomData,
extra_data,
}
}
pub fn explore<F, G, B: Bag<P>>(&mut self, start: P, mut goal: G, mut filter_neighbours: F)
where
F: FnMut(&P, &P, &mut S, &mut D) -> bool,
G: FnMut(&P, &mut S, &mut D) -> ExploreSignals,
{
self.explore_advanced::<_, _, _, B>(
start,
(),
|p, _data, context, extra_data| goal(p, context, extra_data),
|p, n, _data, context, extra_data| filter_neighbours(p, n, context, extra_data),
)
}
pub fn explore_avoid_identical<F, G, B: Bag<P>>(
&mut self,
start: P,
mut goal: G,
mut filter_neighbours: F,
) where
F: FnMut(&P, &P, &mut S, &mut D) -> bool,
G: FnMut(&P, &mut S, &mut D) -> ExploreSignals,
{
self.explore_advanced::<_, _, _, B>(
start,
HashSet::new(),
|p, data, context, extra_data| {
if data.contains(p) {
ExploreSignals::Skip
} else {
data.insert(*p);
goal(p, context, extra_data)
}
},
|p, n, data, context, extra_data| {
!data.contains(n) && filter_neighbours(p, n, context, extra_data)
},
)
}
pub fn explore_advanced<T, F, G, B: Bag<P>>(
&mut self,
start: P,
mut data: T,
mut goal: G,
mut filter_neighbours: F,
) where
F: FnMut(&P, &P, &mut T, &mut S, &mut D) -> bool,
G: FnMut(&P, &mut T, &mut S, &mut D) -> ExploreSignals,
{
let mut open = B::new();
open.put(start);
while !open.is_empty() {
let p = open.get().unwrap();
match goal(&p, &mut data, &mut self.context, &mut self.extra_data) {
ExploreSignals::ReachedGoal => break,
ExploreSignals::Explore => (),
ExploreSignals::Skip => continue,
}
for n in p.neighbours(&self.context) {
if filter_neighbours(&p, &n, &mut data, &mut self.context, &mut self.extra_data) {
open.put(n);
}
}
}
}
}
impl<P, S, D> Exploration<P, S, D>
where
P: IterateNeighbours<S> + PointKeyValue,
P: Clone + Copy + Hash + Eq,
S: IterateNeighboursContext,
{
pub fn explore_avoid_worse<F, G, B: Bag<P>>(
&mut self,
start: P,
mut goal: G,
mut filter_neighbours: F,
) where
F: FnMut(&P, &P, &mut S, &mut D) -> bool,
G: FnMut(&P, &mut S, &mut D) -> ExploreSignals,
{
self.explore_advanced::<_, _, _, B>(
start,
HashMap::<<P as PointKeyValue>::K, <P as PointKeyValue>::V>::new(),
|p, data, context, extra_data| {
let k = p.get_key();
let v = p.get_value();
if let Some(&old_v) = data.get(&k) {
if let Some(ordering) = P::compare_values(&k, &v, &old_v) {
match ordering {
Ordering::Less => return ExploreSignals::Skip,
Ordering::Equal => (),
Ordering::Greater => {
data.insert(k, v);
}
}
}
} else {
data.insert(k, v);
}
goal(p, context, extra_data)
},
|p, n, data, context, extra_data| {
let k = n.get_key();
let v = n.get_value();
if let Some(&old_v) = data.get(&k)
&& P::compare_values(&k, &v, &old_v) == Some(Ordering::Less)
{
return false;
}
filter_neighbours(p, n, context, extra_data)
},
)
}
}