use core::fmt;
use core::hash::Hash;
use alloc::vec;
use alloc::vec::Vec;
use hashbrown::HashMap;
use crate::coord::{Idx, Tag};
use crate::full::MAX_CELLS;
use crate::grid::cost_ceiling;
use crate::path::{Cost, Path};
use crate::search;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GraphError {
TooManyNodes {
nodes: u64,
},
CostTooHigh {
cost: Cost,
ceiling: Cost,
nodes: usize,
},
}
impl fmt::Display for GraphError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Self::TooManyNodes { nodes } => write!(
f,
"the edges name {nodes} nodes; a graph may hold at most {MAX_CELLS}",
),
Self::CostTooHigh {
cost,
ceiling,
nodes,
} => write!(
f,
"an edge costs {cost}, but on a graph of {nodes} nodes no edge may cost more than \
{ceiling} without overflowing Cost ({})",
Cost::MAX,
),
}
}
}
#[derive(Debug, Clone)]
struct Rows {
start: Vec<usize>,
node: Vec<u32>,
cost: Vec<Cost>,
}
impl Rows {
fn of(nodes: usize, edges: impl Iterator<Item = (u32, u32, Cost)> + Clone) -> Self {
let mut start = vec![0usize; nodes + 1];
for (row, _, _) in edges.clone() {
start[row as usize + 1] += 1;
}
for k in 1..start.len() {
start[k] += start[k - 1];
}
let total = start[nodes];
let (mut node, mut cost) = (vec![0; total], vec![0; total]);
let mut at = start.clone();
for (row, other, c) in edges {
let put = at[row as usize];
node[put] = other;
cost[put] = c;
at[row as usize] += 1;
}
Self { start, node, cost }
}
fn row(&self, n: u32) -> impl Iterator<Item = (u32, Cost)> + '_ {
let (lo, hi) = (self.start[n as usize], self.start[n as usize + 1]);
(lo..hi).map(move |k| (self.node[k], self.cost[k]))
}
}
#[derive(Debug, Clone)]
pub struct Graph<K> {
keys: Vec<K>,
index: HashMap<K, u32>,
out: Rows,
back: Rows,
tag: Tag,
}
impl<K: Copy + Eq + Hash + fmt::Debug> Graph<K> {
#[must_use]
pub fn new(edges: impl IntoIterator<Item = (K, K, Cost)>) -> Self {
Self::try_new(edges).unwrap_or_else(|error| panic!("{error}"))
}
pub fn try_new(edges: impl IntoIterator<Item = (K, K, Cost)>) -> Result<Self, GraphError> {
let mut keys = Vec::new();
let mut index = HashMap::new();
let mut number = |key: K| -> Result<u32, GraphError> {
if let Some(&n) = index.get(&key) {
return Ok(n);
}
if keys.len() as u64 >= MAX_CELLS {
return Err(GraphError::TooManyNodes {
nodes: keys.len() as u64 + 1,
});
}
let n = keys.len() as u32;
index.insert(key, n);
keys.push(key);
Ok(n)
};
let mut numbered = Vec::new();
let mut dearest = 0;
for (from, to, cost) in edges {
let (from, to) = (number(from)?, number(to)?);
if from != to {
numbered.push((from, to, cost));
dearest = dearest.max(cost);
}
}
let ceiling = cost_ceiling(keys.len());
if dearest > ceiling {
return Err(GraphError::CostTooHigh {
cost: dearest,
ceiling,
nodes: keys.len(),
});
}
let out = Rows::of(keys.len(), numbered.iter().copied());
let back = Rows::of(
keys.len(),
numbered.iter().map(|&(from, to, cost)| (to, from, cost)),
);
Ok(Self {
tag: Tag::of(keys.iter()),
keys,
index,
out,
back,
})
}
const fn idx(&self, n: u32) -> Idx {
Idx::new(self.tag, n)
}
#[track_caller]
fn slot(&self, i: Idx) -> usize {
debug_assert!(
i.tag().agrees(self.tag),
"node {i} was issued by a different graph or grid than the one being asked \
(indices are per-graph, and this one may be in range for both). \
Look the node up again with `Graph::index_of` or `Graph::at` on the graph you mean.",
);
assert!(
(i.raw() as usize) < self.len(),
"node {i} is not in this graph, which has {} nodes",
self.len(),
);
i.raw() as usize
}
#[must_use]
pub fn len(&self) -> usize {
self.keys.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.keys.is_empty()
}
#[must_use]
#[track_caller]
pub fn at(&self, key: K) -> Idx {
self.index_of(key)
.unwrap_or_else(|| panic!("{key:?} is not a node of this graph"))
}
#[must_use]
pub fn index_of(&self, key: K) -> Option<Idx> {
self.index.get(&key).map(|&n| self.idx(n))
}
#[must_use]
pub fn key(&self, i: Idx) -> K {
self.keys[self.slot(i)]
}
pub fn indices(&self) -> impl Iterator<Item = Idx> + '_ {
(0..self.keys.len() as u32).map(|n| self.idx(n))
}
pub fn keys(&self) -> impl Iterator<Item = K> + '_ {
self.keys.iter().copied()
}
pub fn keys_of(&self, of: impl IntoIterator<Item = Idx>) -> impl Iterator<Item = K> {
of.into_iter().map(|i| self.key(i))
}
#[must_use]
pub fn path(&self, start: Idx, goal: Idx) -> Option<Path> {
let (from, to) = (self.slot(start) as u32, self.slot(goal) as u32);
let route = search::astar(self.len(), from, to, |n| self.out.row(n), |_| 0)?;
let steps = route.nodes.into_iter().map(|n| self.idx(n)).collect();
Some(Path::of(steps, route.cost))
}
#[must_use]
pub fn reachable(&self, start: Idx, budget: Cost) -> Vec<(Idx, Cost)> {
let from = self.slot(start) as u32;
let (found, _) = search::explore(self.len(), from, budget, |n| self.out.row(n));
self.minted(found)
}
#[must_use]
pub fn reaching(&self, goal: Idx, budget: Cost) -> Vec<(Idx, Cost)> {
let to = self.slot(goal) as u32;
let (found, _) = search::explore(self.len(), to, budget, |n| self.back.row(n));
self.minted(found)
}
fn minted(&self, found: Vec<(u32, Cost)>) -> Vec<(Idx, Cost)> {
found
.into_iter()
.map(|(n, cost)| (self.idx(n), cost))
.collect()
}
}