use enumset::EnumSet;
use smallvec::SmallVec;
use std::collections::HashMap;
use crate::arena::Handle;
use crate::arena::List;
use crate::arena::ListArena;
use crate::graph::Edge;
use crate::graph::Node;
use crate::graph::StackGraph;
use crate::partial::Cyclicity;
use crate::partial::PartialPath;
use crate::partial::PartialPaths;
use crate::paths::PathResolutionError;
use crate::stitching::Database;
use crate::stitching::OwnedOrDatabasePath;
pub struct SimilarPathDetector<P> {
paths: HashMap<PathKey, SmallVec<[P; 8]>>,
}
#[doc(hidden)]
#[derive(Clone, Eq, Hash, PartialEq)]
pub struct PathKey {
start_node: Handle<Node>,
end_node: Handle<Node>,
symbol_stack_precondition_len: usize,
scope_stack_precondition_len: usize,
symbol_stack_postcondition_len: usize,
scope_stack_postcondition_len: usize,
}
#[doc(hidden)]
pub trait HasPathKey: Clone {
type Arena;
fn key(&self) -> PathKey;
}
impl HasPathKey for PartialPath {
type Arena = PartialPaths;
fn key(&self) -> PathKey {
PathKey {
start_node: self.start_node,
end_node: self.end_node,
symbol_stack_precondition_len: self.symbol_stack_precondition.len(),
scope_stack_precondition_len: self.scope_stack_precondition.len(),
symbol_stack_postcondition_len: self.symbol_stack_postcondition.len(),
scope_stack_postcondition_len: self.scope_stack_postcondition.len(),
}
}
}
impl<P> SimilarPathDetector<P>
where
P: HasPathKey,
{
pub fn new() -> SimilarPathDetector<P> {
SimilarPathDetector {
paths: HashMap::new(),
}
}
pub fn has_similar_path<Eq>(
&mut self,
_graph: &StackGraph,
arena: &mut P::Arena,
path: &P,
eq: Eq,
) -> bool
where
Eq: Fn(&mut P::Arena, &P, &P) -> bool,
{
let key = path.key();
let possibly_similar_paths = self.paths.entry(key).or_default();
for other_path in possibly_similar_paths.iter() {
if eq(arena, path, other_path) {
return true;
}
}
possibly_similar_paths.push(path.clone());
false
}
#[cfg(feature = "copious-debugging")]
pub fn max_bucket_size(&self) -> usize {
self.paths.iter().map(|b| b.1.len()).max().unwrap_or(0)
}
}
pub trait Appendable {
type Ctx;
fn append_to(
&self,
graph: &StackGraph,
partials: &mut PartialPaths,
ctx: &mut Self::Ctx,
path: &mut PartialPath,
) -> Result<(), PathResolutionError>;
fn start_node(&self, ctx: &mut Self::Ctx) -> Handle<Node>;
fn end_node(&self, ctx: &mut Self::Ctx) -> Handle<Node>;
}
#[derive(Clone)]
pub struct AppendingCycleDetector<A> {
appendages: List<A>,
}
pub type Appendables<A> = ListArena<A>;
impl<A: Appendable + Clone> AppendingCycleDetector<A> {
pub fn new() -> Self {
Self {
appendages: List::empty(),
}
}
pub fn from(appendables: &mut Appendables<A>, appendage: A) -> Self {
let mut result = Self::new();
result.appendages.push_front(appendables, appendage);
result
}
pub fn append(&mut self, appendables: &mut Appendables<A>, appendage: A) {
self.appendages.push_front(appendables, appendage);
}
pub fn is_cyclic(
&self,
graph: &StackGraph,
partials: &mut PartialPaths,
ctx: &mut A::Ctx,
appendables: &mut Appendables<A>,
) -> Result<EnumSet<Cyclicity>, PathResolutionError> {
let mut cycles = EnumSet::new();
let end_node = match self.appendages.clone().pop_front(appendables) {
Some(appendage) => appendage.end_node(ctx),
None => return Ok(cycles),
};
let mut maybe_cyclic_path = None;
let mut appendages = self.appendages;
loop {
let mut prefix_appendages = List::empty();
loop {
let appendable = appendages.pop_front(appendables).cloned();
match appendable {
Some(appendage) => {
let is_cycle = appendage.start_node(ctx) == end_node;
prefix_appendages.push_front(appendables, appendage);
if is_cycle {
break;
}
}
None => return Ok(cycles),
}
}
let mut prefix_path = PartialPath::from_node(graph, partials, end_node);
while let Some(appendage) = prefix_appendages.pop_front(appendables) {
prefix_path.resolve_to_node(graph, partials, appendage.start_node(ctx))?;
appendage.append_to(graph, partials, ctx, &mut prefix_path)?;
}
let cyclic_path = maybe_cyclic_path
.unwrap_or_else(|| PartialPath::from_node(graph, partials, end_node));
prefix_path.resolve_to_node(graph, partials, cyclic_path.start_node)?;
prefix_path.ensure_no_overlapping_variables(partials, &cyclic_path);
prefix_path.concatenate(graph, partials, &cyclic_path)?;
if let Some(cyclicity) = prefix_path.is_cyclic(graph, partials) {
cycles |= cyclicity;
}
maybe_cyclic_path = Some(prefix_path);
}
}
}
impl Appendable for Edge {
type Ctx = ();
fn append_to(
&self,
graph: &StackGraph,
partials: &mut PartialPaths,
_: &mut (),
path: &mut PartialPath,
) -> Result<(), PathResolutionError> {
path.append(graph, partials, *self)
}
fn start_node(&self, _: &mut ()) -> Handle<Node> {
self.source
}
fn end_node(&self, _: &mut ()) -> Handle<Node> {
self.sink
}
}
impl Appendable for OwnedOrDatabasePath {
type Ctx = Database;
fn append_to(
&self,
graph: &StackGraph,
partials: &mut PartialPaths,
db: &mut Database,
path: &mut PartialPath,
) -> Result<(), PathResolutionError> {
path.ensure_no_overlapping_variables(partials, self.get(db));
path.concatenate(graph, partials, self.get(db))
}
fn start_node(&self, db: &mut Database) -> Handle<Node> {
self.get(db).start_node
}
fn end_node(&self, db: &mut Database) -> Handle<Node> {
self.get(db).end_node
}
}