use crate::INF_WEIGHT;
use crate::bundle::INVALID_ID;
use crate::structure::Cch;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Metric {
pub forward: Vec<u32>,
pub backward: Vec<u32>,
}
impl Metric {
#[must_use]
pub fn view(&self) -> crate::bundle::MetricView<'_> {
crate::bundle::MetricView {
forward: &self.forward,
backward: &self.backward,
}
}
}
#[inline]
fn add(a: u32, b: u32) -> u32 {
a.wrapping_add(b)
}
#[inline]
fn min_to(x: &mut u32, y: u32) {
if y < *x {
*x = y;
}
}
impl Cch {
#[must_use]
pub fn customize(&self, weights: &[u32]) -> Metric {
assert_eq!(
weights.len(),
self.input_arc_to_cch_arc.len(),
"weights length must equal input arc count"
);
let arc_count = self.cch_arc_count();
let mut forward = vec![INF_WEIGHT; arc_count];
let mut backward = vec![INF_WEIGHT; arc_count];
for cch_arc in 0..arc_count {
let fwd_in = self.forward_input_arc_of_cch[cch_arc];
if fwd_in != INVALID_ID {
forward[cch_arc] = weights[fwd_in as usize];
}
let bwd_in = self.backward_input_arc_of_cch[cch_arc];
if bwd_in != INVALID_ID {
backward[cch_arc] = weights[bwd_in as usize];
}
let ef = &self.first_extra_forward_input_arc_of_cch;
for j in ef[cch_arc]..ef[cch_arc + 1] {
let ia = self.extra_forward_input_arc_of_cch[j as usize] as usize;
min_to(&mut forward[cch_arc], weights[ia]);
}
let eb = &self.first_extra_backward_input_arc_of_cch;
for j in eb[cch_arc]..eb[cch_arc + 1] {
let ia = self.extra_backward_input_arc_of_cch[j as usize] as usize;
min_to(&mut backward[cch_arc], weights[ia]);
}
}
let node_count = self.node_count();
let mut arc_id_cache = vec![0u32; node_count];
for x in 0..node_count {
let xz_up_end = self.up_first_out[x + 1];
for xz_up in self.up_first_out[x]..xz_up_end {
arc_id_cache[self.up_head[xz_up as usize] as usize] = xz_up;
}
let xy_down_end = self.down_first_out[x + 1];
for xy_down in self.down_first_out[x]..xy_down_end {
let bottom = self.down_to_up[xy_down as usize] as usize;
let y = self.down_head[xy_down as usize] as usize;
let y_up_begin = self.up_first_out[y];
let mut cursor = self.up_first_out[y + 1];
while cursor > y_up_begin {
cursor -= 1;
let mid = cursor as usize;
let z = self.up_head[mid] as usize;
if z <= x {
break;
}
let top = arc_id_cache[z] as usize;
let fwd_candidate = add(backward[bottom], forward[mid]);
let bwd_candidate = add(forward[bottom], backward[mid]);
min_to(&mut forward[top], fwd_candidate);
min_to(&mut backward[top], bwd_candidate);
}
}
}
Metric { forward, backward }
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::graph::Graph;
#[test]
fn metric_view_borrows_fields() {
let m = Metric {
forward: vec![1, 2, 3],
backward: vec![4, 5, 6],
};
let v = m.view();
assert_eq!(v.forward, &[1, 2, 3]);
assert_eq!(v.backward, &[4, 5, 6]);
}
fn csr(node_count: usize, tail: &[u32], head: &[u32]) -> Graph {
let mut counts = vec![0u32; node_count];
for &t in tail {
counts[t as usize] += 1;
}
let mut first_out = vec![0u32; node_count + 1];
for v in 0..node_count {
first_out[v + 1] = first_out[v] + counts[v];
}
let mut next: Vec<usize> = first_out[..node_count]
.iter()
.map(|&x| x as usize)
.collect();
let mut g_head = vec![0u32; head.len()];
for (&t, &h) in tail.iter().zip(head.iter()) {
g_head[next[t as usize]] = h;
next[t as usize] += 1;
}
Graph {
first_out,
head: g_head,
weight: vec![1u32; head.len()],
}
}
#[test]
fn single_arc() {
let g = csr(2, &[0], &[1]);
let order = vec![0u32, 1];
let c = Cch::build(&g, &order);
let m = c.customize(&[42]);
assert_eq!(m.forward, vec![42]);
assert_eq!(m.backward, vec![INF_WEIGHT]);
}
#[test]
fn bidirectional_arc() {
let g = csr(2, &[0, 1], &[1, 0]);
let order = vec![0u32, 1];
let c = Cch::build(&g, &order);
let m = c.customize(&[7, 9]);
assert_eq!(m.forward, vec![7]);
assert_eq!(m.backward, vec![9]);
}
#[test]
fn parallel_arc_min() {
let g = csr(2, &[0, 0, 1, 1], &[1, 1, 0, 0]);
let order = vec![0u32, 1];
let c = Cch::build(&g, &order);
let m = c.customize(&[50, 9, 40, 8]);
assert_eq!(m.forward, vec![9]);
assert_eq!(m.backward, vec![8]);
}
#[test]
fn all_inf() {
let g = csr(3, &[0, 0, 1, 2], &[1, 2, 0, 0]);
let order = vec![0u32, 1, 2];
let c = Cch::build(&g, &order);
let inf = INF_WEIGHT;
let m = c.customize(&[inf, inf, inf, inf]);
assert!(m.forward.iter().all(|&w| w == inf));
assert!(m.backward.iter().all(|&w| w == inf));
}
#[test]
fn triangle_relaxation() {
let g = csr(3, &[0, 0, 1, 2], &[1, 2, 0, 0]);
let order = vec![0u32, 1, 2];
let c = Cch::build(&g, &order);
let m = c.customize(&[3, 5, 4, 6]);
assert_eq!(m.forward[2], 9);
assert_eq!(m.backward[2], 9);
}
#[test]
fn add_inf_helper() {
assert_eq!(add(1, 2), 3);
let s = add(INF_WEIGHT, INF_WEIGHT);
assert!(s > INF_WEIGHT);
}
#[test]
#[should_panic(expected = "weights length must equal input arc count")]
fn wrong_weight_len_panics() {
let g = csr(2, &[0], &[1]);
let order = vec![0u32, 1];
let c = Cch::build(&g, &order);
let _ = c.customize(&[1, 2, 3]);
}
}