use alloc::sync::Arc;
use core::fmt::Debug;
use geo::Distance;
use routers_network::{Entry, Metadata, Network};
use rustc_hash::{FxBuildHasher, FxHashMap};
use scc::HashMap;
pub trait CacheKey: Entry {}
impl<T> CacheKey for T where T: Entry {}
#[derive(Debug)]
pub struct CacheMap<K, V, M, N, Meta>
where
K: CacheKey,
V: Debug,
M: Metadata,
Meta: Debug,
N: Network<K, M>,
{
pub(crate) map: HashMap<K, Arc<V>, FxBuildHasher>,
pub(crate) metadata: Meta,
_marker: core::marker::PhantomData<M>,
_marker2: core::marker::PhantomData<N>,
}
#[derive(Debug)]
pub struct LockedMap<K, V, M, N, Meta>(Arc<CacheMap<K, V, M, N, Meta>>)
where
LockedMap<K, V, M, N, Meta>: Calculable<K, M, N, V>,
M: Metadata,
K: CacheKey,
N: Network<K, M>,
V: Debug,
Meta: Debug;
impl<K, V, M, N, Meta> Default for LockedMap<K, V, M, N, Meta>
where
LockedMap<K, V, M, N, Meta>: Calculable<K, M, N, V>,
CacheMap<K, V, M, N, Meta>: Default,
M: Metadata,
K: CacheKey,
V: Debug,
N: Network<K, M>,
Meta: Debug,
{
fn default() -> Self {
LockedMap(Arc::new(CacheMap::default()))
}
}
impl<K, V, M, N, Meta> Clone for LockedMap<K, V, M, N, Meta>
where
LockedMap<K, V, M, N, Meta>: Calculable<K, M, N, V>,
CacheMap<K, V, M, N, Meta>: Default,
N: Network<K, M>,
M: Metadata,
K: CacheKey,
V: Debug,
Meta: Debug,
{
fn clone(&self) -> Self {
LockedMap(Arc::clone(&self.0))
}
}
impl<K, V, N, M, Meta> LockedMap<K, V, M, N, Meta>
where
LockedMap<K, V, M, N, Meta>: Calculable<K, M, N, V>,
M: Metadata,
K: CacheKey,
N: Network<K, M>,
V: Debug,
Meta: Debug,
{
pub fn query(&self, ctx: &RoutingContext<K, M, N>, key: K) -> Arc<V> {
if let Some(value) = self.0.map.get(&key) {
return Arc::clone(value.get());
}
let calculated = Arc::new(self.calculate(ctx, key));
let _ = self.0.map.insert(key, calculated.clone());
Arc::clone(&calculated)
}
}
impl<K, V, M, N, Meta> Default for CacheMap<K, V, M, N, Meta>
where
K: CacheKey,
V: Debug,
M: Metadata,
N: Network<K, M>,
Meta: Default + Debug,
{
fn default() -> Self {
Self {
map: HashMap::default(),
metadata: Meta::default(),
_marker: core::marker::PhantomData,
_marker2: core::marker::PhantomData,
}
}
}
pub trait Calculable<E: CacheKey, M: Metadata, N: Network<E, M>, V> {
fn calculate(&self, ctx: &RoutingContext<E, M, N>, key: E) -> V;
}
mod successor {
use super::*;
use crate::{primitives::WeightAndDistance, transition::*};
use geo::Haversine;
use routers_network::DirectionAwareEdgeId;
type SuccessorWeights<E> = Vec<(E, DirectionAwareEdgeId<E>, WeightAndDistance)>;
pub type SuccessorsCache<E, M, N> = LockedMap<E, SuccessorWeights<E>, M, N, ()>;
impl<E: CacheKey, M: Metadata, N: Network<E, M>> Calculable<E, M, N, SuccessorWeights<E>>
for SuccessorsCache<E, M, N>
{
#[inline]
fn calculate(&self, ctx: &RoutingContext<E, M, N>, key: E) -> SuccessorWeights<E> {
#[allow(unsafe_code)]
let source = unsafe { ctx.map.point(&key).unwrap_unchecked() };
ctx.map
.edges_outof(key)
.map(|(_, next, (w, edge))| {
const METER_TO_CM: f64 = 100.0;
#[allow(unsafe_code)]
let position = unsafe { ctx.map.point(&next).unwrap_unchecked() };
let distance = Haversine.distance(source, position);
(next, (distance * METER_TO_CM) as u32, w, edge)
})
.map(|(next, distance, weight, edge)| {
let fraction = WeightAndDistance::new(Fraction::mul(weight), distance);
(next, edge, fraction)
})
.collect::<Vec<_>>()
}
}
}
mod predicate {
use crate::{WeightAndDistance, primitives::Dijkstra};
use routers_network::{Entry, Network};
use super::*;
const DEFAULT_THRESHOLD: f64 = 200_000f64;
#[derive(Debug)]
pub struct PredicateMetadata<E, M, N>
where
E: Entry,
M: Metadata,
N: Network<E, M>,
{
successors: SuccessorsCache<E, M, N>,
threshold_distance: f64,
}
impl<E, M, N> Default for PredicateMetadata<E, M, N>
where
E: Entry,
M: Metadata,
N: Network<E, M>,
{
fn default() -> Self {
Self {
successors: SuccessorsCache::default(),
threshold_distance: DEFAULT_THRESHOLD,
}
}
}
type Predicates<E> = FxHashMap<E, (E, WeightAndDistance)>;
pub type PredicateCache<E, M, N> =
LockedMap<E, Predicates<E>, M, N, PredicateMetadata<E, M, N>>;
impl<E: CacheKey, M: Metadata, N: Network<E, M>> Calculable<E, M, N, Predicates<E>>
for PredicateCache<E, M, N>
{
#[inline]
fn calculate(&self, ctx: &RoutingContext<E, M, N>, key: E) -> Predicates<E> {
let threshold = self.0.metadata.threshold_distance;
Dijkstra
.reach(&key, move |node| {
ArcIter::new(self.0.metadata.successors.query(ctx, *node))
.filter(|(_, edge, _)| {
let meta = ctx.map.metadata(&edge.index());
if meta.is_none() {
return false;
}
let direction = edge.direction();
meta.unwrap().accessible(ctx.runtime, direction)
})
.map(|(a, _, b)| (a, b))
})
.take_while(|p| {
(p.total_cost.1 as f64) < threshold
})
.map(|pre| {
let parent = pre.parent.unwrap_or_default();
(pre.node, (parent, pre.total_cost))
})
.collect::<Predicates<E>>()
}
}
}
struct ArcIter<T> {
data: Arc<Vec<T>>,
index: usize,
}
impl<T> ArcIter<T> {
#[inline(always)]
fn new(data: Arc<Vec<T>>) -> Self {
ArcIter { data, index: 0 }
}
}
impl<T> Iterator for ArcIter<T>
where
T: Copy,
{
type Item = T;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
let item = *self.data.get(self.index)?;
self.index += 1;
Some(item)
}
}
pub use predicate::PredicateCache;
pub use successor::SuccessorsCache;
use crate::primitives::RoutingContext;