use crate::graph::GraphStorage;
use crate::traversal::bulk::{merge_saves_work, merge_traversers, merge_traversers_adaptive};
use crate::*;
#[derive(Clone)]
pub(crate) struct StepBarrier {
pub(crate) max_size: Option<u32>,
pub(crate) consumer: Option<BarrierConsumer>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum BarrierConsumer {
NormSack,
}
impl fmt::Display for StepBarrier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match (self.consumer, self.max_size) {
(Some(BarrierConsumer::NormSack), _) => write!(f, "barrier(Barrier.normSack)"),
(None, None) => write!(f, "barrier()"),
(None, Some(n)) => write!(f, "barrier({n})"),
}
}
}
impl StepBarrier {
fn size_error(size: u32) -> TraverserError {
TraverserError::Script(ScriptError::ArgumentMismatch {
step_name: "barrier".to_string(),
expected: "a maximum size of at least 1".to_string(),
actual: size.to_string(),
})
}
}
impl<'g, S: GraphStorage + 'g> GraphTraversalSource<'g, S> {
pub fn barrier(self) -> Self {
self.push_step(barrier_step(None))
}
pub fn barrier_with_size(self, n: u32) -> Self {
self.push_step(barrier_step(Some(n)))
}
pub fn barrier_with(self, barrier: Barrier) -> Self {
self.push_step(consumer_step(barrier))
}
}
fn barrier_step<'g, S: GraphStorage + 'g>(max_size: Option<u32>) -> GraphStep<'g, S> {
GraphStep::FlatBarrier(
"barrier",
Box::new(StepBarrier {
max_size,
consumer: None,
}),
)
}
fn consumer_step<'g, S: GraphStorage + 'g>(barrier: Barrier) -> GraphStep<'g, S> {
let consumer = match barrier {
Barrier::NormSack => BarrierConsumer::NormSack,
};
GraphStep::FlatBarrier(
"barrier",
Box::new(StepBarrier {
max_size: None,
consumer: Some(consumer),
}),
)
}
impl __ {
pub fn barrier<'g, S: GraphStorage + 'g>() -> AnonymousTraversal<'g, S> {
AnonymousTraversal::new().barrier()
}
pub fn barrier_with_size<'g, S: GraphStorage + 'g>(n: u32) -> AnonymousTraversal<'g, S> {
AnonymousTraversal::new().barrier_with_size(n)
}
pub fn barrier_with<'g, S: GraphStorage + 'g>(barrier: Barrier) -> AnonymousTraversal<'g, S> {
AnonymousTraversal::new().barrier_with(barrier)
}
}
impl<'g, S: GraphStorage + 'g> AnonymousTraversal<'g, S> {
pub fn barrier(self) -> Self {
self.push_step(barrier_step(None))
}
pub fn barrier_with_size(self, n: u32) -> Self {
self.push_step(barrier_step(Some(n)))
}
pub fn barrier_with(self, barrier: Barrier) -> Self {
self.push_step(consumer_step(barrier))
}
}
pub(crate) struct StepLazyBarrier<'g, S: GraphStorage + 'g> {
next: Box<dyn GraphStepFlatMap<'g, S> + 'g>,
}
impl<'g, S: GraphStorage + 'g> fmt::Display for StepLazyBarrier<'g, S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "lazy_barrier()")
}
}
pub(crate) fn lazy_barrier_step<'g, S: GraphStorage + 'g>(
next: Box<dyn GraphStepFlatMap<'g, S> + 'g>,
) -> GraphStep<'g, S> {
GraphStep::FlatBarrier("barrier", Box::new(StepLazyBarrier { next }))
}
impl<'g, S: GraphStorage + 'g> GraphStepFlatBarrier<'g, S> for StepLazyBarrier<'g, S> {
fn process(
&self,
context: &mut TraverserContext<'_, S>,
source: Vec<Traverser<S>>,
) -> TraverserResult<Option<Vec<Traverser<S>>>> {
if !context.auto_merge
|| !context.bulk_merge
|| context.path_mode.is_full()
|| !merge_saves_work(context, &source, self.next.as_ref())
{
return Ok(Some(source));
}
Ok(Some(
merge_traversers_adaptive(context, source, "lazy_barrier")?.0,
))
}
fn clone_box(&self) -> Box<dyn GraphStepFlatBarrier<'g, S> + 'g> {
Box::new(StepLazyBarrier {
next: self.next.clone_box(),
})
}
}
impl<'g, S: GraphStorage + 'g> GraphStepFlatBarrier<'g, S> for StepBarrier {
fn process(
&self,
context: &mut TraverserContext<'_, S>,
source: Vec<Traverser<S>>,
) -> TraverserResult<Option<Vec<Traverser<S>>>> {
if self.max_size == Some(0) {
return Err(Box::new(Self::size_error(0)));
}
let merged = merge_traversers(context, source, "barrier")?;
let mut merged = merged;
match self.consumer {
Some(BarrierConsumer::NormSack) => norm_sack(context, &mut merged)?,
None => {}
}
Ok(Some(merged))
}
fn clone_box(&self) -> Box<dyn GraphStepFlatBarrier<'g, S> + 'g> {
Box::new(self.clone())
}
}
fn sack_number(value: &ElementProperty) -> Option<f64> {
match value {
ElementProperty::Number(n) => n.as_i64().map(|i| i as f64).or_else(|| n.as_f64()),
_ => None,
}
}
fn norm_sack_error(value: &ElementProperty) -> TraverserError {
ValueError::OperatorFailed {
operator: "normSack",
kind: OperatorFailure::IncompatibleOperands { expected: "number" },
left: crate::value::operator::describe(value),
right: "the sum of the sacks (float64)".to_string(),
}
.into()
}
fn norm_sack<S: GraphStorage>(
context: &mut TraverserContext<'_, S>,
stream: &mut [Traverser<S>],
) -> TraverserResult<()> {
let with_operator = context.sack_merge.is_some();
let mut total = 0.0_f64;
for traverser in stream.iter() {
context.tick()?;
let sack = context.sacks.get(traverser.sack);
match sack {
ElementProperty::Null => {}
other => match sack_number(other) {
Some(n) => total += n * traverser.bulk as f64,
None => return Err(Box::new(norm_sack_error(other))),
},
}
}
for traverser in stream.iter_mut() {
context.tick()?;
let Some(n) = sack_number(context.sacks.get(traverser.sack)) else {
continue;
};
let numerator = if with_operator {
n * traverser.bulk as f64
} else {
n
};
traverser.sack = context
.sacks
.intern(ElementProperty::from(numerator / total))?;
}
Ok(())
}