use std::cmp::Reverse;
use std::collections::BinaryHeap;
use std::sync::Mutex;
use fixedbitset::FixedBitSet;
use rustc_hash::FxHashMap;
use crate::K;
use crate::Timetable;
use crate::algorithm::boarding::BoardingTree;
use crate::algorithm::label_bag::LabelBag;
use crate::ids::RouteIdx;
use crate::ids::StopIdx;
use crate::label::ArrivalTime;
use crate::label::Label;
use crate::time::SecondOfDay;
pub struct RaptorCache<L: Label = ArrivalTime> {
pub(crate) n_stops: u32,
pub(crate) n_routes: u32,
pub(crate) labels: Vec<Vec<LabelBag<L>>>,
pub(crate) best_arrival: Vec<LabelBag<L>>,
pub(crate) board_detail: BoardingTree,
pub(crate) marked_stops: FixedBitSet,
pub(crate) q_entry: Vec<Option<u32>>,
pub(crate) q_routes: Vec<RouteIdx>,
pub(crate) walked_buf: Vec<StopIdx>,
pub(crate) origin_set: FixedBitSet,
pub(crate) relax_heap: BinaryHeap<Reverse<(SecondOfDay, u32)>>,
pub(crate) ever_reached: FixedBitSet,
}
impl<L: Label> RaptorCache<L> {
pub fn for_timetable<T: Timetable + ?Sized>(tt: &T) -> Self {
Self::with_capacity(tt.n_stops() as u32, tt.n_routes() as u32)
}
pub fn with_capacity(n_stops: u32, n_routes: u32) -> Self {
Self {
n_stops,
n_routes,
labels: Vec::new(),
best_arrival: (0..n_stops).map(|_| LabelBag::new()).collect(),
board_detail: FxHashMap::default(),
marked_stops: FixedBitSet::with_capacity(n_stops as usize),
q_entry: vec![None; n_routes as usize],
q_routes: Vec::new(),
walked_buf: Vec::new(),
origin_set: FixedBitSet::with_capacity(n_stops as usize),
relax_heap: BinaryHeap::new(),
ever_reached: FixedBitSet::with_capacity(n_stops as usize),
}
}
pub(crate) fn reset_for_query(&mut self, transfers: K, tt_n_stops: u32, tt_n_routes: u32) {
assert_eq!(
self.n_stops, tt_n_stops,
"RaptorCache sized for {} stops but timetable has {}",
self.n_stops, tt_n_stops
);
assert_eq!(
self.n_routes, tt_n_routes,
"RaptorCache sized for {} routes but timetable has {}",
self.n_routes, tt_n_routes
);
let needed = transfers + 1;
for v in self.labels.iter_mut() {
for b in v.iter_mut() {
b.clear();
}
}
if self.labels.len() < needed {
self.labels.resize_with(needed, || {
(0..self.n_stops).map(|_| LabelBag::new()).collect()
});
} else {
self.labels.truncate(needed);
}
for v in &mut self.best_arrival {
v.clear();
}
self.board_detail.clear();
self.marked_stops.clear();
self.ever_reached.clear();
for r in self.q_routes.drain(..) {
self.q_entry[r.idx()] = None;
}
self.walked_buf.clear();
}
}
pub struct RaptorCachePool<L: Label = ArrivalTime> {
n_stops: u32,
n_routes: u32,
pool: Mutex<Vec<RaptorCache<L>>>,
}
impl<L: Label> RaptorCachePool<L> {
pub fn for_timetable<T: Timetable + ?Sized>(tt: &T) -> Self {
Self::with_capacity(tt.n_stops() as u32, tt.n_routes() as u32)
}
pub fn with_capacity(n_stops: u32, n_routes: u32) -> Self {
Self {
n_stops,
n_routes,
pool: Mutex::new(Vec::new()),
}
}
pub fn checkout(&self) -> PooledCache<'_, L> {
let cache = self
.pool
.lock()
.expect("RaptorCachePool mutex poisoned")
.pop()
.unwrap_or_else(|| RaptorCache::with_capacity(self.n_stops, self.n_routes));
PooledCache {
pool: self,
cache: Some(cache),
}
}
}
impl<L: Label> std::fmt::Debug for RaptorCachePool<L> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let len = self.pool.lock().map(|p| p.len()).unwrap_or(0);
f.debug_struct("RaptorCachePool")
.field("n_stops", &self.n_stops)
.field("n_routes", &self.n_routes)
.field("idle_caches", &len)
.finish()
}
}
pub struct PooledCache<'p, L: Label = ArrivalTime> {
pool: &'p RaptorCachePool<L>,
cache: Option<RaptorCache<L>>,
}
impl<L: Label> std::ops::Deref for PooledCache<'_, L> {
type Target = RaptorCache<L>;
fn deref(&self) -> &RaptorCache<L> {
self.cache.as_ref().expect("PooledCache used after drop")
}
}
impl<L: Label> std::ops::DerefMut for PooledCache<'_, L> {
fn deref_mut(&mut self) -> &mut RaptorCache<L> {
self.cache.as_mut().expect("PooledCache used after drop")
}
}
impl<L: Label> Drop for PooledCache<'_, L> {
fn drop(&mut self) {
if let Some(cache) = self.cache.take() {
if let Ok(mut pool) = self.pool.pool.lock() {
pool.push(cache);
}
}
}
}