use enumset::EnumSet;
use smallvec::SmallVec;
use std::cmp::Ordering;
use std::collections::HashMap;
use crate::arena::Arena;
use crate::arena::Handle;
use crate::arena::List;
use crate::arena::ListArena;
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::stats::FrequencyDistribution;
use crate::stitching::Appendable;
use crate::stitching::ToAppendable;
pub struct SimilarPathDetector<P> {
paths: HashMap<PathKey, SmallVec<[P; 4]>>,
counts: Option<HashMap<PathKey, SmallVec<[usize; 4]>>>,
}
#[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(),
counts: None,
}
}
pub fn set_collect_stats(&mut self, collect_stats: bool) {
if !collect_stats {
self.counts = None;
} else if self.counts.is_none() {
self.counts = Some(HashMap::new());
}
}
pub fn add_path<Cmp>(
&mut self,
_graph: &StackGraph,
arena: &mut P::Arena,
path: &P,
cmp: Cmp,
) -> bool
where
Cmp: Fn(&mut P::Arena, &P, &P) -> Option<Ordering>,
{
let key = path.key();
let possibly_similar_paths = self.paths.entry(key.clone()).or_default();
let mut possible_similar_counts = self
.counts
.as_mut()
.map(move |cs| cs.entry(key).or_default());
let mut idx = 0;
let mut count = 0;
while idx < possibly_similar_paths.len() {
let other_path = &mut possibly_similar_paths[idx];
match cmp(arena, path, other_path) {
Some(Ordering::Less) => {
possibly_similar_paths.remove(idx);
if let Some(possible_similar_counts) = possible_similar_counts.as_mut() {
count += possible_similar_counts[idx];
possible_similar_counts.remove(idx);
}
continue;
}
Some(_) => {
if let Some(possible_similar_counts) = possible_similar_counts {
possible_similar_counts[idx] += 1;
}
return true;
}
None => {
idx += 1;
}
}
}
possibly_similar_paths.push(path.clone());
if let Some(possible_similar_counts) = possible_similar_counts {
possible_similar_counts.push(count);
}
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 fn stats(&self) -> SimilarPathStats {
let mut stats = SimilarPathStats::default();
if let Some(counts) = &self.counts {
for bucket in counts.values() {
stats.similar_path_bucket_size.record(bucket.len());
for count in bucket.iter() {
stats.similar_path_count.record(*count);
}
}
}
stats
}
}
#[derive(Clone, Debug, Default)]
pub struct SimilarPathStats {
pub similar_path_count: FrequencyDistribution<usize>,
pub similar_path_bucket_size: FrequencyDistribution<usize>,
}
impl std::ops::AddAssign<Self> for SimilarPathStats {
fn add_assign(&mut self, rhs: Self) {
self.similar_path_bucket_size += rhs.similar_path_bucket_size;
self.similar_path_count += rhs.similar_path_count;
}
}
impl std::ops::AddAssign<&Self> for SimilarPathStats {
fn add_assign(&mut self, rhs: &Self) {
self.similar_path_bucket_size += &rhs.similar_path_bucket_size;
self.similar_path_count += &rhs.similar_path_count;
}
}
pub struct Appendables<H> {
elements: ListArena<InternedOrHandle<H>>,
interned: Arena<PartialPath>,
}
impl<H> Appendables<H> {
pub fn new() -> Self {
Self {
elements: ListArena::new(),
interned: Arena::new(),
}
}
}
#[derive(Clone)]
enum InternedOrHandle<H> {
Interned(Handle<PartialPath>),
Database(H),
}
impl<H> InternedOrHandle<H>
where
H: Clone,
{
fn append_to<'a, A, Db>(
&self,
graph: &StackGraph,
partials: &mut PartialPaths,
db: &'a Db,
interned: &Arena<PartialPath>,
path: &mut PartialPath,
) -> Result<(), PathResolutionError>
where
A: Appendable + 'a,
Db: ToAppendable<H, A>,
{
match self {
Self::Interned(h) => interned.get(*h).append_to(graph, partials, path),
Self::Database(h) => db.get_appendable(h).append_to(graph, partials, path),
}
}
fn start_node<'a, A, Db>(&self, db: &'a Db, interned: &Arena<PartialPath>) -> Handle<Node>
where
A: Appendable + 'a,
Db: ToAppendable<H, A>,
{
match self {
Self::Interned(h) => interned.get(*h).start_node,
Self::Database(h) => db.get_appendable(h).start_node(),
}
}
fn end_node<'a, A, Db>(&self, db: &'a Db, interned: &Arena<PartialPath>) -> Handle<Node>
where
A: Appendable + 'a,
Db: ToAppendable<H, A>,
{
match self {
Self::Interned(h) => interned.get(*h).end_node,
Self::Database(h) => db.get_appendable(h).end_node(),
}
}
}
#[derive(Clone)]
pub struct AppendingCycleDetector<H> {
appendages: List<InternedOrHandle<H>>,
}
impl<H> AppendingCycleDetector<H> {
pub fn new() -> Self {
Self {
appendages: List::empty(),
}
}
pub fn from(appendables: &mut Appendables<H>, path: PartialPath) -> Self {
let h = appendables.interned.add(path);
let mut result = Self::new();
result
.appendages
.push_front(&mut appendables.elements, InternedOrHandle::Interned(h));
result
}
pub fn append(&mut self, appendables: &mut Appendables<H>, appendage: H) {
self.appendages.push_front(
&mut appendables.elements,
InternedOrHandle::Database(appendage),
);
}
}
impl<H> AppendingCycleDetector<H>
where
H: Clone,
{
pub fn is_cyclic<'a, A, Db>(
&self,
graph: &StackGraph,
partials: &mut PartialPaths,
db: &'a Db,
appendables: &mut Appendables<H>,
) -> Result<EnumSet<Cyclicity>, PathResolutionError>
where
A: Appendable + 'a,
Db: ToAppendable<H, A>,
{
let mut cycles = EnumSet::new();
let end_node = match self.appendages.clone().pop_front(&mut appendables.elements) {
Some(appendage) => appendage.end_node(db, &appendables.interned),
None => return Ok(cycles),
};
let mut maybe_cyclic_path = None;
let mut remaining_appendages = self.appendages;
let mut prefix_appendages = Vec::new();
loop {
let mut counting_appendages = remaining_appendages;
let mut cycle_length = 0usize;
loop {
let appendable = counting_appendages.pop_front(&mut appendables.elements);
match appendable {
Some(appendage) => {
cycle_length += 1;
let is_cycle = appendage.start_node(db, &appendables.interned) == end_node;
if is_cycle {
break;
}
}
None => return Ok(cycles),
}
}
prefix_appendages.clear();
prefix_appendages.reserve(cycle_length);
for _ in 0..cycle_length {
let appendable = remaining_appendages
.pop_front(&mut appendables.elements)
.expect("")
.clone();
prefix_appendages.push(appendable);
}
let mut prefix_path = PartialPath::from_node(graph, partials, end_node);
while let Some(appendage) = prefix_appendages.pop() {
appendage.append_to(
graph,
partials,
db,
&appendables.interned,
&mut prefix_path,
)?;
}
let cyclic_path = maybe_cyclic_path
.unwrap_or_else(|| PartialPath::from_node(graph, partials, end_node));
cyclic_path.append_to(graph, partials, &mut prefix_path)?;
if prefix_path.edges.len() > 0 {
if let Some(cyclicity) = prefix_path.is_cyclic(graph, partials) {
cycles |= cyclicity;
}
}
maybe_cyclic_path = Some(prefix_path);
}
}
}