use crate::transition::*;
use crate::EndAttachError::{EndsAlreadyAttached, LayerMissing};
use crate::definition::{Layer, Layers};
use core::fmt::Debug;
use pathfinding::num_traits::{ConstZero, Zero};
use petgraph::algo::astar;
use petgraph::graph::EdgeReference;
use petgraph::prelude::EdgeRef;
use petgraph::{Direction, Graph};
use routers_network::Entry;
use scc::HashMap;
pub type OpenCandidateGraph = Graph<CandidateRef, CandidateEdge>;
pub type LockedCandidateGraph = OpenCandidateGraph;
pub struct Candidates<E>
where
E: Entry,
{
pub(crate) graph: LockedCandidateGraph,
pub(crate) lookup: HashMap<CandidateId, Candidate<E>>,
ends: Option<(CandidateId, CandidateId)>,
}
impl<E> Debug for Candidates<E>
where
E: Entry,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
let entries = self.lookup.len();
write!(
f,
"Candidates {{ graph: <locked>, lookup: \"{entries} Entries\" }}"
)
}
}
impl<E> Candidates<E>
where
E: Entry,
{
pub fn new(graph: OpenCandidateGraph, lookup: HashMap<CandidateId, Candidate<E>>) -> Self {
Self {
graph,
lookup,
..Self::default()
}
}
pub fn next_layer(&self, candidate: &CandidateId) -> Vec<CandidateId> {
self.graph
.edges_directed(*candidate, Direction::Outgoing)
.map(|edge| edge.target())
.collect()
}
pub fn attach_ends(
&mut self,
layers: &Layers,
) -> Result<(CandidateId, CandidateId), EndAttachError> {
if self.ends.is_some() {
return Err(EndsAlreadyAttached);
}
let graph = &mut self.graph;
let source = graph.add_node(CandidateRef::butt());
let target = graph.add_node(CandidateRef::butt());
layers
.first()
.ok_or(LayerMissing)?
.nodes
.iter()
.for_each(|node| {
graph.add_edge(source, *node, CandidateEdge::zero());
});
layers
.last()
.ok_or(LayerMissing)?
.nodes
.iter()
.for_each(|node| {
graph.add_edge(*node, target, CandidateEdge::zero());
});
let ends = (source, target);
self.ends = Some(ends);
Ok(ends)
}
pub fn collapse(self) -> Result<CollapsedPath<E>, CollapseError> {
let (source, target) = self.ends.ok_or(CollapseError::NoEnds)?;
let graph = &self.graph;
let cost_fn = |e: EdgeReference<CandidateEdge>| {
let transition_cost = e.weight().weight;
let emission_cost = graph
.node_weight(e.target())
.map_or(u32::MAX, |v| v.weight());
transition_cost + emission_cost
};
let zero = |_| u32::ZERO;
let (cost, route) = astar(graph, source, |node| node == target, cost_fn, zero)
.ok_or(CollapseError::NoPathFound)?;
Ok(CollapsedPath::new(cost, vec![], route, self))
}
pub fn edge(&self, a: &CandidateId, b: &CandidateId) -> Option<CandidateEdge> {
let edge_index = self.graph.find_edge(*a, *b)?;
self.graph.edge_weight(edge_index).cloned()
}
pub fn attach(&mut self, candidate: CandidateId, layer: &Layer) {
for node in &layer.nodes {
self.graph.add_edge(candidate, *node, CandidateEdge::zero());
}
}
pub fn weave(&mut self, layers: &Layers) {
layers.layers.windows(2).for_each(|layers| {
if let [a, b] = layers {
a.nodes.iter().for_each(|node| self.attach(*node, b))
}
});
}
pub fn candidate(&self, a: &CandidateId) -> Option<Candidate<E>> {
self.lookup.get(a).map(|c| *c)
}
}
impl<E> Default for Candidates<E>
where
E: Entry,
{
fn default() -> Self {
let graph = Graph::new();
let lookup = HashMap::default();
Candidates {
graph,
lookup,
ends: None,
}
}
}