use alloc::{collections::BTreeMap, vec, vec::Vec};
use core::{fmt, ops::RangeBounds};
use crate::{
algo::{toposort, Cycle},
visit::{GraphBase, IntoNeighborsDirected, IntoNodeIdentifiers, NodeIndexable, Visitable},
};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
#[repr(transparent)]
pub struct TopologicalPosition(pub(super) usize);
#[derive(Clone)]
pub(super) struct OrderMap<N> {
pos_to_node: BTreeMap<TopologicalPosition, N>,
node_to_pos: Vec<TopologicalPosition>,
}
impl<N> Default for OrderMap<N> {
fn default() -> Self {
Self {
pos_to_node: Default::default(),
node_to_pos: Default::default(),
}
}
}
impl<N: fmt::Debug> fmt::Debug for OrderMap<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OrderMap")
.field("order", &self.pos_to_node)
.finish()
}
}
impl<N: Copy> OrderMap<N> {
pub(super) fn try_from_graph<G>(graph: G) -> Result<Self, Cycle<G::NodeId>>
where
G: NodeIndexable<NodeId = N> + IntoNeighborsDirected + IntoNodeIdentifiers + Visitable,
{
let topo_vec = toposort(graph, None)?;
let mut pos_to_node = BTreeMap::new();
let mut node_to_pos = vec![TopologicalPosition::default(); graph.node_bound()];
for (i, &id) in topo_vec.iter().enumerate() {
let pos = TopologicalPosition(i);
pos_to_node.insert(pos, id);
node_to_pos[graph.to_index(id)] = pos;
}
Ok(Self {
pos_to_node,
node_to_pos,
})
}
pub(super) fn with_capacity(nodes: usize) -> Self {
Self {
pos_to_node: BTreeMap::new(),
node_to_pos: Vec::with_capacity(nodes),
}
}
#[track_caller]
pub(super) fn get_position(
&self,
id: N,
graph: impl NodeIndexable<NodeId = N>,
) -> TopologicalPosition {
let idx = graph.to_index(id);
assert!(idx < self.node_to_pos.len());
self.node_to_pos[idx]
}
pub(super) fn at_position(&self, pos: TopologicalPosition) -> Option<N> {
self.pos_to_node.get(&pos).copied()
}
pub(super) fn nodes_iter(&self) -> impl Iterator<Item = N> + '_ {
self.pos_to_node.values().copied()
}
pub(super) fn range(
&self,
range: impl RangeBounds<TopologicalPosition>,
) -> impl Iterator<Item = N> + '_ {
self.pos_to_node.range(range).map(|(_, &n)| n)
}
pub(super) fn add_node(
&mut self,
id: N,
graph: impl NodeIndexable<NodeId = N>,
) -> TopologicalPosition {
let new_pos = self
.pos_to_node
.iter()
.next_back()
.map(|(TopologicalPosition(idx), _)| TopologicalPosition(idx + 1))
.unwrap_or_default();
let idx = graph.to_index(id);
if idx >= self.node_to_pos.len() {
self.node_to_pos
.resize(graph.node_bound(), TopologicalPosition::default());
}
self.pos_to_node.insert(new_pos, id);
self.node_to_pos[idx] = new_pos;
new_pos
}
#[track_caller]
pub(super) fn remove_node(&mut self, id: N, graph: impl NodeIndexable<NodeId = N>) {
let idx = graph.to_index(id);
assert!(idx < self.node_to_pos.len());
let pos = self.node_to_pos[idx];
self.node_to_pos[idx] = TopologicalPosition::default();
self.pos_to_node.remove(&pos);
}
#[track_caller]
pub(super) fn set_position(
&mut self,
id: N,
pos: TopologicalPosition,
graph: impl NodeIndexable<NodeId = N>,
) {
let idx = graph.to_index(id);
assert!(idx < self.node_to_pos.len());
self.pos_to_node.insert(pos, id);
self.node_to_pos[idx] = pos;
}
}
impl<G: Visitable> super::Acyclic<G> {
#[track_caller]
pub fn get_position<'a>(&'a self, id: G::NodeId) -> TopologicalPosition
where
&'a G: NodeIndexable + GraphBase<NodeId = G::NodeId>,
{
self.order_map.get_position(id, &self.graph)
}
pub fn at_position(&self, pos: TopologicalPosition) -> Option<G::NodeId> {
self.order_map.at_position(pos)
}
}