use ojo_graph::Graph;
use std::collections::{HashMap, HashSet};
use crate::{Change, Changes, Graggle, LiveGraph, NodeId};
pub struct CycleResolver<'a> {
graggle: Graggle<'a>,
sccs: ojo_graph::Partition<LiveGraph<'a>>,
large_sccs: Vec<usize>,
scc_reps: HashMap<usize, NodeId>,
}
impl<'a> CycleResolver<'a> {
pub fn new(graggle: Graggle<'a>) -> CycleResolver<'a> {
let sccs = graggle.as_live_graph().tarjan();
let large_sccs = sccs
.parts()
.enumerate()
.filter(|(_, part)| part.len() >= 2)
.map(|(i, _)| i)
.collect::<Vec<_>>();
CycleResolver {
graggle,
sccs,
large_sccs,
scc_reps: HashMap::new(),
}
}
pub fn next_component(&self) -> Option<&HashSet<NodeId>> {
self.large_sccs.last().map(|i| self.sccs.part(*i))
}
fn cur(&self) -> usize {
*self.large_sccs.last().unwrap()
}
pub fn resolve_component(&mut self, rep: NodeId) {
assert!(self.sccs.part(self.cur()).contains(&rep));
let cur = self.large_sccs.pop().unwrap();
self.scc_reps.insert(cur, rep);
}
pub fn into_order_resolver(self) -> OrderResolver<'a> {
assert!(self.large_sccs.is_empty());
let scc_reps = (0..self.sccs.num_components())
.map(|i| {
if let Some(&u) = self.scc_reps.get(&i) {
u
} else {
let mut iter = self.sccs.part(i).iter();
let rep = iter.next().expect("components must be non-empty");
assert!(iter.next().is_none(), "this component must have size 1");
*rep
}
})
.collect::<Vec<_>>();
let in_edge_count = self
.sccs
.nodes()
.map(|u| (u, self.sccs.in_edges(&u).count()))
.collect::<HashMap<_, _>>();
let candidates = in_edge_count
.iter()
.filter(|&(_, &count)| count == 0)
.map(|(u, _)| *u)
.collect::<Vec<_>>();
OrderResolver {
graggle: self.graggle,
ordered: vec![],
seen: HashSet::new(),
sccs: self.sccs,
scc_reps,
remaining_in_edges: in_edge_count,
candidates,
}
}
}
pub struct CandidateChain<'a> {
graggle: Graggle<'a>,
id: NodeId,
}
impl<'a> CandidateChain<'a> {
pub fn first(&self) -> NodeId {
self.id
}
pub fn iter(&self) -> impl Iterator<Item = NodeId> + 'a {
ChainIter::new(self.graggle, self.id)
}
}
pub struct OrderResolver<'a> {
graggle: Graggle<'a>,
ordered: Vec<NodeId>,
sccs: ojo_graph::Partition<LiveGraph<'a>>,
scc_reps: Vec<NodeId>,
seen: HashSet<usize>,
candidates: Vec<usize>,
remaining_in_edges: HashMap<usize, usize>,
}
impl<'a> OrderResolver<'a> {
pub fn ordered_nodes(&self) -> &[NodeId] {
&self.ordered[..]
}
pub fn candidates<'b>(&'b self) -> impl Iterator<Item = CandidateChain<'a>> + 'b {
self.candidates.iter().map(move |u| CandidateChain {
graggle: self.graggle,
id: self.scc_reps[*u],
})
}
fn advance_past(&mut self, scc: usize) {
let idx = self
.candidates
.iter()
.position(|x| *x == scc)
.expect("tried to remove a non-candidate");
self.candidates.remove(idx);
for u in self.sccs.out_neighbors(&scc) {
let remaining = self.remaining_in_edges.get_mut(&u).unwrap();
assert!(*remaining >= 1);
*remaining -= 1;
if *remaining == 0 {
self.candidates.insert(idx, u);
}
}
}
pub fn choose(&mut self, next: &NodeId) {
let next_idx = self.sccs.index_of(next);
assert!(self.candidates.contains(&next_idx));
self.ordered.push(*next);
self.seen.insert(next_idx);
self.advance_past(next_idx);
}
pub fn delete(&mut self, u: &NodeId) {
let u_idx = self.sccs.index_of(u);
assert!(self.candidates.contains(&u_idx));
self.advance_past(u_idx);
}
pub fn is_finished(&self) -> bool {
self.candidates.is_empty()
}
pub fn changes(&self) -> Changes {
let mut changes = vec![];
let not_deleted = self.ordered.iter().cloned().collect::<HashSet<_>>();
for u in self.graggle.nodes() {
if !not_deleted.contains(&u) {
changes.push(Change::DeleteNode { id: u });
}
}
for i in 1..self.ordered.len() {
let u = self.ordered[i - 1];
let v = self.ordered[i];
if !self.graggle.out_neighbors(&u).any(|w| *w == v) {
changes.push(Change::NewEdge { src: u, dest: v });
}
}
Changes { changes }
}
}
struct ChainIter<'a> {
next: Option<NodeId>,
graggle: Graggle<'a>,
}
impl<'a> ChainIter<'a> {
fn new(graggle: Graggle<'a>, u: NodeId) -> ChainIter<'a> {
ChainIter {
next: Some(u),
graggle,
}
}
}
impl<'a> Iterator for ChainIter<'a> {
type Item = NodeId;
fn next(&mut self) -> Option<NodeId> {
let ret = self.next;
if let Some(cur) = self.next {
self.next = None;
let mut neighbors = self.graggle.out_neighbors(&cur);
if let Some(next) = neighbors.next() {
let mut next_in = self.graggle.in_neighbors(&next);
if neighbors.next().is_none()
&& next_in.next().is_some()
&& next_in.next().is_none()
{
self.next = Some(*next);
}
}
}
ret
}
}
#[cfg(test)]
mod tests {
use itertools::Itertools;
use super::*;
#[test]
fn chain_iter() {
let graggle = graggle!(
live: 0, 1, 2, 3, 4, 5
edges: 0-1, 1-2, 2-3, 3-4, 4-5, 0-5, 2-5
);
let check = |init: u64, expected: Vec<u64>| {
let actual =
ChainIter::new(graggle.as_graggle(), NodeId::cur(init)).collect::<Vec<_>>();
let expected = expected
.into_iter()
.map(|x| NodeId::cur(x))
.collect::<Vec<_>>();
assert_eq!(actual, expected);
};
check(0, vec![0]);
check(1, vec![1, 2]);
check(2, vec![2]);
check(3, vec![3, 4]);
check(4, vec![4]);
check(5, vec![5]);
}
#[test]
fn resolver_diamond() {
let graggle = graggle!(
live: 0, 1, 2, 3
edges: 0-1, 0-2, 1-3, 2-3
);
let mut res = CycleResolver::new(graggle.as_graggle()).into_order_resolver();
println!("{:?}", res.candidates);
assert_eq!(res.candidates().count(), 1);
assert_eq!(
res.candidates().next().unwrap().iter().collect::<Vec<_>>(),
vec![NodeId::cur(0)]
);
res.choose(&NodeId::cur(0));
assert_eq!(res.candidates().count(), 2);
assert_eq!(
res.candidates()
.map(|x| x.iter())
.flatten()
.sorted()
.collect::<Vec<_>>(),
vec![NodeId::cur(1), NodeId::cur(2)]
);
res.choose(&NodeId::cur(1));
assert_eq!(res.candidates().count(), 1);
assert_eq!(
res.candidates().next().unwrap().iter().collect::<Vec<_>>(),
vec![NodeId::cur(2)]
);
res.choose(&NodeId::cur(2));
assert_eq!(res.candidates().count(), 1);
assert_eq!(
res.candidates().next().unwrap().iter().collect::<Vec<_>>(),
vec![NodeId::cur(3)]
);
res.choose(&NodeId::cur(3));
assert_eq!(res.candidates().count(), 0);
assert_eq!(
res.changes(),
Changes {
changes: vec![Change::NewEdge {
src: NodeId::cur(1),
dest: NodeId::cur(2)
}]
}
);
}
}