use std::cmp::Ordering;
use std::hash::Hash;
use std::vec::IntoIter;
use hashbrown::{hash_map::Entry, HashMap};
use petgraph::{
visit::{
EdgeCount, EdgeRef, GraphBase, GraphProp, IntoEdges, IntoNodeIdentifiers, NodeCount,
Visitable,
},
Undirected,
};
use crate::traversal::{depth_first_search, DfsEvent};
type Edge<G> = (<G as GraphBase>::NodeId, <G as GraphBase>::NodeId);
fn modify_if_min<K, V>(xs: &mut HashMap<K, V>, key: K, val: V)
where
K: Hash + Eq,
V: Ord + Copy,
{
xs.entry(key).and_modify(|e| {
if val < *e {
*e = val;
}
});
}
fn edges_filtered_and_sorted_by<G, P, F, K>(
graph: G,
a: G::NodeId,
filter: P,
compare: F,
) -> IntoIter<Edge<G>>
where
G: IntoEdges,
P: Fn(&Edge<G>) -> bool,
F: Fn(&Edge<G>) -> K,
K: Ord,
{
let mut edges = graph
.edges(a)
.filter_map(|edge| {
let e = (edge.source(), edge.target());
filter(&e).then_some(e)
})
.collect::<Vec<_>>();
edges.sort_by_key(compare);
edges.dedup();
edges.into_iter()
}
fn is_target<G: GraphBase>(edge: Option<&Edge<G>>, v: G::NodeId) -> Option<&Edge<G>> {
edge.filter(|e| e.1 == v)
}
#[derive(Clone, Copy, PartialEq, PartialOrd)]
struct Interval<T> {
inner: Option<(T, T)>,
}
impl<T> Default for Interval<T> {
fn default() -> Self {
Interval { inner: None }
}
}
impl<T> Interval<T> {
fn new(low: T, high: T) -> Self {
Interval {
inner: Some((low, high)),
}
}
fn is_empty(&self) -> bool {
self.inner.is_none()
}
fn unwrap(self) -> (T, T) {
self.inner.unwrap()
}
fn low(&self) -> Option<&T> {
match self.inner {
Some((ref low, _)) => Some(low),
None => None,
}
}
fn high(&self) -> Option<&T> {
match self.inner {
Some((_, ref high)) => Some(high),
None => None,
}
}
fn as_ref(&mut self) -> Option<&(T, T)> {
self.inner.as_ref()
}
fn as_mut(&mut self) -> Option<&mut (T, T)> {
self.inner.as_mut()
}
fn as_mut_low(&mut self) -> Option<&mut T> {
match self.inner {
Some((ref mut low, _)) => Some(low),
None => None,
}
}
}
impl<T> Interval<(T, T)>
where
T: Copy + Hash + Eq,
{
fn conflict<G>(&self, lr_state: &LRState<G>, edge: Edge<G>) -> bool
where
G: GraphBase<NodeId = T>,
{
match self.inner {
Some((_, ref h)) => lr_state.lowpt.get(h) > lr_state.lowpt.get(&edge),
_ => false,
}
}
}
#[derive(Clone, Copy, PartialEq, PartialOrd)]
struct ConflictPair<T> {
left: Interval<T>,
right: Interval<T>,
}
impl<T> Default for ConflictPair<T> {
fn default() -> Self {
ConflictPair {
left: Interval::default(),
right: Interval::default(),
}
}
}
impl<T> ConflictPair<T> {
fn new(left: Interval<T>, right: Interval<T>) -> Self {
ConflictPair { left, right }
}
fn swap(&mut self) {
std::mem::swap(&mut self.left, &mut self.right)
}
fn is_empty(&self) -> bool {
self.left.is_empty() && self.right.is_empty()
}
}
impl<T> ConflictPair<(T, T)>
where
T: Copy + Hash + Eq,
{
fn lowest<G>(&self, lr_state: &LRState<G>) -> usize
where
G: GraphBase<NodeId = T>,
{
match (self.left.low(), self.right.low()) {
(Some(l_low), Some(r_low)) => lr_state.lowpt[l_low].min(lr_state.lowpt[r_low]),
(Some(l_low), None) => lr_state.lowpt[l_low],
(None, Some(r_low)) => lr_state.lowpt[r_low],
(None, None) => usize::MAX,
}
}
}
enum Sign {
Plus,
Minus,
}
enum LRTestDfsEvent<N> {
Finish(N),
TreeEdge(N, N),
BackEdge(N, N),
FinishEdge(N, N),
}
struct NonPlanar {}
struct LRState<G: GraphBase>
where
G::NodeId: Hash + Eq,
{
graph: G,
roots: Vec<G::NodeId>,
height: HashMap<G::NodeId, usize>,
eparent: HashMap<G::NodeId, Edge<G>>,
lowpt: HashMap<Edge<G>, usize>,
lowpt_2: HashMap<Edge<G>, usize>,
lowpt_edge: HashMap<Edge<G>, Edge<G>>,
nesting_depth: HashMap<Edge<G>, usize>,
stack: Vec<ConflictPair<Edge<G>>>,
stack_emarker: HashMap<Edge<G>, ConflictPair<Edge<G>>>,
eref: HashMap<Edge<G>, Edge<G>>,
side: HashMap<Edge<G>, Sign>,
}
impl<G> LRState<G>
where
G: GraphBase + NodeCount + EdgeCount + IntoEdges + Visitable,
G::NodeId: Hash + Eq,
{
fn new(graph: G) -> Self {
let num_nodes = graph.node_count();
let num_edges = graph.edge_count();
LRState {
graph,
roots: Vec::new(),
height: HashMap::with_capacity(num_nodes),
eparent: HashMap::with_capacity(num_edges),
lowpt: HashMap::with_capacity(num_edges),
lowpt_2: HashMap::with_capacity(num_edges),
lowpt_edge: HashMap::with_capacity(num_edges),
nesting_depth: HashMap::with_capacity(num_edges),
stack: Vec::new(),
stack_emarker: HashMap::with_capacity(num_edges),
eref: HashMap::with_capacity(num_edges),
side: graph
.edge_references()
.map(|e| ((e.source(), e.target()), Sign::Plus))
.collect(),
}
}
fn lr_orientation_visitor(&mut self, event: DfsEvent<G::NodeId, &G::EdgeWeight>) {
match event {
DfsEvent::Discover(v, _) => {
if let Entry::Vacant(entry) = self.height.entry(v) {
entry.insert(0);
self.roots.push(v);
}
}
DfsEvent::TreeEdge(v, w, _) => {
let ei = (v, w);
let v_height = self.height[&v];
let w_height = v_height + 1;
self.eparent.insert(w, ei);
self.height.insert(w, w_height);
self.lowpt.insert(ei, v_height);
self.lowpt_2.insert(ei, w_height);
}
DfsEvent::BackEdge(v, w, _) => {
if Some(&(w, v)) != self.eparent.get(&v) {
let ei = (v, w);
self.lowpt.insert(ei, self.height[&w]);
self.lowpt_2.insert(ei, self.height[&v]);
}
}
DfsEvent::Finish(v, _) => {
for edge in self.graph.edges(v) {
let w = edge.target();
let ei = (v, w);
let low = match self.lowpt.get(&ei) {
Some(val) => *val,
None =>
{
continue
}
};
if self.lowpt_2[&ei] < self.height[&v] {
self.nesting_depth.insert(ei, 2 * low + 1);
} else {
self.nesting_depth.insert(ei, 2 * low);
}
if let Some(e_par) = self.eparent.get(&v) {
match self.lowpt[&ei].cmp(&self.lowpt[e_par]) {
Ordering::Less => {
self.lowpt_2
.insert(*e_par, self.lowpt[e_par].min(self.lowpt_2[&ei]));
self.lowpt.insert(*e_par, self.lowpt[&ei]);
}
Ordering::Greater => {
modify_if_min(&mut self.lowpt_2, *e_par, self.lowpt[&ei]);
}
_ => {
let val = self.lowpt_2[&ei];
modify_if_min(&mut self.lowpt_2, *e_par, val);
}
}
}
}
}
_ => (),
}
}
fn lr_testing_visitor(&mut self, event: LRTestDfsEvent<G::NodeId>) -> Result<(), NonPlanar> {
match event {
LRTestDfsEvent::TreeEdge(v, w) => {
let ei = (v, w);
if let Some(&last) = self.stack.last() {
self.stack_emarker.insert(ei, last);
}
}
LRTestDfsEvent::BackEdge(v, w) => {
let ei = (v, w);
if let Some(&last) = self.stack.last() {
self.stack_emarker.insert(ei, last);
}
self.lowpt_edge.insert(ei, ei);
let c_pair = ConflictPair::new(Interval::default(), Interval::new(ei, ei));
self.stack.push(c_pair);
}
LRTestDfsEvent::FinishEdge(v, w) => {
let ei = (v, w);
if self.lowpt[&ei] < self.height[&v] {
let e_par = self.eparent[&v];
let val = self.lowpt_edge[&ei];
match self.lowpt_edge.entry(e_par) {
Entry::Occupied(_) => {
self.add_constraints(ei, e_par)?;
}
Entry::Vacant(o) => {
o.insert(val);
}
}
}
}
LRTestDfsEvent::Finish(v) => {
if let Some(&e) = self.eparent.get(&v) {
let u = e.0;
self.remove_back_edges(u);
if self.lowpt[&e] < self.height[&u] {
if let Some(top) = self.stack.last() {
let e_high = match (top.left.high(), top.right.high()) {
(Some(hl), Some(hr)) => {
if self.lowpt[hl] > self.lowpt[hr] {
hl
} else {
hr
}
}
(Some(hl), None) => hl,
(None, Some(hr)) => hr,
_ => {
unreachable!()
}
};
self.eref.insert(e, *e_high);
}
}
}
}
}
Ok(())
}
fn until_top_of_stack_hits_emarker(&mut self, ei: Edge<G>) -> Option<ConflictPair<Edge<G>>> {
if let Some(&c_pair) = self.stack.last() {
if self.stack_emarker[&ei] != c_pair {
return self.stack.pop();
}
}
None
}
fn until_top_of_stack_is_conflicting(&mut self, ei: Edge<G>) -> Option<ConflictPair<Edge<G>>> {
if let Some(c_pair) = self.stack.last() {
if c_pair.left.conflict(self, ei) || c_pair.right.conflict(self, ei) {
return self.stack.pop();
}
}
None
}
fn union_intervals(&mut self, pi: &mut Interval<Edge<G>>, qi: Interval<Edge<G>>) {
match pi.as_mut_low() {
Some(p_low) => {
let (q_low, q_high) = qi.unwrap();
self.eref.insert(*p_low, q_high);
*p_low = q_low;
}
None => {
*pi = qi;
}
}
}
fn add_constraints(&mut self, ei: Edge<G>, e: Edge<G>) -> Result<(), NonPlanar> {
let mut c_pair = ConflictPair::<Edge<G>>::default();
while let Some(mut q_pair) = self.until_top_of_stack_hits_emarker(ei) {
if !q_pair.left.is_empty() {
q_pair.swap();
if !q_pair.left.is_empty() {
return Err(NonPlanar {});
}
}
let qr_low = q_pair.right.low().unwrap();
if self.lowpt[qr_low] > self.lowpt[&e] {
self.union_intervals(&mut c_pair.right, q_pair.right);
} else {
self.eref.insert(*qr_low, self.lowpt_edge[&e]);
}
}
while let Some(mut q_pair) = self.until_top_of_stack_is_conflicting(ei) {
if q_pair.right.conflict(self, ei) {
q_pair.swap();
if q_pair.right.conflict(self, ei) {
return Err(NonPlanar {});
}
}
if let Some((qr_low, qr_high)) = q_pair.right.as_ref() {
if let Some(pr_low) = c_pair.right.as_mut_low() {
self.eref.insert(*pr_low, *qr_high);
*pr_low = *qr_low;
}
};
self.union_intervals(&mut c_pair.left, q_pair.left);
}
if !c_pair.is_empty() {
self.stack.push(c_pair);
}
Ok(())
}
fn until_lowest_top_of_stack_has_height(
&mut self,
v: G::NodeId,
) -> Option<ConflictPair<Edge<G>>> {
if let Some(c_pair) = self.stack.last() {
if c_pair.lowest(self) == self.height[&v] {
return self.stack.pop();
}
}
None
}
fn follow_eref_until_is_target(&self, edge: Edge<G>, v: G::NodeId) -> Option<Edge<G>> {
let mut res = Some(&edge);
while let Some(b) = is_target::<G>(res, v) {
res = self.eref.get(b);
}
res.copied()
}
fn remove_back_edges(&mut self, v: G::NodeId) {
while let Some(c_pair) = self.until_lowest_top_of_stack_has_height(v) {
if let Some(pl_low) = c_pair.left.low() {
self.side.insert(*pl_low, Sign::Minus);
}
}
if let Some(mut c_pair) = self.stack.pop() {
if let Some((pl_low, pl_high)) = c_pair.left.as_mut() {
match self.follow_eref_until_is_target(*pl_high, v) {
Some(val) => {
*pl_high = val;
}
None => {
let pr_low = c_pair.right.low().unwrap();
self.eref.insert(*pl_low, *pr_low);
self.side.insert(*pl_low, Sign::Minus);
c_pair.left = Interval::default();
}
}
}
if let Some((pr_low, ref mut pr_high)) = c_pair.right.as_mut() {
match self.follow_eref_until_is_target(*pr_high, v) {
Some(val) => {
*pr_high = val;
}
None => {
let pl_low = c_pair.left.low().unwrap();
self.eref.insert(*pr_low, *pl_low);
self.side.insert(*pr_low, Sign::Minus);
c_pair.right = Interval::default();
}
};
}
if !c_pair.is_empty() {
self.stack.push(c_pair);
}
}
}
}
fn lr_visit_ordered_dfs_tree<G, F, E>(
lr_state: &mut LRState<G>,
v: G::NodeId,
mut visitor: F,
) -> Result<(), E>
where
G: GraphBase + IntoEdges,
G::NodeId: Hash + Eq,
F: FnMut(&mut LRState<G>, LRTestDfsEvent<G::NodeId>) -> Result<(), E>,
{
let mut stack: Vec<(G::NodeId, IntoIter<Edge<G>>)> = vec![(
v,
edges_filtered_and_sorted_by(
lr_state.graph,
v,
|e| lr_state.lowpt.contains_key(e),
|e| lr_state.nesting_depth[e],
),
)];
while let Some(elem) = stack.last_mut() {
let v = elem.0;
let adjacent_edges = &mut elem.1;
let mut next = None;
for (v, w) in adjacent_edges {
if Some(&(v, w)) == lr_state.eparent.get(&w) {
visitor(lr_state, LRTestDfsEvent::TreeEdge(v, w))?;
next = Some(w);
break;
} else {
visitor(lr_state, LRTestDfsEvent::BackEdge(v, w))?;
visitor(lr_state, LRTestDfsEvent::FinishEdge(v, w))?;
}
}
match next {
Some(w) => stack.push((
w,
edges_filtered_and_sorted_by(
lr_state.graph,
w,
|e| lr_state.lowpt.contains_key(e),
|e| lr_state.nesting_depth[e],
),
)),
None => {
stack.pop();
visitor(lr_state, LRTestDfsEvent::Finish(v))?;
if let Some(&(u, v)) = lr_state.eparent.get(&v) {
visitor(lr_state, LRTestDfsEvent::FinishEdge(u, v))?;
}
}
}
}
Ok(())
}
pub fn is_planar<G>(graph: G) -> bool
where
G: GraphProp<EdgeType = Undirected>
+ NodeCount
+ EdgeCount
+ IntoEdges
+ IntoNodeIdentifiers
+ Visitable,
G::NodeId: Hash + Eq,
{
let mut state = LRState::new(graph);
depth_first_search(graph, graph.node_identifiers(), |event| {
state.lr_orientation_visitor(event)
});
for v in state.roots.clone() {
let res = lr_visit_ordered_dfs_tree(&mut state, v, |state, event| {
state.lr_testing_visitor(event)
});
if res.is_err() {
return false;
}
}
true
}