use std::hash::Hash;
use crate::stream::collection_extract::CollectionExtract;
use crate::stream::collector::{Accumulator, Collector};
use crate::stream::relational::operator::Operator;
use super::indexes::remove_index_from_group_bucket;
use super::state::{ComplementedGroupedNodeState, MatchRow};
impl<S, A, B, T, JK, GK, EA, EB, ET, KA, KB, F, GF, KT, C, V, R, Acc, D>
ComplementedGroupedNodeState<S, A, B, T, JK, GK, EA, EB, ET, KA, KB, F, GF, KT, C, V, R, Acc, D>
where
S: Send + Sync + 'static,
A: 'static,
B: 'static,
T: Send + Sync + 'static,
JK: Eq + Hash + Clone + Send + Sync + 'static,
GK: Eq + Hash + Send + Sync,
EA: CollectionExtract<S, Item = A> + 'static,
EB: CollectionExtract<S, Item = B> + 'static,
ET: CollectionExtract<S, Item = T> + 'static,
KA: Fn(&A) -> JK + Send + Sync + 'static,
KB: Fn(&B) -> JK + Send + Sync + 'static,
F: Fn(&S, &A, &B, usize, usize) -> bool + Send + Sync,
GF: Fn(&A, &B) -> GK + Send + Sync,
KT: Fn(&T) -> GK + Send + Sync,
C: for<'i> Collector<(&'i A, &'i B), Value = V, Result = R, Accumulator = Acc> + Send + Sync,
V: Send + Sync,
R: Send + Sync,
Acc: Accumulator<V, R> + Send + Sync,
D: Fn(&T) -> R + Send + Sync,
{
pub fn evaluation_state(
&self,
solution: &S,
) -> super::state::ComplementedGroupedEvaluationState<GK, V, R, Acc> {
let mut groups = std::collections::HashMap::<GK, Acc>::new();
self.join.visit_all(solution, &mut |row| {
let a = row.left.entity;
let b = row.right.entity;
let (a_idx, b_idx) = (row.left.index, row.right.index);
if !(self.filter)(solution, a, b, a_idx, b_idx) {
return;
}
let key = (self.group_key_fn)(a, b);
let value = self.collector.extract((a, b));
groups
.entry(key)
.or_insert_with(|| self.collector.create_accumulator())
.accumulate(value);
});
let mut targets = Vec::new();
for target in self.extractor_t.extract(solution) {
if self.extractor_t.contains(solution, target) {
targets.push(((self.key_t)(target), (self.default_fn)(target)));
}
}
super::state::ComplementedGroupedEvaluationState {
groups,
targets,
_phantom: std::marker::PhantomData,
}
}
pub fn initialize(&mut self, solution: &S) {
self.reset();
self.join.initialize(solution);
let handles = self.join.handles();
for handle in handles {
let Some(row) = self.join.resolve(solution, handle) else {
continue;
};
let (a_idx, b_idx) = (row.left.index, row.right.index);
if (self.filter)(solution, row.left.entity, row.right.entity, a_idx, b_idx) {
self.retain(solution, a_idx, b_idx);
}
}
let entities_t = self.extractor_t.extract(solution);
for t_idx in 0..entities_t.len() {
self.insert_complement(solution, entities_t, t_idx);
}
self.changed_groups.clear();
self.changed_complements.clear();
}
pub(super) fn retain(&mut self, solution: &S, a_idx: usize, b_idx: usize) {
let (group_key, value) = {
let a = &self.join.left().extractor().extract(solution)[a_idx];
let b = &self.join.right().extractor().extract(solution)[b_idx];
((self.group_key_fn)(a, b), self.collector.extract((a, b)))
};
let (group_id, retraction) = self.insert_value(group_key, value);
let row_idx = self.match_rows.len();
self.a_to_matches.entry(a_idx).or_default().push(row_idx);
self.b_to_matches.entry(b_idx).or_default().push(row_idx);
self.match_rows.push(MatchRow {
pair: (a_idx, b_idx),
group_id,
retraction,
});
}
pub fn on_insert(
&mut self,
solution: &S,
entity_index: usize,
descriptor_index: usize,
node_name: &str,
) {
self.changed_groups.clear();
self.changed_complements.clear();
let a_changed = self.a_source.assert_localizes(descriptor_index, node_name);
let b_changed = self.b_source.assert_localizes(descriptor_index, node_name);
let t_changed = self.t_source.assert_localizes(descriptor_index, node_name);
if !a_changed && !b_changed && !t_changed {
return;
}
if a_changed {
self.retract_by_a(entity_index);
}
if b_changed {
self.retract_by_b(entity_index);
}
if a_changed || b_changed {
let changes = self.join.insert(solution, descriptor_index, entity_index);
for handle in changes.inserted {
if let Some(row) = self.join.resolve(solution, handle) {
let (a_idx, b_idx) = (row.left.index, row.right.index);
if (self.filter)(solution, row.left.entity, row.right.entity, a_idx, b_idx) {
self.retain(solution, a_idx, b_idx);
}
}
}
}
if t_changed {
let entities_t = self.extractor_t.extract(solution);
self.insert_complement(solution, entities_t, entity_index);
}
self.update_count += 1;
self.changed_key_count += self.changed_groups.len();
}
pub fn on_retract(
&mut self,
solution: &S,
entity_index: usize,
descriptor_index: usize,
node_name: &str,
) {
self.changed_groups.clear();
self.changed_complements.clear();
let a_changed = self.a_source.assert_localizes(descriptor_index, node_name);
let b_changed = self.b_source.assert_localizes(descriptor_index, node_name);
let t_changed = self.t_source.assert_localizes(descriptor_index, node_name);
if !a_changed && !b_changed && !t_changed {
return;
}
if a_changed {
self.retract_by_a(entity_index);
}
if b_changed {
self.retract_by_b(entity_index);
}
if t_changed {
self.retract_complement(entity_index);
}
if a_changed || b_changed {
self.join.retract(solution, descriptor_index, entity_index);
}
self.update_count += 1;
self.changed_key_count += self.changed_groups.len();
}
pub fn reset(&mut self) {
self.join.clear();
self.match_rows.clear();
self.a_to_matches.clear();
self.b_to_matches.clear();
self.t_by_group.clear();
self.t_index_to_group.clear();
self.t_defaults.clear();
self.groups.clear();
self.groups_by_hash.clear();
self.changed_groups.clear();
self.changed_complements.clear();
}
pub fn update_count(&self) -> usize {
self.update_count
}
pub fn changed_key_count(&self) -> usize {
self.changed_key_count
}
pub(super) fn retract_by_a(&mut self, a_idx: usize) {
while let Some(row_idx) = self
.a_to_matches
.get(&a_idx)
.and_then(|bucket| bucket.last())
.copied()
{
self.remove_match(row_idx);
}
}
pub(super) fn retract_by_b(&mut self, b_idx: usize) {
while let Some(row_idx) = self
.b_to_matches
.get(&b_idx)
.and_then(|bucket| bucket.last())
.copied()
{
self.remove_match(row_idx);
}
}
pub(super) fn remove_match(&mut self, row_idx: usize) {
if row_idx >= self.match_rows.len() {
return;
}
let last_idx = self.match_rows.len() - 1;
let removed = self.match_rows.swap_remove(row_idx);
Self::remove_from_bucket(&mut self.a_to_matches, removed.pair.0, row_idx);
Self::remove_from_bucket(&mut self.b_to_matches, removed.pair.1, row_idx);
if row_idx != last_idx {
let moved_pair = self.match_rows[row_idx].pair;
Self::replace_position(&mut self.a_to_matches, moved_pair.0, last_idx, row_idx);
Self::replace_position(&mut self.b_to_matches, moved_pair.1, last_idx, row_idx);
}
self.retract_value(removed.group_id, removed.retraction);
}
fn remove_from_bucket(
buckets: &mut std::collections::HashMap<usize, Vec<usize>>,
key: usize,
row_idx: usize,
) {
let mut empty = false;
if let Some(bucket) = buckets.get_mut(&key) {
if let Some(position) = bucket.iter().position(|held| *held == row_idx) {
bucket.swap_remove(position);
}
empty = bucket.is_empty();
}
if empty {
buckets.remove(&key);
}
}
fn replace_position(
buckets: &mut std::collections::HashMap<usize, Vec<usize>>,
key: usize,
from: usize,
to: usize,
) {
if let Some(bucket) = buckets.get_mut(&key) {
for held in bucket.iter_mut() {
if *held == from {
*held = to;
}
}
}
}
pub(super) fn index_complement(&mut self, group_id: usize, t_idx: usize) {
if let Some(old_group_id) = self.t_index_to_group.insert(t_idx, group_id) {
remove_index_from_group_bucket(&mut self.t_by_group, old_group_id, t_idx);
self.mark_changed(old_group_id);
}
self.t_by_group.entry(group_id).or_default().push(t_idx);
self.mark_complement_changed(t_idx);
self.mark_changed(group_id);
}
pub(super) fn insert_complement(&mut self, solution: &S, entities_t: &[T], t_idx: usize) {
if t_idx >= entities_t.len() {
return;
}
let complement = &entities_t[t_idx];
if !self.extractor_t.contains(solution, complement) {
return;
}
let key = (self.key_t)(complement);
let default_result = (self.default_fn)(complement);
let group_id = self.group_id_for_key(key);
self.t_defaults.insert(t_idx, default_result);
self.index_complement(group_id, t_idx);
}
pub(super) fn retract_complement(&mut self, t_idx: usize) {
let Some(group_id) = self.t_index_to_group.remove(&t_idx) else {
return;
};
self.t_defaults.remove(&t_idx);
remove_index_from_group_bucket(&mut self.t_by_group, group_id, t_idx);
self.mark_complement_changed(t_idx);
self.mark_changed(group_id);
}
}