use super::{
operand::{
EdgeIndexComparisonOperand, EdgeIndexOperand, EdgeIndicesComparisonOperand,
EdgeIndicesOperand,
},
BinaryArithmeticKind, EdgeOperand, MultipleComparisonKind, SingleComparisonKind,
};
use crate::{
errors::{MedRecordError, MedRecordResult},
medrecord::{
datatypes::{Contains, EndsWith, Mod, StartsWith},
querying::{
attributes::AttributesTreeOperand,
edges::SingleKind,
group_by::{GroupOperand, PartitionGroups},
nodes::NodeOperand,
tee_grouped_iterator,
values::{MultipleValuesWithIndexContext, MultipleValuesWithIndexOperand},
wrapper::{CardinalityWrapper, MatchMode, Wrapper},
BoxedIterator, DeepClone, EvaluateForward, EvaluateForwardGrouped, GroupedIterator,
},
EdgeIndex, Group, MedRecordAttribute, MedRecordValue,
},
MedRecord,
};
use itertools::Itertools;
use medmodels_utils::{aliases::MrHashSet, traits::ReadWriteOrPanic};
use rand::{rng, seq::IteratorRandom};
use std::ops::{Add, Mul, Sub};
#[derive(Debug, Clone)]
pub enum EdgeOperation {
Values {
operand: Wrapper<MultipleValuesWithIndexOperand<EdgeOperand>>,
},
Attributes {
operand: Wrapper<AttributesTreeOperand<EdgeOperand>>,
},
Indices {
operand: Wrapper<EdgeIndicesOperand>,
},
InGroup {
group: CardinalityWrapper<Group>,
},
HasAttribute {
attribute: CardinalityWrapper<MedRecordAttribute>,
},
SourceNode {
operand: Wrapper<NodeOperand>,
},
TargetNode {
operand: Wrapper<NodeOperand>,
},
EitherOr {
either: Wrapper<EdgeOperand>,
or: Wrapper<EdgeOperand>,
},
Exclude {
operand: Wrapper<EdgeOperand>,
},
GroupBy {
operand: Wrapper<GroupOperand<EdgeOperand>>,
},
}
impl DeepClone for EdgeOperation {
fn deep_clone(&self) -> Self {
match self {
Self::Values { operand } => Self::Values {
operand: operand.deep_clone(),
},
Self::Attributes { operand } => Self::Attributes {
operand: operand.deep_clone(),
},
Self::Indices { operand } => Self::Indices {
operand: operand.deep_clone(),
},
Self::InGroup { group } => Self::InGroup {
group: group.clone(),
},
Self::HasAttribute { attribute } => Self::HasAttribute {
attribute: attribute.clone(),
},
Self::SourceNode { operand } => Self::SourceNode {
operand: operand.deep_clone(),
},
Self::TargetNode { operand } => Self::TargetNode {
operand: operand.deep_clone(),
},
Self::EitherOr { either, or } => Self::EitherOr {
either: either.deep_clone(),
or: or.deep_clone(),
},
Self::Exclude { operand } => Self::Exclude {
operand: operand.deep_clone(),
},
Self::GroupBy { operand } => Self::GroupBy {
operand: operand.deep_clone(),
},
}
}
}
impl EdgeOperation {
pub(crate) fn evaluate<'a>(
&self,
medrecord: &'a MedRecord,
edge_indices: impl Iterator<Item = &'a EdgeIndex> + 'a,
) -> MedRecordResult<BoxedIterator<'a, &'a EdgeIndex>> {
Ok(match self {
Self::Values { operand } => {
Box::new(Self::evaluate_values(medrecord, edge_indices, operand)?)
}
Self::Attributes { operand } => {
Box::new(Self::evaluate_attributes(medrecord, edge_indices, operand)?)
}
Self::Indices { operand } => {
Box::new(Self::evaluate_indices(medrecord, edge_indices, operand)?)
}
Self::InGroup { group } => Box::new(Self::evaluate_in_group(
medrecord,
edge_indices,
group.clone(),
)),
Self::HasAttribute { attribute } => Box::new(Self::evaluate_has_attribute(
medrecord,
edge_indices,
attribute.clone(),
)),
Self::SourceNode { operand } => Box::new(Self::evaluate_source_node(
medrecord,
edge_indices,
operand,
)?),
Self::TargetNode { operand } => Box::new(Self::evaluate_target_node(
medrecord,
edge_indices,
operand,
)?),
Self::EitherOr { either, or } => {
let (edge_indices_1, rest) = edge_indices.tee();
let (edge_indices_2, edge_indices_3) = rest.tee();
let either_set: MrHashSet<_> = either
.evaluate_forward(medrecord, Box::new(edge_indices_1))?
.collect();
let or_set: MrHashSet<_> = or
.evaluate_forward(medrecord, Box::new(edge_indices_2))?
.collect();
Box::new(
edge_indices_3
.filter(move |edge| either_set.contains(edge) || or_set.contains(edge)),
)
}
Self::Exclude { operand } => {
let (edge_indices_1, edge_indices_2) = edge_indices.tee();
let result: MrHashSet<_> = operand
.evaluate_forward(medrecord, Box::new(edge_indices_1))?
.collect();
Box::new(edge_indices_2.filter(move |node_index| !result.contains(node_index)))
}
Self::GroupBy { operand } => {
Box::new(Self::evaluate_group_by(medrecord, edge_indices, operand)?)
}
})
}
#[inline]
pub(crate) fn get_values<'a>(
medrecord: &'a MedRecord,
edge_indices: impl Iterator<Item = &'a EdgeIndex>,
attribute: MedRecordAttribute,
) -> impl Iterator<Item = (&'a EdgeIndex, MedRecordValue)> {
edge_indices.flat_map(move |edge_index| {
Some((
edge_index,
medrecord
.edge_attributes(edge_index)
.expect("Edge must exist")
.get(&attribute)?
.clone(),
))
})
}
#[inline]
fn evaluate_values<'a>(
medrecord: &'a MedRecord,
edge_indices: impl Iterator<Item = &'a EdgeIndex> + 'a,
operand: &Wrapper<MultipleValuesWithIndexOperand<EdgeOperand>>,
) -> MedRecordResult<impl Iterator<Item = &'a EdgeIndex>> {
let MultipleValuesWithIndexContext::Operand((_, ref attribute)) =
operand.0.read_or_panic().context
else {
unreachable!()
};
let values = Self::get_values(medrecord, edge_indices, attribute.clone());
Ok(operand
.evaluate_forward(medrecord, Box::new(values))?
.map(|value| value.0))
}
#[inline]
pub(crate) fn get_attributes<'a>(
medrecord: &'a MedRecord,
edge_indices: impl Iterator<Item = &'a EdgeIndex>,
) -> impl Iterator<Item = (&'a EdgeIndex, Vec<MedRecordAttribute>)> {
edge_indices.map(move |edge_index| {
let attributes = medrecord
.edge_attributes(edge_index)
.expect("Edge must exist")
.keys()
.cloned();
(edge_index, attributes.collect())
})
}
#[inline]
fn evaluate_attributes<'a>(
medrecord: &'a MedRecord,
edge_indices: impl Iterator<Item = &'a EdgeIndex> + 'a,
operand: &Wrapper<AttributesTreeOperand<EdgeOperand>>,
) -> MedRecordResult<impl Iterator<Item = &'a EdgeIndex>> {
let attributes = Self::get_attributes(medrecord, edge_indices);
Ok(operand
.evaluate_forward(medrecord, Box::new(attributes))?
.map(|value| value.0))
}
#[inline]
fn evaluate_indices<'a>(
medrecord: &MedRecord,
edge_indices: impl Iterator<Item = &'a EdgeIndex>,
operand: &Wrapper<EdgeIndicesOperand>,
) -> MedRecordResult<impl Iterator<Item = &'a EdgeIndex>> {
let (edge_indices_1, edge_indices_2) = Itertools::tee(edge_indices);
let result: MrHashSet<_> = operand
.evaluate_forward(medrecord, Box::new(edge_indices_1.cloned()))?
.collect();
Ok(edge_indices_2
.into_iter()
.filter(move |index| result.contains(*index)))
}
#[inline]
fn evaluate_in_group<'a>(
medrecord: &'a MedRecord,
edge_indices: impl Iterator<Item = &'a EdgeIndex>,
group: CardinalityWrapper<Group>,
) -> impl Iterator<Item = &'a EdgeIndex> {
edge_indices.filter(move |edge_index| {
let groups_of_edge = medrecord
.groups_of_edge(edge_index)
.expect("Node must exist");
let groups_of_edge: MrHashSet<_> = groups_of_edge.collect();
match &group {
CardinalityWrapper::Single(group) => groups_of_edge.contains(&group),
CardinalityWrapper::Multiple(groups, match_mode) => {
if groups.is_empty() {
return false;
}
match match_mode {
MatchMode::Any => groups.iter().any(|group| groups_of_edge.contains(group)),
MatchMode::All => groups.iter().all(|group| groups_of_edge.contains(group)),
}
}
}
})
}
#[inline]
fn evaluate_has_attribute<'a>(
medrecord: &'a MedRecord,
edge_indices: impl Iterator<Item = &'a EdgeIndex>,
attribute: CardinalityWrapper<MedRecordAttribute>,
) -> impl Iterator<Item = &'a EdgeIndex> {
edge_indices.filter(move |edge_index| {
let attributes_of_edge = medrecord
.edge_attributes(edge_index)
.expect("Node must exist")
.keys();
let attributes_of_edge: MrHashSet<_> = attributes_of_edge.collect();
match &attribute {
CardinalityWrapper::Single(attribute) => attributes_of_edge.contains(&attribute),
CardinalityWrapper::Multiple(attributes, match_mode) => {
if attributes.is_empty() {
return false;
}
match match_mode {
MatchMode::Any => attributes
.iter()
.any(|attribute| attributes_of_edge.contains(&attribute)),
MatchMode::All => attributes
.iter()
.all(|attribute| attributes_of_edge.contains(&attribute)),
}
}
}
})
}
#[inline]
fn evaluate_source_node<'a>(
medrecord: &'a MedRecord,
edge_indices: impl Iterator<Item = &'a EdgeIndex> + 'a,
operand: &Wrapper<NodeOperand>,
) -> MedRecordResult<impl Iterator<Item = &'a EdgeIndex>> {
let (edge_indices_1, edge_indices_2) = Itertools::tee(edge_indices);
let node_indices = edge_indices_1.map(|edge_index| {
let edge_endpoints = medrecord
.edge_endpoints(edge_index)
.expect("Edge must exist");
edge_endpoints.0
});
let node_indices: MrHashSet<_> = operand
.evaluate_forward(medrecord, Box::new(node_indices))?
.collect();
Ok(edge_indices_2.filter(move |edge_index| {
let edge_endpoints = medrecord
.edge_endpoints(edge_index)
.expect("Edge must exist");
node_indices.contains(edge_endpoints.0)
}))
}
#[inline]
fn evaluate_target_node<'a>(
medrecord: &'a MedRecord,
edge_indices: impl Iterator<Item = &'a EdgeIndex> + 'a,
operand: &Wrapper<NodeOperand>,
) -> MedRecordResult<impl Iterator<Item = &'a EdgeIndex>> {
let (edge_indices_1, edge_indices_2) = Itertools::tee(edge_indices);
let node_indices = edge_indices_1.map(|edge_index| {
let edge_endpoints = medrecord
.edge_endpoints(edge_index)
.expect("Edge must exist");
edge_endpoints.1
});
let node_indices: MrHashSet<_> = operand
.evaluate_forward(medrecord, Box::new(node_indices))?
.collect();
Ok(edge_indices_2.filter(move |edge_index| {
let edge_endpoints = medrecord
.edge_endpoints(edge_index)
.expect("Edge must exist");
node_indices.contains(edge_endpoints.1)
}))
}
fn evaluate_group_by<'a>(
medrecord: &'a MedRecord,
edge_indices: impl Iterator<Item = &'a EdgeIndex> + 'a,
operand: &Wrapper<GroupOperand<EdgeOperand>>,
) -> MedRecordResult<impl Iterator<Item = &'a EdgeIndex>> {
Ok(EdgeOperand::merge(
operand.evaluate_forward(medrecord, Box::new(edge_indices))?,
))
}
}
impl EdgeOperation {
pub(crate) fn evaluate_grouped<'a>(
&self,
medrecord: &'a MedRecord,
edge_indices: GroupedIterator<'a, BoxedIterator<'a, &'a EdgeIndex>>,
) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, &'a EdgeIndex>>> {
Ok(match self {
EdgeOperation::Values { operand } => Box::new(Self::evaluate_values_grouped(
medrecord,
edge_indices,
operand,
)?),
EdgeOperation::Attributes { operand } => Box::new(Self::evaluate_attributes_grouped(
medrecord,
edge_indices,
operand,
)?),
EdgeOperation::Indices { operand } => Box::new(Self::evaluate_indices_grouped(
medrecord,
edge_indices,
operand,
)?),
EdgeOperation::InGroup { group } => {
let group = group.clone();
Box::new(edge_indices.map(move |(key, edge_indices)| {
(
key,
Box::new(Self::evaluate_in_group(
medrecord,
edge_indices,
group.clone(),
)) as BoxedIterator<_>,
)
}))
}
EdgeOperation::HasAttribute { attribute } => {
let attribute = attribute.clone();
Box::new(edge_indices.map(move |(key, edge_indices)| {
(
key,
Box::new(Self::evaluate_has_attribute(
medrecord,
edge_indices,
attribute.clone(),
)) as BoxedIterator<_>,
)
}))
}
EdgeOperation::SourceNode { operand } => Box::new(Self::evaluate_source_node_grouped(
medrecord,
edge_indices,
operand,
)?),
EdgeOperation::TargetNode { operand } => Box::new(Self::evaluate_target_node_grouped(
medrecord,
edge_indices,
operand,
)?),
EdgeOperation::EitherOr { either, or } => Box::new(Self::evaluate_either_or_grouped(
medrecord,
edge_indices,
either,
or,
)?),
EdgeOperation::Exclude { operand } => Box::new(Self::evaluate_exclude_grouped(
medrecord,
edge_indices,
operand,
)?),
EdgeOperation::GroupBy { operand: _ } => unreachable!(),
})
}
#[inline]
fn evaluate_values_grouped<'a>(
medrecord: &'a MedRecord,
edge_indices: GroupedIterator<'a, BoxedIterator<'a, &'a EdgeIndex>>,
operand: &Wrapper<MultipleValuesWithIndexOperand<EdgeOperand>>,
) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, &'a EdgeIndex>>> {
let MultipleValuesWithIndexContext::Operand((_, ref attribute)) =
operand.0.read_or_panic().context
else {
unreachable!()
};
let values: Vec<_> = edge_indices
.map(|(key, edge_indices)| {
(
key,
Box::new(Self::get_values(medrecord, edge_indices, attribute.clone()))
as <MultipleValuesWithIndexOperand<EdgeOperand> as EvaluateForward<
'a,
>>::InputValue,
)
})
.collect();
Ok(Box::new(
operand
.evaluate_forward_grouped(medrecord, Box::new(values.into_iter()))?
.map(|(key, values)| {
(
key,
Box::new(values.map(|value| value.0)) as BoxedIterator<_>,
)
}),
))
}
#[inline]
fn evaluate_attributes_grouped<'a>(
medrecord: &'a MedRecord,
edge_indices: GroupedIterator<'a, BoxedIterator<'a, &'a EdgeIndex>>,
operand: &Wrapper<AttributesTreeOperand<EdgeOperand>>,
) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, &'a EdgeIndex>>> {
let attributes = edge_indices.map(|(key, edge_indices)| {
(
key,
Box::new(Self::get_attributes(medrecord, edge_indices))
as <AttributesTreeOperand<EdgeOperand> as EvaluateForward<'a>>::InputValue,
)
});
Ok(Box::new(
operand
.evaluate_forward_grouped(medrecord, Box::new(attributes))?
.map(|(key, attributes)| {
(
key,
Box::new(attributes.map(|value| value.0)) as BoxedIterator<_>,
)
}),
))
}
#[inline]
fn evaluate_indices_grouped<'a>(
medrecord: &'a MedRecord,
edge_indices: GroupedIterator<'a, BoxedIterator<'a, &'a EdgeIndex>>,
operand: &Wrapper<EdgeIndicesOperand>,
) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, &'a EdgeIndex>>> {
let (edge_indices_1, edge_indices_2) = tee_grouped_iterator(edge_indices);
let edge_indices_1 = edge_indices_1
.map(|(key, edge_indices)| (key, Box::new(edge_indices.cloned()) as BoxedIterator<_>));
let mut edge_indices_1: Vec<_> = operand
.evaluate_forward_grouped(medrecord, Box::new(edge_indices_1))?
.collect();
Ok(Box::new(edge_indices_2.map(move |(key, edge_indices)| {
let edge_indices_position = &edge_indices_1
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let edge_indices_1: MrHashSet<_> =
edge_indices_1.remove(*edge_indices_position).1.collect();
let filtered_indices: Vec<_> = edge_indices
.filter(|edge_index| edge_indices_1.contains(*edge_index))
.collect();
(
key,
Box::new(filtered_indices.into_iter()) as BoxedIterator<_>,
)
})))
}
#[inline]
fn evaluate_source_node_grouped<'a>(
medrecord: &'a MedRecord,
edge_indices: GroupedIterator<'a, BoxedIterator<'a, &'a EdgeIndex>>,
operand: &Wrapper<NodeOperand>,
) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, &'a EdgeIndex>>> {
let (edge_indices_1, edge_indices_2) = tee_grouped_iterator(edge_indices);
let node_indices = edge_indices_1.map(|(key, edge_indices)| {
let node_indices: BoxedIterator<_> = Box::new(edge_indices.map(|edge_index| {
let edge_endpoints = medrecord
.edge_endpoints(edge_index)
.expect("Edge must exist");
edge_endpoints.0
}));
(key, node_indices)
});
let mut node_indices: Vec<_> = operand
.evaluate_forward_grouped(medrecord, Box::new(node_indices))?
.collect();
Ok(Box::new(edge_indices_2.map(move |(key, edge_indices)| {
let node_indices_position = &node_indices
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let node_indices: MrHashSet<_> =
node_indices.remove(*node_indices_position).1.collect();
let filtered_indices: Vec<_> = edge_indices
.filter(|edge_index| {
let edge_endpoints = medrecord
.edge_endpoints(edge_index)
.expect("Edge must exist");
node_indices.contains(edge_endpoints.0)
})
.collect();
(
key,
Box::new(filtered_indices.into_iter()) as BoxedIterator<_>,
)
})))
}
#[inline]
fn evaluate_target_node_grouped<'a>(
medrecord: &'a MedRecord,
edge_indices: GroupedIterator<'a, BoxedIterator<'a, &'a EdgeIndex>>,
operand: &Wrapper<NodeOperand>,
) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, &'a EdgeIndex>>> {
let (edge_indices_1, edge_indices_2) = tee_grouped_iterator(edge_indices);
let node_indices = edge_indices_1.map(|(key, edge_indices)| {
let node_indices: BoxedIterator<_> = Box::new(edge_indices.map(|edge_index| {
let edge_endpoints = medrecord
.edge_endpoints(edge_index)
.expect("Edge must exist");
edge_endpoints.1
}));
(key, node_indices)
});
let mut node_indices: Vec<_> = operand
.evaluate_forward_grouped(medrecord, Box::new(node_indices))?
.collect();
Ok(Box::new(edge_indices_2.map(move |(key, edge_indices)| {
let node_indices_position = &node_indices
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let node_indices: MrHashSet<_> =
node_indices.remove(*node_indices_position).1.collect();
let filtered_indices: Vec<_> = edge_indices
.filter(|edge_index| {
let edge_endpoints = medrecord
.edge_endpoints(edge_index)
.expect("Edge must exist");
node_indices.contains(edge_endpoints.1)
})
.collect();
(
key,
Box::new(filtered_indices.into_iter()) as BoxedIterator<_>,
)
})))
}
#[inline]
fn evaluate_either_or_grouped<'a>(
medrecord: &'a MedRecord,
edge_indices: GroupedIterator<'a, BoxedIterator<'a, &'a EdgeIndex>>,
either: &Wrapper<EdgeOperand>,
or: &Wrapper<EdgeOperand>,
) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, &'a EdgeIndex>>> {
let (edge_indices_1, edge_indices_2) = tee_grouped_iterator(edge_indices);
let either_indices = either.evaluate_forward_grouped(medrecord, edge_indices_1)?;
let mut or_indices: Vec<_> = or
.evaluate_forward_grouped(medrecord, edge_indices_2)?
.collect();
let edge_indices = either_indices.map(move |(key, either_indices)| {
let indices_position = or_indices
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let or_indices = or_indices.remove(indices_position).1;
let edge_indices: BoxedIterator<_> = Box::new(
either_indices
.chain(or_indices)
.unique_by(|edge_index| **edge_index),
);
(key, edge_indices)
});
Ok(Box::new(edge_indices))
}
#[inline]
fn evaluate_exclude_grouped<'a>(
medrecord: &'a MedRecord,
edge_indices: GroupedIterator<'a, BoxedIterator<'a, &'a EdgeIndex>>,
operand: &Wrapper<EdgeOperand>,
) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, &'a EdgeIndex>>> {
let (edge_indices_1, edge_indices_2) = tee_grouped_iterator(edge_indices);
let mut result: Vec<_> = operand
.evaluate_forward_grouped(medrecord, edge_indices_1)?
.collect();
let edge_indices = edge_indices_2.map(move |(key, values)| {
let indices_position = result
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let excluded_indices: MrHashSet<_> = result.remove(indices_position).1.collect();
let edge_indices: BoxedIterator<_> =
Box::new(values.filter(move |edge_index| !excluded_indices.contains(edge_index)));
(key, edge_indices)
});
Ok(Box::new(edge_indices))
}
}
#[derive(Debug, Clone)]
pub enum EdgeIndicesOperation {
EdgeIndexOperation {
operand: Wrapper<EdgeIndexOperand>,
},
EdgeIndexComparisonOperation {
operand: EdgeIndexComparisonOperand,
kind: SingleComparisonKind,
},
EdgeIndicesComparisonOperation {
operand: EdgeIndicesComparisonOperand,
kind: MultipleComparisonKind,
},
BinaryArithmeticOperation {
operand: EdgeIndexComparisonOperand,
kind: BinaryArithmeticKind,
},
IsMax,
IsMin,
EitherOr {
either: Wrapper<EdgeIndicesOperand>,
or: Wrapper<EdgeIndicesOperand>,
},
Exclude {
operand: Wrapper<EdgeIndicesOperand>,
},
Merge {
operand: Wrapper<EdgeIndicesOperand>,
},
}
impl DeepClone for EdgeIndicesOperation {
fn deep_clone(&self) -> Self {
match self {
Self::EdgeIndexOperation { operand } => Self::EdgeIndexOperation {
operand: operand.deep_clone(),
},
Self::EdgeIndexComparisonOperation { operand, kind } => {
Self::EdgeIndexComparisonOperation {
operand: operand.deep_clone(),
kind: kind.clone(),
}
}
Self::EdgeIndicesComparisonOperation { operand, kind } => {
Self::EdgeIndicesComparisonOperation {
operand: operand.deep_clone(),
kind: kind.clone(),
}
}
Self::BinaryArithmeticOperation { operand, kind } => Self::BinaryArithmeticOperation {
operand: operand.deep_clone(),
kind: kind.clone(),
},
Self::IsMax => Self::IsMax,
Self::IsMin => Self::IsMin,
Self::EitherOr { either, or } => Self::EitherOr {
either: either.deep_clone(),
or: or.deep_clone(),
},
Self::Exclude { operand } => Self::Exclude {
operand: operand.deep_clone(),
},
Self::Merge { operand } => Self::Merge {
operand: operand.deep_clone(),
},
}
}
}
impl EdgeIndicesOperation {
pub(crate) fn evaluate<'a>(
&self,
medrecord: &'a MedRecord,
indices: impl Iterator<Item = EdgeIndex> + 'a,
) -> MedRecordResult<BoxedIterator<'a, EdgeIndex>> {
Ok(match self {
Self::EdgeIndexOperation { operand } => {
Self::evaluate_edge_index_operation(medrecord, indices, operand)?
}
Self::EdgeIndexComparisonOperation { operand, kind } => {
Self::evaluate_edge_index_comparison_operation(medrecord, indices, operand, kind)?
}
Self::EdgeIndicesComparisonOperation { operand, kind } => {
Self::evaluate_edge_indices_comparison_operation(medrecord, indices, operand, kind)?
}
Self::BinaryArithmeticOperation { operand, kind } => Box::new(
Self::evaluate_binary_arithmetic_operation(medrecord, indices, operand, kind)?,
),
Self::IsMax => Self::evaluate_is_max(indices),
Self::IsMin => Self::evaluate_is_min(indices),
Self::EitherOr { either, or } => {
Self::evaluate_either_or(medrecord, indices, either, or)?
}
Self::Exclude { operand } => Self::evaluate_exclude(medrecord, indices, operand)?,
Self::Merge { operand: _ } => unreachable!(),
})
}
#[inline]
pub(crate) fn get_max(indices: impl Iterator<Item = EdgeIndex>) -> Option<EdgeIndex> {
indices.max()
}
#[inline]
pub(crate) fn get_min(indices: impl Iterator<Item = EdgeIndex>) -> Option<EdgeIndex> {
indices.min()
}
#[inline]
pub(crate) fn get_count(indices: impl Iterator<Item = EdgeIndex>) -> EdgeIndex {
indices.count() as EdgeIndex
}
#[inline]
pub(crate) fn get_sum(indices: impl Iterator<Item = EdgeIndex>) -> EdgeIndex {
indices.sum()
}
#[inline]
pub(crate) fn get_random(indices: impl Iterator<Item = EdgeIndex>) -> Option<EdgeIndex> {
indices.choose(&mut rng())
}
#[inline]
fn evaluate_edge_index_operation<'a>(
medrecord: &MedRecord,
indices: impl Iterator<Item = EdgeIndex> + 'a,
operand: &Wrapper<EdgeIndexOperand>,
) -> MedRecordResult<BoxedIterator<'a, EdgeIndex>> {
let (indices_1, indices_2) = Itertools::tee(indices);
let kind = &operand.0.read_or_panic().kind;
let index = match kind {
SingleKind::Max => EdgeIndicesOperation::get_max(indices_1),
SingleKind::Min => EdgeIndicesOperation::get_min(indices_1),
SingleKind::Count => Some(EdgeIndicesOperation::get_count(indices_1)),
SingleKind::Sum => Some(EdgeIndicesOperation::get_sum(indices_1)),
SingleKind::Random => EdgeIndicesOperation::get_random(indices_1),
};
Ok(match operand.evaluate_forward(medrecord, index)? {
Some(_) => Box::new(indices_2.into_iter()),
None => Box::new(std::iter::empty()),
})
}
#[inline]
fn evaluate_edge_index_comparison_operation<'a>(
medrecord: &MedRecord,
indices: impl Iterator<Item = EdgeIndex> + 'a,
comparison_operand: &EdgeIndexComparisonOperand,
kind: &SingleComparisonKind,
) -> MedRecordResult<BoxedIterator<'a, EdgeIndex>> {
let comparison_index =
comparison_operand
.evaluate_backward(medrecord)?
.ok_or(MedRecordError::QueryError(
"No index to compare".to_string(),
))?;
match kind {
SingleComparisonKind::GreaterThan => Ok(Box::new(
indices.filter(move |index| index > &comparison_index),
)),
SingleComparisonKind::GreaterThanOrEqualTo => Ok(Box::new(
indices.filter(move |index| index >= &comparison_index),
)),
SingleComparisonKind::LessThan => Ok(Box::new(
indices.filter(move |index| index < &comparison_index),
)),
SingleComparisonKind::LessThanOrEqualTo => Ok(Box::new(
indices.filter(move |index| index <= &comparison_index),
)),
SingleComparisonKind::EqualTo => Ok(Box::new(
indices.filter(move |index| index == &comparison_index),
)),
SingleComparisonKind::NotEqualTo => Ok(Box::new(
indices.filter(move |index| index != &comparison_index),
)),
SingleComparisonKind::StartsWith => Ok(Box::new(
indices.filter(move |index| index.starts_with(&comparison_index)),
)),
SingleComparisonKind::EndsWith => Ok(Box::new(
indices.filter(move |index| index.ends_with(&comparison_index)),
)),
SingleComparisonKind::Contains => Ok(Box::new(
indices.filter(move |index| index.contains(&comparison_index)),
)),
}
}
#[inline]
fn evaluate_edge_indices_comparison_operation<'a>(
medrecord: &MedRecord,
indices: impl Iterator<Item = EdgeIndex> + 'a,
comparison_operand: &EdgeIndicesComparisonOperand,
kind: &MultipleComparisonKind,
) -> MedRecordResult<BoxedIterator<'a, EdgeIndex>> {
let comparison_indices = comparison_operand.evaluate_backward(medrecord)?;
match kind {
MultipleComparisonKind::IsIn => Ok(Box::new(
indices.filter(move |index| comparison_indices.contains(index)),
)),
MultipleComparisonKind::IsNotIn => Ok(Box::new(
indices.filter(move |index| !comparison_indices.contains(index)),
)),
}
}
#[inline]
fn evaluate_binary_arithmetic_operation(
medrecord: &MedRecord,
indices: impl Iterator<Item = EdgeIndex>,
operand: &EdgeIndexComparisonOperand,
kind: &BinaryArithmeticKind,
) -> MedRecordResult<impl Iterator<Item = EdgeIndex>> {
let arithmetic_index =
operand
.evaluate_backward(medrecord)?
.ok_or(MedRecordError::QueryError(
"No index to compare".to_string(),
))?;
Ok(indices
.map(move |index| match kind {
BinaryArithmeticKind::Add => Ok(index.add(arithmetic_index)),
BinaryArithmeticKind::Sub => Ok(index.sub(arithmetic_index)),
BinaryArithmeticKind::Mul => Ok(index.mul(arithmetic_index)),
BinaryArithmeticKind::Pow => Ok(index.pow(arithmetic_index)),
BinaryArithmeticKind::Mod => index.r#mod(arithmetic_index),
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter())
}
#[inline]
fn evaluate_is_max<'a>(
indices: impl Iterator<Item = EdgeIndex> + 'a,
) -> BoxedIterator<'a, EdgeIndex> {
let (indices_1, indices_2) = Itertools::tee(indices);
let max_index = Self::get_max(indices_1);
let Some(max_index) = max_index else {
return Box::new(std::iter::empty());
};
Box::new(indices_2.filter(move |index| *index == max_index))
}
#[inline]
fn evaluate_is_min<'a>(
indices: impl Iterator<Item = EdgeIndex> + 'a,
) -> BoxedIterator<'a, EdgeIndex> {
let (indices_1, indices_2) = Itertools::tee(indices);
let min_index = Self::get_min(indices_1);
let Some(min_index) = min_index else {
return Box::new(std::iter::empty());
};
Box::new(indices_2.filter(move |index| *index == min_index))
}
#[inline]
fn evaluate_either_or<'a>(
medrecord: &'a MedRecord,
indices: impl Iterator<Item = EdgeIndex> + 'a,
either: &Wrapper<EdgeIndicesOperand>,
or: &Wrapper<EdgeIndicesOperand>,
) -> MedRecordResult<BoxedIterator<'a, EdgeIndex>> {
let (indices_1, indices_2) = Itertools::tee(indices);
let either_indices = either.evaluate_forward(medrecord, Box::new(indices_1))?;
let or_indices = or.evaluate_forward(medrecord, Box::new(indices_2))?;
Ok(Box::new(either_indices.chain(or_indices).unique()))
}
#[inline]
fn evaluate_exclude<'a>(
medrecord: &'a MedRecord,
indices: impl Iterator<Item = EdgeIndex> + 'a,
operand: &Wrapper<EdgeIndicesOperand>,
) -> MedRecordResult<BoxedIterator<'a, EdgeIndex>> {
let (indices_1, indices_2) = Itertools::tee(indices);
let result: MrHashSet<_> = operand
.evaluate_forward(medrecord, Box::new(indices_1))?
.collect();
Ok(Box::new(
indices_2.filter(move |index| !result.contains(index)),
))
}
}
impl EdgeIndicesOperation {
pub(crate) fn evaluate_grouped<'a>(
&self,
medrecord: &'a MedRecord,
edge_indices: GroupedIterator<'a, BoxedIterator<'a, EdgeIndex>>,
) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, EdgeIndex>>> {
Ok(match self {
EdgeIndicesOperation::EdgeIndexOperation { operand } => Box::new(
Self::evaluate_edge_index_operation_grouped(medrecord, edge_indices, operand)?,
),
EdgeIndicesOperation::EdgeIndexComparisonOperation { operand, kind } => Box::new(
edge_indices
.map(move |(key, edge_indices)| {
Ok((
key,
Box::new(Self::evaluate_edge_index_comparison_operation(
medrecord,
edge_indices,
operand,
kind,
)?) as BoxedIterator<_>,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
EdgeIndicesOperation::EdgeIndicesComparisonOperation { operand, kind } => Box::new(
edge_indices
.map(move |(key, edge_indices)| {
Ok((
key,
Box::new(Self::evaluate_edge_indices_comparison_operation(
medrecord,
edge_indices,
operand,
kind,
)?) as BoxedIterator<_>,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
EdgeIndicesOperation::BinaryArithmeticOperation { operand, kind } => Box::new(
edge_indices
.map(move |(key, edge_indices)| {
Ok((
key,
Box::new(Self::evaluate_binary_arithmetic_operation(
medrecord,
edge_indices,
operand,
kind,
)?) as BoxedIterator<_>,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
EdgeIndicesOperation::IsMax => Box::new(
edge_indices
.map(move |(key, edge_indices)| (key, Self::evaluate_is_max(edge_indices))),
),
EdgeIndicesOperation::IsMin => Box::new(
edge_indices
.map(move |(key, edge_indices)| (key, Self::evaluate_is_min(edge_indices))),
),
EdgeIndicesOperation::EitherOr { either, or } => Box::new(
Self::evaluate_either_or_grouped(medrecord, edge_indices, either, or)?,
),
EdgeIndicesOperation::Exclude { operand } => Box::new(Self::evaluate_exclude_grouped(
medrecord,
edge_indices,
operand,
)?),
EdgeIndicesOperation::Merge { operand } => {
let (edge_indices_1, edge_indices_2) = tee_grouped_iterator(edge_indices);
let edge_indices_1 = edge_indices_1.flat_map(|(_, value)| value);
let edge_indinces_1: MrHashSet<_> = operand
.evaluate_forward(medrecord, Box::new(edge_indices_1))?
.collect();
Box::new(edge_indices_2.map(move |(key, edge_indices)| {
let edge_indices: Vec<_> = edge_indices
.filter(|edge_index| edge_indinces_1.contains(edge_index))
.collect();
let edge_indices: BoxedIterator<_> = Box::new(edge_indices.into_iter());
(key, edge_indices)
}))
}
})
}
#[inline]
fn evaluate_edge_index_operation_grouped<'a>(
medrecord: &'a MedRecord,
edge_indices: GroupedIterator<'a, BoxedIterator<'a, EdgeIndex>>,
operand: &Wrapper<EdgeIndexOperand>,
) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, EdgeIndex>>> {
let (edge_indices_1, edge_indices_2) = tee_grouped_iterator(edge_indices);
let mut edge_indices_2: Vec<_> = edge_indices_2.collect();
let kind = &operand.0.read_or_panic().kind;
let edge_indices_1: Vec<_> = edge_indices_1
.map(move |(key, edge_indices)| {
Ok((
key,
match kind {
SingleKind::Max => EdgeIndicesOperation::get_max(edge_indices),
SingleKind::Min => EdgeIndicesOperation::get_min(edge_indices),
SingleKind::Count => Some(EdgeIndicesOperation::get_count(edge_indices)),
SingleKind::Sum => Some(EdgeIndicesOperation::get_sum(edge_indices)),
SingleKind::Random => EdgeIndicesOperation::get_random(edge_indices),
},
))
})
.collect::<MedRecordResult<_>>()?;
let edge_indices_1 =
operand.evaluate_forward_grouped(medrecord, Box::new(edge_indices_1.into_iter()))?;
Ok(Box::new(edge_indices_1.map(
move |(key, value)| match value {
Some(_) => {
let edge_indices_position = edge_indices_2
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
edge_indices_2.remove(edge_indices_position)
}
None => (key, Box::new(std::iter::empty()) as BoxedIterator<_>),
},
)))
}
#[inline]
fn evaluate_either_or_grouped<'a>(
medrecord: &'a MedRecord,
edge_indices: GroupedIterator<'a, BoxedIterator<'a, EdgeIndex>>,
either: &Wrapper<EdgeIndicesOperand>,
or: &Wrapper<EdgeIndicesOperand>,
) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, EdgeIndex>>> {
let (edge_indices_1, edge_indices_2) = tee_grouped_iterator(edge_indices);
let either_indices = either.evaluate_forward_grouped(medrecord, edge_indices_1)?;
let mut or_indices: Vec<_> = or
.evaluate_forward_grouped(medrecord, edge_indices_2)?
.collect();
let edge_indices = either_indices.map(move |(key, either_indices)| {
let indices_position = or_indices
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let or_indices = or_indices.remove(indices_position).1;
let edge_indices: BoxedIterator<_> = Box::new(
either_indices
.chain(or_indices)
.unique_by(|edge_index| *edge_index),
);
(key, edge_indices)
});
Ok(Box::new(edge_indices))
}
#[inline]
fn evaluate_exclude_grouped<'a>(
medrecord: &'a MedRecord,
edge_indices: GroupedIterator<'a, BoxedIterator<'a, EdgeIndex>>,
operand: &Wrapper<EdgeIndicesOperand>,
) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, EdgeIndex>>> {
let (edge_indices_1, edge_indices_2) = tee_grouped_iterator(edge_indices);
let mut result: Vec<_> = operand
.evaluate_forward_grouped(medrecord, edge_indices_1)?
.collect();
let edge_indices = edge_indices_2.map(move |(key, values)| {
let indices_position = result
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let excluded_indices: MrHashSet<_> = result.remove(indices_position).1.collect();
let edge_indices: BoxedIterator<_> =
Box::new(values.filter(move |edge_index| !excluded_indices.contains(edge_index)));
(key, edge_indices)
});
Ok(Box::new(edge_indices))
}
}
#[derive(Debug, Clone)]
pub enum EdgeIndexOperation {
EdgeIndexComparisonOperation {
operand: EdgeIndexComparisonOperand,
kind: SingleComparisonKind,
},
EdgeIndicesComparisonOperation {
operand: EdgeIndicesComparisonOperand,
kind: MultipleComparisonKind,
},
BinaryArithmeticOperation {
operand: EdgeIndexComparisonOperand,
kind: BinaryArithmeticKind,
},
EitherOr {
either: Wrapper<EdgeIndexOperand>,
or: Wrapper<EdgeIndexOperand>,
},
Exclude {
operand: Wrapper<EdgeIndexOperand>,
},
Merge {
operand: Wrapper<EdgeIndicesOperand>,
},
}
impl DeepClone for EdgeIndexOperation {
fn deep_clone(&self) -> Self {
match self {
Self::EdgeIndexComparisonOperation { operand, kind } => {
Self::EdgeIndexComparisonOperation {
operand: operand.deep_clone(),
kind: kind.clone(),
}
}
Self::EdgeIndicesComparisonOperation { operand, kind } => {
Self::EdgeIndicesComparisonOperation {
operand: operand.deep_clone(),
kind: kind.clone(),
}
}
Self::BinaryArithmeticOperation { operand, kind } => Self::BinaryArithmeticOperation {
operand: operand.deep_clone(),
kind: kind.clone(),
},
Self::EitherOr { either, or } => Self::EitherOr {
either: either.deep_clone(),
or: or.deep_clone(),
},
Self::Exclude { operand } => Self::Exclude {
operand: operand.deep_clone(),
},
Self::Merge { operand } => Self::Merge {
operand: operand.deep_clone(),
},
}
}
}
impl EdgeIndexOperation {
pub(crate) fn evaluate(
&self,
medrecord: &MedRecord,
index: Option<EdgeIndex>,
) -> MedRecordResult<Option<EdgeIndex>> {
let Some(index) = index else {
return Ok(None);
};
match self {
Self::EdgeIndexComparisonOperation { operand, kind } => {
Self::evaluate_edge_index_comparison_operation(medrecord, index, operand, kind)
}
Self::EdgeIndicesComparisonOperation { operand, kind } => {
Self::evaluate_edge_indcies_comparison_operation(medrecord, index, operand, kind)
}
Self::BinaryArithmeticOperation { operand, kind } => {
Self::evaluate_binary_arithmetic_operation(medrecord, index, operand, kind)
}
Self::EitherOr { either, or } => Self::evaluate_either_or(medrecord, index, either, or),
Self::Exclude { operand } => {
let result = operand.evaluate_forward(medrecord, Some(index))?.is_some();
Ok(if result { None } else { Some(index) })
}
Self::Merge { operand: _ } => unreachable!(),
}
}
#[inline]
fn evaluate_edge_index_comparison_operation(
medrecord: &MedRecord,
index: EdgeIndex,
comparison_operand: &EdgeIndexComparisonOperand,
kind: &SingleComparisonKind,
) -> MedRecordResult<Option<EdgeIndex>> {
let comparison_index =
comparison_operand
.evaluate_backward(medrecord)?
.ok_or(MedRecordError::QueryError(
"No index to compare".to_string(),
))?;
let comparison_result = match kind {
SingleComparisonKind::GreaterThan => index > comparison_index,
SingleComparisonKind::GreaterThanOrEqualTo => index >= comparison_index,
SingleComparisonKind::LessThan => index < comparison_index,
SingleComparisonKind::LessThanOrEqualTo => index <= comparison_index,
SingleComparisonKind::EqualTo => index == comparison_index,
SingleComparisonKind::NotEqualTo => index != comparison_index,
SingleComparisonKind::StartsWith => index.starts_with(&comparison_index),
SingleComparisonKind::EndsWith => index.ends_with(&comparison_index),
SingleComparisonKind::Contains => index.contains(&comparison_index),
};
Ok(if comparison_result { Some(index) } else { None })
}
#[inline]
fn evaluate_edge_indcies_comparison_operation(
medrecord: &MedRecord,
index: EdgeIndex,
comparison_operand: &EdgeIndicesComparisonOperand,
kind: &MultipleComparisonKind,
) -> MedRecordResult<Option<EdgeIndex>> {
let comparison_indices = comparison_operand.evaluate_backward(medrecord)?;
let comparison_result = match kind {
MultipleComparisonKind::IsIn => comparison_indices.contains(&index),
MultipleComparisonKind::IsNotIn => !comparison_indices.contains(&index),
};
Ok(if comparison_result { Some(index) } else { None })
}
#[inline]
fn evaluate_binary_arithmetic_operation(
medrecord: &MedRecord,
index: EdgeIndex,
operand: &EdgeIndexComparisonOperand,
kind: &BinaryArithmeticKind,
) -> MedRecordResult<Option<EdgeIndex>> {
let arithmetic_index =
operand
.evaluate_backward(medrecord)?
.ok_or(MedRecordError::QueryError(
"No index to compare".to_string(),
))?;
Ok(Some(match kind {
BinaryArithmeticKind::Add => index.add(arithmetic_index),
BinaryArithmeticKind::Sub => index.sub(arithmetic_index),
BinaryArithmeticKind::Mul => index.mul(arithmetic_index),
BinaryArithmeticKind::Pow => index.pow(arithmetic_index),
BinaryArithmeticKind::Mod => index.r#mod(arithmetic_index)?,
}))
}
#[inline]
fn evaluate_either_or(
medrecord: &MedRecord,
index: EdgeIndex,
either: &Wrapper<EdgeIndexOperand>,
or: &Wrapper<EdgeIndexOperand>,
) -> MedRecordResult<Option<EdgeIndex>> {
let either_result = either.evaluate_forward(medrecord, Some(index))?;
let or_result = or.evaluate_forward(medrecord, Some(index))?;
match (either_result, or_result) {
(Some(either_result), _) => Ok(Some(either_result)),
(None, Some(or_result)) => Ok(Some(or_result)),
_ => Ok(None),
}
}
}
impl EdgeIndexOperation {
pub(crate) fn evaluate_grouped<'a>(
&self,
medrecord: &'a MedRecord,
edge_indices: GroupedIterator<'a, Option<EdgeIndex>>,
) -> MedRecordResult<GroupedIterator<'a, Option<EdgeIndex>>> {
Ok(match self {
EdgeIndexOperation::EdgeIndexComparisonOperation { operand, kind } => Box::new(
edge_indices
.map(move |(key, edge_index)| {
let Some(edge_index) = edge_index else {
return Ok((key, None));
};
Ok((
key,
Self::evaluate_edge_index_comparison_operation(
medrecord, edge_index, operand, kind,
)?,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
EdgeIndexOperation::EdgeIndicesComparisonOperation { operand, kind } => Box::new(
edge_indices
.map(move |(key, edge_index)| {
let Some(edge_index) = edge_index else {
return Ok((key, None));
};
Ok((
key,
Self::evaluate_edge_indcies_comparison_operation(
medrecord, edge_index, operand, kind,
)?,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
EdgeIndexOperation::BinaryArithmeticOperation { operand, kind } => Box::new(
edge_indices
.map(move |(key, edge_index)| {
let Some(edge_index) = edge_index else {
return Ok((key, None));
};
Ok((
key,
Self::evaluate_binary_arithmetic_operation(
medrecord, edge_index, operand, kind,
)?,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
EdgeIndexOperation::EitherOr { either, or } => {
Self::evaluate_either_or_grouped(medrecord, edge_indices, either, or)?
}
EdgeIndexOperation::Exclude { operand } => {
Self::evaluate_exclude_grouped(medrecord, edge_indices, operand)?
}
EdgeIndexOperation::Merge { operand } => {
let (edge_indices_1, edge_indices_2) = Itertools::tee(edge_indices);
let edge_indices_1 = edge_indices_1.filter_map(|(_, indices)| indices);
let edge_indices_1: MrHashSet<_> = operand
.evaluate_forward(medrecord, Box::new(edge_indices_1))?
.collect();
Box::new(edge_indices_2.map(move |(key, edge_index)| {
let edge_index = edge_index.filter(|index| edge_indices_1.contains(index));
(key, edge_index)
}))
}
})
}
#[inline]
fn evaluate_either_or_grouped<'a>(
medrecord: &'a MedRecord,
edge_indices: GroupedIterator<'a, Option<EdgeIndex>>,
either: &Wrapper<EdgeIndexOperand>,
or: &Wrapper<EdgeIndexOperand>,
) -> MedRecordResult<GroupedIterator<'a, Option<EdgeIndex>>> {
let (edge_indices_1, edge_indices_2) = Itertools::tee(edge_indices);
let either_indices =
either.evaluate_forward_grouped(medrecord, Box::new(edge_indices_1))?;
let mut or_indices: Vec<_> = or
.evaluate_forward_grouped(medrecord, Box::new(edge_indices_2))?
.collect();
let edge_indices = either_indices.map(move |(key, either_indices)| {
let indices_position = or_indices
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let or_index = or_indices.remove(indices_position).1;
let index = match (either_indices, or_index) {
(Some(either_result), _) => Some(either_result),
(None, Some(or_result)) => Some(or_result),
_ => None,
};
(key, index)
});
Ok(Box::new(edge_indices))
}
#[inline]
fn evaluate_exclude_grouped<'a>(
medrecord: &'a MedRecord,
edge_indices: GroupedIterator<'a, Option<EdgeIndex>>,
operand: &Wrapper<EdgeIndexOperand>,
) -> MedRecordResult<GroupedIterator<'a, Option<EdgeIndex>>> {
let (edge_indices_1, edge_indices_2) = Itertools::tee(edge_indices);
let mut result: Vec<_> = operand
.evaluate_forward_grouped(medrecord, Box::new(edge_indices_1))?
.collect();
let edge_indices = edge_indices_2.map(move |(key, edge_index)| {
let index_position = result
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let excluded_index = result.remove(index_position).1;
let edge_index = match excluded_index {
Some(_) => None,
None => edge_index,
};
(key, edge_index)
});
Ok(Box::new(edge_indices))
}
}