#![doc = include_str!("../README.md")]
#[cxx::bridge]
pub mod ffi {
extern "C++" {
include!("routingkit_cch_wrapper.h");
type CCH; type CCHMetric; type CCHQuery; type CCHPartial;
unsafe fn cch_new(
order: &[u32],
tail: &[u32],
head: &[u32],
log_message: fn(&str),
filter_always_inf_arcs: bool,
) -> UniquePtr<CCH>;
unsafe fn cch_metric_new(cch: &CCH, weights: &[u32]) -> UniquePtr<CCHMetric>;
unsafe fn cch_metric_customize(metric: Pin<&mut CCHMetric>);
unsafe fn cch_metric_reset(metric: Pin<&mut CCHMetric>, weights: &[u32]);
unsafe fn cch_metric_parallel_customize(metric: Pin<&mut CCHMetric>, thread_count: u32);
unsafe fn cch_partial_new(cch: &CCH) -> UniquePtr<CCHPartial>;
unsafe fn cch_partial_reset(partial: Pin<&mut CCHPartial>);
unsafe fn cch_partial_update_arc(partial: Pin<&mut CCHPartial>, arc: u32);
unsafe fn cch_partial_customize(partial: Pin<&mut CCHPartial>, metric: Pin<&mut CCHMetric>);
unsafe fn cch_query_new(metric: &CCHMetric) -> UniquePtr<CCHQuery>;
unsafe fn cch_query_reset(query: Pin<&mut CCHQuery>, metric: &CCHMetric);
unsafe fn cch_query_add_source(query: Pin<&mut CCHQuery>, s: u32, dist: u32);
unsafe fn cch_query_add_target(query: Pin<&mut CCHQuery>, t: u32, dist: u32);
unsafe fn cch_query_run(query: Pin<&mut CCHQuery>);
unsafe fn cch_query_distance(query: &CCHQuery) -> u32;
unsafe fn cch_query_node_path(query: &CCHQuery) -> Vec<u32>;
unsafe fn cch_query_arc_path(query: &CCHQuery) -> Vec<u32>;
unsafe fn cch_query_pin_targets(query: Pin<&mut CCHQuery>, targets: &[u32]);
unsafe fn cch_query_run_to_pinned_targets(query: Pin<&mut CCHQuery>);
unsafe fn cch_query_distances_to_targets(query: &CCHQuery) -> Vec<u32>;
unsafe fn cch_query_reset_source(query: Pin<&mut CCHQuery>);
unsafe fn cch_query_pin_sources(query: Pin<&mut CCHQuery>, sources: &[u32]);
unsafe fn cch_query_run_to_pinned_sources(query: Pin<&mut CCHQuery>);
unsafe fn cch_query_distances_to_sources(query: &CCHQuery) -> Vec<u32>;
unsafe fn cch_query_reset_target(query: Pin<&mut CCHQuery>);
unsafe fn cch_compute_order_inertial(
node_count: u32,
tail: &[u32],
head: &[u32],
latitude: &[f32],
longitude: &[f32],
) -> Vec<u32>;
unsafe fn cch_compute_order_degree(node_count: u32, tail: &[u32], head: &[u32])
-> Vec<u32>;
}
}
unsafe impl Send for ffi::CCH {}
unsafe impl Sync for ffi::CCH {}
unsafe impl Send for ffi::CCHMetric {}
unsafe impl Sync for ffi::CCHMetric {}
unsafe impl Send for ffi::CCHQuery {}
use cxx::UniquePtr;
use ffi::*;
pub use ffi::{
cch_compute_order_degree as compute_order_degree_unchecked,
cch_compute_order_inertial as compute_order_inertial_unchecked,
};
pub const INF_WEIGHT: u32 = 2_147_483_647;
fn is_permutation(arr: &[u32]) -> bool {
let n = arr.len();
let mut seen = vec![false; n];
for &val in arr {
if (val as usize) >= n || seen[val as usize] {
return false;
}
seen[val as usize] = true;
}
true
}
pub fn compute_order_degree(node_count: u32, tail: &[u32], head: &[u32]) -> Vec<u32> {
assert!(
tail.iter()
.chain(head)
.max()
.map_or(true, |&v| v < node_count),
"tail/head contain node ids outside valid range"
);
assert!(
tail.len() == head.len(),
"tail and head arrays must have the same length"
);
unsafe { cch_compute_order_degree(node_count, tail, head) }
}
pub fn compute_order_inertial(
node_count: u32,
tail: &[u32],
head: &[u32],
latitude: &[f32],
longitude: &[f32],
) -> Vec<u32> {
assert!(
tail.iter()
.chain(head)
.max()
.map_or(true, |&v| v < node_count),
"tail/head contain node ids outside valid range"
);
assert!(
tail.len() == head.len(),
"tail and head arrays must have the same length"
);
assert!(
latitude.len() == (node_count as usize) && longitude.len() == (node_count as usize),
"latitude/longitude length must equal node count"
);
unsafe { cch_compute_order_inertial(node_count, tail, head, latitude, longitude) }
}
pub struct CCH {
inner: UniquePtr<ffi::CCH>,
edge_count: usize,
node_count: usize,
}
impl CCH {
pub fn new(
order: &[u32],
tail: &[u32],
head: &[u32],
log_message: fn(&str),
filter_always_inf_arcs: bool,
) -> Self {
assert!(
is_permutation(order),
"order array is not a valid permutation"
);
assert!(
tail.len() == head.len(),
"tail and head arrays must have the same length"
);
assert!(
tail.iter()
.chain(head)
.max()
.map_or(true, |&v| (v as usize) < order.len()),
"tail/head contain node ids outside valid range"
);
unsafe { Self::new_unchecked(order, tail, head, log_message, filter_always_inf_arcs) }
}
pub unsafe fn new_unchecked(
order: &[u32],
tail: &[u32],
head: &[u32],
log_message: fn(&str),
filter_always_inf_arcs: bool,
) -> Self {
let cch = unsafe { cch_new(order, tail, head, log_message, filter_always_inf_arcs) };
CCH {
inner: cch,
edge_count: tail.len(),
node_count: order.len(),
}
}
}
pub struct CCHMetric<'a> {
inner: UniquePtr<ffi::CCHMetric>,
weights: Box<[u32]>, cch: &'a CCH,
}
impl<'a> CCHMetric<'a> {
pub fn new(cch: &'a CCH, weights: Vec<u32>) -> Self {
assert!(
weights.len() == cch.edge_count,
"weights length must equal arc count",
);
let boxed: Box<[u32]> = weights.into_boxed_slice();
let metric = unsafe {
let mut metric = cch_metric_new(&cch.inner, &boxed);
cch_metric_customize(metric.as_mut().unwrap());
metric
};
CCHMetric {
inner: metric,
weights: boxed,
cch,
}
}
#[cfg(feature = "openmp")]
pub fn parallel_new(cch: &'a CCH, weights: Vec<u32>, thread_count: u32) -> Self {
assert!(
weights.len() == cch.edge_count,
"weights length must equal arc count",
);
let boxed: Box<[u32]> = weights.into_boxed_slice();
let metric = unsafe {
let mut metric = cch_metric_new(&cch.inner, &boxed);
cch_metric_parallel_customize(metric.as_mut().unwrap(), thread_count);
metric
};
CCHMetric {
inner: metric,
weights: boxed,
cch,
}
}
pub fn weights(&self) -> &[u32] {
&self.weights
}
pub fn reset(&mut self, weights: Vec<u32>) {
assert!(
weights.len() == self.cch.edge_count,
"weights length must equal arc count",
);
self.weights = weights.into_boxed_slice();
unsafe {
cch_metric_reset(self.inner.as_mut().unwrap(), &self.weights);
cch_metric_customize(self.inner.as_mut().unwrap());
}
}
#[cfg(feature = "openmp")]
pub fn parallel_reset(&mut self, weights: Vec<u32>, thread_count: u32) {
assert!(
weights.len() == self.cch.edge_count,
"weights length must equal arc count",
);
self.weights = weights.into_boxed_slice();
unsafe {
cch_metric_reset(self.inner.as_mut().unwrap(), &self.weights);
cch_metric_parallel_customize(self.inner.as_mut().unwrap(), thread_count);
}
}
}
pub struct CCHMetricPartialUpdater<'a> {
partial: UniquePtr<ffi::CCHPartial>,
cch: &'a CCH,
}
impl<'a> CCHMetricPartialUpdater<'a> {
pub fn new(cch: &'a CCH) -> Self {
let partial = unsafe { cch_partial_new(cch.inner.as_ref().unwrap()) };
CCHMetricPartialUpdater { partial, cch }
}
pub fn apply<T>(&mut self, metric: &mut CCHMetric<'a>, updates: &T)
where
T: for<'b> std::ops::Index<&'b u32, Output = u32>,
for<'b> &'b T: IntoIterator<Item = (&'b u32, &'b u32)>,
{
assert!(
std::ptr::eq(metric.cch, self.cch),
"CCHMetricPartialUpdater must be used with metrics from the same CCH"
);
for (k, v) in updates {
metric.weights[*k as usize] = *v; }
unsafe {
cch_partial_reset(self.partial.as_mut().unwrap());
for (k, _) in updates {
cch_partial_update_arc(self.partial.as_mut().unwrap(), *k);
}
cch_partial_customize(
self.partial.as_mut().unwrap(),
metric.inner.as_mut().unwrap(),
);
}
}
}
pub struct CCHQuery<'a> {
inner: UniquePtr<ffi::CCHQuery>,
metric: &'a CCHMetric<'a>,
state: [bool; 2],
}
impl<'a> CCHQuery<'a> {
pub fn new(metric: &'a CCHMetric<'a>) -> Self {
let inner = unsafe { cch_query_new(&metric.inner) };
CCHQuery {
inner,
metric,
state: [false; 2],
}
}
pub fn add_source(&mut self, s: u32, dist: u32) {
assert!(
(s as usize) < self.metric.cch.node_count,
"source node id out of range",
);
unsafe {
cch_query_add_source(self.inner.as_mut().unwrap(), s, dist);
}
self.state[0] = true;
}
pub fn add_target(&mut self, t: u32, dist: u32) {
assert!(
(t as usize) < self.metric.cch.node_count,
"target node id out of range",
);
unsafe {
cch_query_add_target(self.inner.as_mut().unwrap(), t, dist);
}
self.state[1] = true;
}
pub fn run<'b>(&'b mut self) -> CCHQueryResult<'b, 'a> {
assert!(
self.state.iter().all(|&x| x),
"must add at least one source and one target before running the query"
);
unsafe {
cch_query_run(self.inner.as_mut().unwrap());
}
CCHQueryResult { query: self }
}
}
pub struct CCHQueryResult<'b, 'a> {
query: &'b mut CCHQuery<'a>,
}
impl<'b, 'a> CCHQueryResult<'b, 'a> {
pub fn distance(&self) -> Option<u32> {
let res = unsafe { cch_query_distance(self.query.inner.as_ref().unwrap()) };
if res == INF_WEIGHT {
None
} else {
Some(res)
}
}
pub fn node_path(&self) -> Vec<u32> {
unsafe { cch_query_node_path(self.query.inner.as_ref().unwrap()) }
}
pub fn arc_path(&self) -> Vec<u32> {
unsafe { cch_query_arc_path(self.query.inner.as_ref().unwrap()) }
}
}
impl<'b, 'a> Drop for CCHQueryResult<'b, 'a> {
fn drop(&mut self) {
unsafe {
cch_query_reset(
self.query.inner.as_mut().unwrap(),
self.query.metric.inner.as_ref().unwrap(),
);
}
self.query.state.iter_mut().for_each(|x| *x = false);
}
}
pub struct CCHOneToMany<'a> {
inner: UniquePtr<ffi::CCHQuery>,
metric: &'a CCHMetric<'a>,
target_count: usize,
}
impl<'a> CCHOneToMany<'a> {
pub fn new(metric: &'a CCHMetric<'a>, targets: &[u32]) -> Self {
assert!(!targets.is_empty(), "must provide at least one target");
assert!(
targets
.iter()
.all(|&t| (t as usize) < metric.cch.node_count),
"target node id out of range",
);
let mut inner = unsafe { cch_query_new(&metric.inner) };
unsafe { cch_query_pin_targets(inner.as_mut().unwrap(), targets) };
CCHOneToMany {
inner,
metric,
target_count: targets.len(),
}
}
pub fn target_count(&self) -> usize {
self.target_count
}
pub fn repin_targets(&mut self, targets: &[u32]) {
assert!(!targets.is_empty(), "must provide at least one target");
assert!(
targets
.iter()
.all(|&t| (t as usize) < self.metric.cch.node_count),
"target node id out of range",
);
unsafe {
cch_query_reset(
self.inner.as_mut().unwrap(),
self.metric.inner.as_ref().unwrap(),
);
cch_query_pin_targets(self.inner.as_mut().unwrap(), targets);
}
self.target_count = targets.len();
}
pub fn distances_from(&mut self, source: u32) -> Vec<Option<u32>> {
self.distances_from_multi(&[(source, 0)])
}
pub fn distances_from_multi(&mut self, sources: &[(u32, u32)]) -> Vec<Option<u32>> {
assert!(!sources.is_empty(), "must provide at least one source");
assert!(
sources
.iter()
.all(|&(s, _)| (s as usize) < self.metric.cch.node_count),
"source node id out of range",
);
unsafe {
for &(s, d) in sources {
cch_query_add_source(self.inner.as_mut().unwrap(), s, d);
}
cch_query_run_to_pinned_targets(self.inner.as_mut().unwrap());
let dist = cch_query_distances_to_targets(self.inner.as_ref().unwrap());
cch_query_reset_source(self.inner.as_mut().unwrap());
dist.into_iter()
.map(|d| if d == INF_WEIGHT { None } else { Some(d) })
.collect()
}
}
}
pub struct CCHManyToOne<'a> {
inner: UniquePtr<ffi::CCHQuery>,
metric: &'a CCHMetric<'a>,
source_count: usize,
}
impl<'a> CCHManyToOne<'a> {
pub fn new(metric: &'a CCHMetric<'a>, sources: &[u32]) -> Self {
assert!(!sources.is_empty(), "must provide at least one source");
assert!(
sources
.iter()
.all(|&s| (s as usize) < metric.cch.node_count),
"source node id out of range",
);
let mut inner = unsafe { cch_query_new(&metric.inner) };
unsafe { cch_query_pin_sources(inner.as_mut().unwrap(), sources) };
CCHManyToOne {
inner,
metric,
source_count: sources.len(),
}
}
pub fn source_count(&self) -> usize {
self.source_count
}
pub fn repin_sources(&mut self, sources: &[u32]) {
assert!(!sources.is_empty(), "must provide at least one source");
assert!(
sources
.iter()
.all(|&s| (s as usize) < self.metric.cch.node_count),
"source node id out of range",
);
unsafe {
cch_query_reset(
self.inner.as_mut().unwrap(),
self.metric.inner.as_ref().unwrap(),
);
cch_query_pin_sources(self.inner.as_mut().unwrap(), sources);
}
self.source_count = sources.len();
}
pub fn distances_to(&mut self, target: u32) -> Vec<Option<u32>> {
self.distances_to_multi(&[(target, 0)])
}
pub fn distances_to_multi(&mut self, targets: &[(u32, u32)]) -> Vec<Option<u32>> {
assert!(!targets.is_empty(), "must provide at least one target");
assert!(
targets
.iter()
.all(|&(t, _)| (t as usize) < self.metric.cch.node_count),
"target node id out of range",
);
unsafe {
for &(t, d) in targets {
cch_query_add_target(self.inner.as_mut().unwrap(), t, d);
}
cch_query_run_to_pinned_sources(self.inner.as_mut().unwrap());
let dist = cch_query_distances_to_sources(self.inner.as_ref().unwrap());
cch_query_reset_target(self.inner.as_mut().unwrap());
dist.into_iter()
.map(|d| if d == INF_WEIGHT { None } else { Some(d) })
.collect()
}
}
}
#[cfg(feature = "pyo3")]
mod python_binding;