mod nd;
use crate::graph::Graph;
#[must_use]
#[allow(
clippy::cast_possible_truncation,
reason = "node ids and the node count are u32-bounded by the crate's id space"
)]
pub fn degree_order(graph: &Graph) -> Vec<u32> {
let node_count = graph.node_count();
let mut degree = vec![0u32; node_count];
for (v, window) in graph.first_out.windows(2).enumerate() {
let (from, to) = (window[0] as usize, window[1] as usize);
for &h in &graph.head[from..to] {
degree[v] += 1;
degree[h as usize] += 1;
}
}
let mut order: Vec<u32> = (0..node_count as u32).collect();
order.sort_unstable_by(|&a, &b| degree[a as usize].cmp(°ree[b as usize]).then(a.cmp(&b)));
order
}
#[must_use]
pub fn inertial_order(
node_count: u32,
tail: &[u32],
head: &[u32],
latitude: &[f32],
longitude: &[f32],
) -> Vec<u32> {
assert_eq!(
tail.len(),
head.len(),
"tail and head must have equal length"
);
assert_eq!(
latitude.len(),
node_count as usize,
"latitude length must equal node_count"
);
assert_eq!(
longitude.len(),
node_count as usize,
"longitude length must equal node_count"
);
nd::inertial::compute_nested_node_dissection_order_using_inertial_flow(
node_count, tail, head, latitude, longitude,
)
}
#[cfg(test)]
mod tests {
use super::*;
fn graph_from_arc_list(node_count: usize, tail: &[u32], head: &[u32]) -> Graph {
assert_eq!(tail.len(), head.len());
let arc_count = tail.len();
let mut first_out = vec![0u32; node_count + 1];
for &t in tail {
first_out[t as usize + 1] += 1;
}
for i in 1..=node_count {
first_out[i] += first_out[i - 1];
}
Graph {
first_out,
head: head.to_vec(),
weight: vec![1u32; arc_count],
}
}
#[test]
fn degree_order_empty_graph() {
let g = Graph {
first_out: vec![0],
head: vec![],
weight: vec![],
};
let order = degree_order(&g);
assert!(order.is_empty(), "empty graph should return empty order");
}
#[test]
fn degree_order_single_isolated_node() {
let g = Graph {
first_out: vec![0, 0],
head: vec![],
weight: vec![],
};
let order = degree_order(&g);
assert_eq!(order, vec![0u32]);
}
#[test]
fn degree_order_head_only_node() {
let tail = vec![1u32];
let head = vec![0u32];
let g = graph_from_arc_list(2, &tail, &head);
let order = degree_order(&g);
assert_eq!(order, vec![0u32, 1]);
}
#[test]
fn degree_order_is_permutation() {
let tail = vec![0u32, 0, 0, 1, 1, 2, 2, 3];
let head = vec![1u32, 1, 4, 2, 4, 3, 4, 4];
let g = graph_from_arc_list(5, &tail, &head);
let order = degree_order(&g);
let mut sorted = order.clone();
sorted.sort_unstable();
let expected: Vec<u32> = (0..5).collect();
assert_eq!(
sorted, expected,
"degree_order must be a permutation of 0..n"
);
}
#[test]
fn degree_order_matches_cpp_oracle() {
let tail = vec![0u32, 0, 0, 1, 1, 2, 2, 3];
let head = vec![1u32, 1, 4, 2, 4, 3, 4, 4];
let node_count: u32 = 5;
let g = graph_from_arc_list(node_count as usize, &tail, &head);
let rust_order = degree_order(&g);
let oracle_order = routingkit_cch::compute_order_degree(node_count, &tail, &head);
assert_eq!(
rust_order, oracle_order,
"degree_order must be bit-identical to the C++ oracle"
);
}
}