use crate::domain::prefix_sum::ChunkStartState;
use crate::domain::types::{Boundary, BoundaryCandidate, DeltaVec, PartialState};
use rayon::prelude::*;
pub struct BoundaryReducer;
impl BoundaryReducer {
pub fn evaluate_candidates(
candidates: &[BoundaryCandidate],
chunk_start: &ChunkStartState,
) -> Vec<Boundary> {
Self::evaluate_candidates_with_symmetry(candidates, chunk_start, &[])
}
pub fn evaluate_candidates_with_symmetry(
candidates: &[BoundaryCandidate],
chunk_start: &ChunkStartState,
symmetric_types: &[bool],
) -> Vec<Boundary> {
candidates
.iter()
.filter_map(|candidate| {
let all_outside =
candidate
.local_depths
.iter()
.enumerate()
.all(|(i, &local_depth)| {
let global_depth = chunk_start.cumulative_deltas[i].net + local_depth;
if symmetric_types.get(i).copied().unwrap_or(false) {
global_depth % 2 == 0
} else {
global_depth <= 0
}
});
if all_outside {
Some(Boundary {
offset: chunk_start.global_offset + candidate.local_offset,
flags: candidate.flags,
})
} else {
None
}
})
.collect()
}
pub fn reduce_all(states: &[PartialState], chunk_starts: &[ChunkStartState]) -> Vec<Boundary> {
Self::reduce_all_with_symmetry(states, chunk_starts, &[])
}
pub fn reduce_all_with_symmetry(
states: &[PartialState],
chunk_starts: &[ChunkStartState],
symmetric_types: &[bool],
) -> Vec<Boundary> {
assert_eq!(states.len(), chunk_starts.len());
let mut boundaries: Vec<Boundary> = states
.par_iter()
.zip(chunk_starts.par_iter())
.flat_map(|(state, chunk_start)| {
Self::evaluate_candidates_with_symmetry(
&state.boundary_candidates,
chunk_start,
symmetric_types,
)
})
.collect();
boundaries.sort_by_key(|b| b.offset);
boundaries.dedup_by_key(|b| b.offset);
boundaries
}
pub fn reduce_single(state: &PartialState) -> Vec<Boundary> {
Self::reduce_single_with_symmetry(state, &[])
}
pub fn reduce_single_with_symmetry(
state: &PartialState,
symmetric_types: &[bool],
) -> Vec<Boundary> {
let chunk_start = ChunkStartState {
cumulative_deltas: DeltaVec::from_vec(vec![
crate::domain::types::DeltaEntry {
net: 0,
min: 0
};
state.deltas.len()
]),
global_offset: 0,
};
Self::evaluate_candidates_with_symmetry(
&state.boundary_candidates,
&chunk_start,
symmetric_types,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::domain::types::DeltaEntry;
use crate::domain::types::{BoundaryVec, DepthVec};
use crate::domain::BoundaryFlags;
#[test]
fn test_evaluate_candidates_all_zero_depths() {
let candidates = vec![
BoundaryCandidate {
local_offset: 10,
local_depths: DepthVec::from_vec(vec![0, 0]),
flags: BoundaryFlags::WEAK,
},
BoundaryCandidate {
local_offset: 25,
local_depths: DepthVec::from_vec(vec![0, 0]),
flags: BoundaryFlags::STRONG,
},
];
let chunk_start = ChunkStartState {
cumulative_deltas: DeltaVec::from_vec(vec![
DeltaEntry { net: 0, min: 0 },
DeltaEntry { net: 0, min: 0 },
]),
global_offset: 100,
};
let boundaries = BoundaryReducer::evaluate_candidates(&candidates, &chunk_start);
assert_eq!(boundaries.len(), 2);
assert_eq!(boundaries[0].offset, 110); assert_eq!(boundaries[1].offset, 125); }
#[test]
fn test_evaluate_candidates_inside_enclosure() {
let candidates = vec![
BoundaryCandidate {
local_offset: 10,
local_depths: DepthVec::from_vec(vec![1]), flags: BoundaryFlags::WEAK,
},
BoundaryCandidate {
local_offset: 25,
local_depths: DepthVec::from_vec(vec![0]), flags: BoundaryFlags::STRONG,
},
];
let chunk_start = ChunkStartState {
cumulative_deltas: DeltaVec::from_vec(vec![DeltaEntry { net: 0, min: 0 }]),
global_offset: 0,
};
let boundaries = BoundaryReducer::evaluate_candidates(&candidates, &chunk_start);
assert_eq!(boundaries.len(), 1);
assert_eq!(boundaries[0].offset, 25);
}
#[test]
fn test_evaluate_candidates_with_cumulative_depth() {
let candidates = vec![BoundaryCandidate {
local_offset: 10,
local_depths: DepthVec::from_vec(vec![-1]), flags: BoundaryFlags::WEAK,
}];
let chunk_start = ChunkStartState {
cumulative_deltas: DeltaVec::from_vec(vec![DeltaEntry { net: 1, min: 0 }]), global_offset: 100,
};
let boundaries = BoundaryReducer::evaluate_candidates(&candidates, &chunk_start);
assert_eq!(boundaries.len(), 1); assert_eq!(boundaries[0].offset, 110);
}
#[test]
fn test_reduce_all() {
let states = vec![
PartialState {
boundary_candidates: BoundaryVec::from_vec(vec![BoundaryCandidate {
local_offset: 10,
local_depths: DepthVec::from_vec(vec![0]),
flags: BoundaryFlags::WEAK,
}]),
deltas: DeltaVec::from_vec(vec![DeltaEntry { net: 1, min: 0 }]),
abbreviation: Default::default(),
chunk_length: 50,
},
PartialState {
boundary_candidates: BoundaryVec::from_vec(vec![BoundaryCandidate {
local_offset: 15,
local_depths: DepthVec::from_vec(vec![-1]),
flags: BoundaryFlags::STRONG,
}]),
deltas: DeltaVec::from_vec(vec![DeltaEntry { net: -1, min: -1 }]),
abbreviation: Default::default(),
chunk_length: 50,
},
];
let chunk_starts = vec![
ChunkStartState {
cumulative_deltas: DeltaVec::from_vec(vec![DeltaEntry { net: 0, min: 0 }]),
global_offset: 0,
},
ChunkStartState {
cumulative_deltas: DeltaVec::from_vec(vec![DeltaEntry { net: 1, min: 0 }]),
global_offset: 50,
},
];
let boundaries = BoundaryReducer::reduce_all(&states, &chunk_starts);
assert_eq!(boundaries.len(), 2);
assert_eq!(boundaries[0].offset, 10); assert_eq!(boundaries[1].offset, 65); }
}