use crate::{graphs::bvgraph::DCF, traits::LenderIntoIter, utils::Granularity};
use super::{LenderLabel, NodeLabelsLender, ParMapFold};
use core::ops::Range;
use dsi_progress_logger::prelude::*;
use impl_tools::autoimpl;
use lender::*;
use std::rc::Rc;
use sux::{
dict::EliasFanoBuilder,
rank_sel::{SelectAdaptConst, SelectZeroAdaptConst},
traits::Succ,
utils::FairChunks,
};
use thiserror::Error;
#[autoimpl(for<S: trait + ?Sized> &S, &mut S, Rc<S>)]
pub trait SequentialLabeling {
type Label;
type Lender<'node>: for<'next> NodeLabelsLender<'next, Label = Self::Label>
where
Self: 'node;
fn num_nodes(&self) -> usize;
fn num_arcs_hint(&self) -> Option<u64> {
None
}
fn iter(&self) -> Self::Lender<'_> {
self.iter_from(0)
}
fn iter_from(&self, from: usize) -> Self::Lender<'_>;
fn build_dcf(&self) -> DCF {
let n = self.num_nodes();
let num_arcs = self
.num_arcs_hint()
.expect("build_dcf requires num_arcs_hint()") as usize;
let mut efb = EliasFanoBuilder::new(n + 1, num_arcs);
efb.push(0);
let mut cumul = 0usize;
let mut lender = self.iter();
while let Some((_node, succs)) = lender.next() {
cumul += succs.into_iter().count();
efb.push(cumul);
}
unsafe {
efb.build().map_high_bits(|high_bits| {
SelectZeroAdaptConst::<_, _, 12, 4>::new(SelectAdaptConst::<_, _, 12, 4>::new(
high_bits,
))
})
}
}
fn par_node_apply<
A: Default + Send,
F: Fn(Range<usize>) -> A + Sync,
R: Fn(A, A) -> A + Sync,
>(
&self,
func: F,
fold: R,
granularity: Granularity,
pl: &mut impl ConcurrentProgressLog,
) -> A {
let num_nodes = self.num_nodes();
let node_granularity = granularity.node_granularity(num_nodes, self.num_arcs_hint());
(0..num_nodes.div_ceil(node_granularity))
.map(|i| i * node_granularity..num_nodes.min((i + 1) * node_granularity))
.par_map_fold_with(
pl.clone(),
|pl, range| {
let len = range.len();
let res = func(range);
pl.update_with_count(len);
res
},
fold,
)
}
fn par_apply<
F: Fn(Range<usize>) -> A + Sync,
A: Default + Send,
R: Fn(A, A) -> A + Sync,
D: for<'a> Succ<Input = usize, Output<'a> = usize>,
>(
&self,
func: F,
fold: R,
granularity: Granularity,
deg_cumul: &D,
pl: &mut impl ConcurrentProgressLog,
) -> A {
FairChunks::new(
granularity.arc_granularity(
self.num_nodes(),
Some(deg_cumul.get(deg_cumul.len() - 1) as u64),
),
deg_cumul,
)
.par_map_fold_with(
pl.clone(),
|pl, range| {
let len = range.len();
let res = func(range);
pl.update_with_count(len);
res
},
fold,
)
}
}
#[derive(Error, Debug, Clone, PartialEq, Eq)]
pub enum EqError {
#[error("Different number of nodes: {first} != {second}")]
NumNodes { first: usize, second: usize },
#[error("Different number of arcs: {first} != {second}")]
NumArcs { first: u64, second: u64 },
#[error("Different node: at index {index} {first} != {second}")]
Node {
index: usize,
first: usize,
second: usize,
},
#[error("Different successors for node {node}: at index {index} {first} != {second}")]
Successors {
node: usize,
index: usize,
first: String,
second: String,
},
#[error("Different outdegree for node {node}: {first} != {second}")]
Outdegree {
node: usize,
first: usize,
second: usize,
},
}
#[doc(hidden)]
pub fn eq_succs<L: PartialEq + std::fmt::Debug>(
node: usize,
succ0: impl IntoIterator<Item = L>,
succ1: impl IntoIterator<Item = L>,
) -> Result<(), EqError> {
let mut succ0 = succ0.into_iter();
let mut succ1 = succ1.into_iter();
let mut index = 0;
loop {
match (succ0.next(), succ1.next()) {
(None, None) => return Ok(()),
(Some(s0), Some(s1)) => {
if s0 != s1 {
return Err(EqError::Successors {
node,
index,
first: format!("{s0:?}"),
second: format!("{s1:?}"),
});
}
}
(None, Some(_)) => {
return Err(EqError::Outdegree {
node,
first: index,
second: index + 1 + succ1.count(),
});
}
(Some(_), None) => {
return Err(EqError::Outdegree {
node,
first: index + 1 + succ0.count(),
second: index,
});
}
}
index += 1;
}
}
pub fn eq_sorted<L0: SequentialLabeling, L1: SequentialLabeling<Label = L0::Label>>(
l0: &L0,
l1: &L1,
) -> Result<(), EqError>
where
for<'a> L0::Lender<'a>: SortedLender,
for<'a> L1::Lender<'a>: SortedLender,
for<'a, 'b> LenderIntoIter<'b, L0::Lender<'a>>: SortedIterator,
for<'a, 'b> LenderIntoIter<'b, L1::Lender<'a>>: SortedIterator,
L0::Label: PartialEq + std::fmt::Debug,
{
if l0.num_nodes() != l1.num_nodes() {
return Err(EqError::NumNodes {
first: l0.num_nodes(),
second: l1.num_nodes(),
});
}
for_!((index, ((node0, succ0), (node1, succ1))) in l0.iter().zip(l1.iter()).enumerate() {
if node0 != node1 {
return Err(EqError::Node {
index,
first: node0,
second: node1,
});
}
eq_succs(node0, succ0, succ1)?;
});
Ok(())
}
pub type Labels<'succ, 'node, S> =
<<S as SequentialLabeling>::Lender<'node> as NodeLabelsLender<'succ>>::IntoIterator;
pub unsafe trait SortedLender: Lender {}
#[derive(Debug, Clone)]
#[repr(transparent)]
pub struct AssumeSortedLender<L> {
lender: L,
}
impl<L> AssumeSortedLender<L> {
pub unsafe fn new(lender: L) -> Self {
Self { lender }
}
}
unsafe impl<L: Lender> SortedLender for AssumeSortedLender<L> {}
impl<'succ, L: Lender> Lending<'succ> for AssumeSortedLender<L> {
type Lend = <L as Lending<'succ>>::Lend;
}
impl<L: Lender> Lender for AssumeSortedLender<L> {
unsafe_assume_covariance!();
#[inline(always)]
fn next(&mut self) -> Option<Lend<'_, Self>> {
self.lender.next()
}
#[inline(always)]
fn size_hint(&self) -> (usize, Option<usize>) {
self.lender.size_hint()
}
}
impl<L: ExactSizeLender> ExactSizeLender for AssumeSortedLender<L> {
#[inline(always)]
fn len(&self) -> usize {
self.lender.len()
}
}
impl<'lend, L> NodeLabelsLender<'lend> for AssumeSortedLender<L>
where
L: Lender + for<'next> NodeLabelsLender<'next>,
{
type Label = LenderLabel<'lend, L>;
type IntoIterator = <L as NodeLabelsLender<'lend>>::IntoIterator;
}
pub unsafe trait SortedIterator: Iterator {}
#[derive(Debug, Clone)]
#[repr(transparent)]
pub struct AssumeSortedIterator<I> {
iter: I,
}
impl<I> AssumeSortedIterator<I> {
pub unsafe fn new(iter: I) -> Self {
Self { iter }
}
}
unsafe impl<I: Iterator> SortedIterator for AssumeSortedIterator<I> {}
impl<I: Iterator> Iterator for AssumeSortedIterator<I> {
type Item = I::Item;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
#[inline(always)]
fn count(self) -> usize {
self.iter.count()
}
}
impl<I: ExactSizeIterator> ExactSizeIterator for AssumeSortedIterator<I> {
#[inline(always)]
fn len(&self) -> usize {
self.iter.len()
}
}
#[autoimpl(for<S: trait + ?Sized> &S, &mut S, Rc<S>)]
pub trait RandomAccessLabeling: SequentialLabeling {
type Labels<'succ>: IntoIterator<Item = <Self as SequentialLabeling>::Label>
where
Self: 'succ;
fn num_arcs(&self) -> u64;
fn labels(&self, node_id: usize) -> <Self as RandomAccessLabeling>::Labels<'_>;
fn outdegree(&self, node_id: usize) -> usize;
}
#[derive(Error, Debug, Clone, PartialEq, Eq)]
pub enum CheckImplError {
#[error("Different number of nodes: {iter} (iter) != {method} (num_nodes)")]
NumNodes { iter: usize, method: usize },
#[error("Different number of arcs: {iter} (iter) != {method} (num_arcs)")]
NumArcs { iter: u64, method: u64 },
#[error(
"Different successors for node {node}: at index {index} {sequential} (sequential) != {random_access} (random access)"
)]
Successors {
node: usize,
index: usize,
sequential: String,
random_access: String,
},
#[error(
"Different outdegree for node {node}: {sequential} (sequential) != {random_access} (random access)"
)]
Outdegree {
node: usize,
sequential: usize,
random_access: usize,
},
}
pub fn check_impl<L: RandomAccessLabeling>(l: L) -> Result<(), CheckImplError>
where
L::Label: PartialEq + std::fmt::Debug,
{
let mut num_nodes = 0;
let mut num_arcs: u64 = 0;
for_!((node, succ_iter) in l.iter() {
num_nodes += 1;
let mut succ_iter = succ_iter.into_iter();
let mut succ = l.labels(node).into_iter();
let mut index = 0;
loop {
match (succ_iter.next(), succ.next()) {
(None, None) => break,
(Some(s0), Some(s1)) => {
if s0 != s1 {
return Err(CheckImplError::Successors {
node,
index,
sequential: format!("{s0:?}"),
random_access: format!("{s1:?}"),
});
}
}
(None, Some(_)) => {
return Err(CheckImplError::Outdegree {
node,
sequential: index,
random_access: index + 1 + succ.count(),
});
}
(Some(_), None) => {
return Err(CheckImplError::Outdegree {
node,
sequential: index + 1 + succ_iter.count(),
random_access: index,
});
}
}
index += 1;
}
num_arcs += index as u64;
});
if num_nodes != l.num_nodes() {
Err(CheckImplError::NumNodes {
method: l.num_nodes(),
iter: num_nodes,
})
} else if num_arcs != l.num_arcs() {
Err(CheckImplError::NumArcs {
method: l.num_arcs(),
iter: num_arcs,
})
} else {
Ok(())
}
}
pub struct LenderImpl<'node, G: RandomAccessLabeling> {
pub labeling: &'node G,
pub nodes: core::ops::Range<usize>,
}
unsafe impl<G: RandomAccessLabeling> SortedLender for LenderImpl<'_, G> {}
impl<'succ, G: RandomAccessLabeling> NodeLabelsLender<'succ> for LenderImpl<'_, G> {
type Label = G::Label;
type IntoIterator = <G as RandomAccessLabeling>::Labels<'succ>;
}
impl<'succ, G: RandomAccessLabeling> Lending<'succ> for LenderImpl<'_, G> {
type Lend = (usize, <G as RandomAccessLabeling>::Labels<'succ>);
}
impl<G: RandomAccessLabeling> Lender for LenderImpl<'_, G> {
unsafe_assume_covariance!();
#[inline(always)]
fn next(&mut self) -> Option<Lend<'_, Self>> {
self.nodes
.next()
.map(|node_id| (node_id, self.labeling.labels(node_id)))
}
#[inline(always)]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.nodes.len(), Some(self.nodes.len()))
}
}
impl<G: RandomAccessLabeling> ExactSizeLender for LenderImpl<'_, G> {
#[inline(always)]
fn len(&self) -> usize {
self.nodes.len()
}
}