use crate::boxes::BBox;
use crate::set::BBoxSet;
use crate::{HasInfinity, Rng};
pub fn one_way_scan<B, ID>(
intervals: &BBoxSet<B, ID>,
points: &BBoxSet<B, ID>,
max_dim_check: usize,
out: &mut Vec<(ID, ID)>,
) where
B: BBox,
ID: Copy + PartialOrd,
B::Num: PartialOrd,
{
let p_len = points.len();
let mut p_min_idx = 0;
for i in &intervals.boxes {
let &(i, i_id) = i;
let i_min = i.lo(0);
let i_max = i.hi(0);
while p_min_idx < p_len && points.boxes[p_min_idx].0.lo(0) < i_min {
p_min_idx += 1;
}
if p_min_idx == p_len {
return;
}
'points: for p_idx in p_min_idx..p_len {
let (p, p_id) = points.boxes[p_idx];
let p_min = p.lo(0);
if p_min >= i_max {
break 'points;
}
if p_id == i_id {
continue 'points;
}
for dim in 1..max_dim_check + 1 {
if !p.intersects_in(dim, i.lo(dim), i.hi(dim)) {
continue 'points;
}
}
if p_min == i_min && p_id > i_id {
continue 'points;
}
out.push((i_id, p_id));
}
}
}
pub fn simulated_one_way_scan<B, ID>(
intervals: &BBoxSet<B, ID>,
points: &BBoxSet<B, ID>,
max_dim_check: usize,
out: &mut Vec<(ID, ID)>,
) where
B: BBox,
ID: Copy + PartialOrd,
B::Num: PartialOrd,
{
_two_way_scan::<B, ID, true>(intervals, points, max_dim_check, out);
}
pub fn two_way_scan<B, ID>(a: &BBoxSet<B, ID>, b: &BBoxSet<B, ID>, out: &mut Vec<(ID, ID)>)
where
B: BBox,
ID: Copy,
B::Num: PartialOrd,
ID: PartialOrd,
{
_two_way_scan::<B, ID, false>(a, b, B::DIM - 1, out);
}
fn _two_way_scan<B, ID, const SIMULATE_ONE_WAY: bool>(
intervals: &BBoxSet<B, ID>,
points: &BBoxSet<B, ID>,
max_dim_check: usize,
out: &mut Vec<(ID, ID)>,
) where
B: BBox,
ID: Copy + PartialOrd,
B::Num: PartialOrd,
{
let mut i_min_idx = 0;
let i_len = intervals.len();
let mut p_min_idx = 0;
let p_len = points.len();
let dim_range_upper = if SIMULATE_ONE_WAY {
max_dim_check } else {
max_dim_check + 1
};
while i_min_idx < i_len && p_min_idx < p_len {
let (i_min, i_min_id) = intervals.get(i_min_idx);
let (p_min, p_min_id) = points.get(p_min_idx);
if i_min.lo(0) < p_min.lo(0) {
'points: for p_idx in p_min_idx..p_len {
let (p, p_id) = points.get(p_idx);
if p.lo(0) >= i_min.hi(0) {
break 'points;
}
if p_id == i_min_id {
continue 'points;
}
for dim in 1..dim_range_upper {
if !p.intersects_in(dim, i_min.lo(dim), i_min.hi(dim)) {
continue 'points;
}
}
if SIMULATE_ONE_WAY
&& (!i_min.contains_in(max_dim_check, p.lo(max_dim_check))
|| (i_min.lo(max_dim_check) == p.lo(max_dim_check) && i_min_id > p_id))
{
continue 'points;
}
out.push((p_id, i_min_id));
}
i_min_idx += 1;
} else {
'intervals: for i_idx in i_min_idx..i_len {
let (i, i_id) = intervals.get(i_idx);
if i.lo(0) >= p_min.hi(0) {
break 'intervals;
}
if i_id == p_min_id {
continue 'intervals;
}
for dim in 1..dim_range_upper {
if !i.intersects_in(dim, p_min.lo(dim), p_min.hi(dim)) {
continue 'intervals;
}
}
if SIMULATE_ONE_WAY
&& (!i.contains_in(max_dim_check, p_min.lo(max_dim_check))
|| (i.lo(max_dim_check) == p_min.lo(max_dim_check) && i_id > p_min_id))
{
continue 'intervals;
}
out.push((p_min_id, i_id));
}
p_min_idx += 1;
}
}
}
pub fn hybrid<B, ID, R, const CUTOFF: usize>(
intervals: &BBoxSet<B, ID>,
points: &BBoxSet<B, ID>,
lo: B::Num,
hi: B::Num,
dim: usize,
out: &mut Vec<(ID, ID)>,
rand: &mut R,
) where
B: BBox,
ID: PartialOrd + Copy,
B::Num: PartialOrd + HasInfinity,
R: Rng,
{
if intervals.empty() || points.empty() || hi <= lo {
return;
}
if dim == 0 {
one_way_scan(intervals, points, 0, out);
return;
}
if intervals.len() < CUTOFF || points.len() < CUTOFF {
simulated_one_way_scan(intervals, points, dim, out);
return;
}
let (intervals_m, intervals_lr) =
intervals.partition(|(i, _)| i.lo(dim) < lo && i.hi(dim) > hi);
let (ninfty, infty) = (B::Num::NINFTY, B::Num::INFTY);
hybrid::<B, ID, R, CUTOFF>(&intervals_m, points, ninfty, infty, dim - 1, out, rand);
hybrid::<B, ID, R, CUTOFF>(points, &intervals_m, ninfty, infty, dim - 1, out, rand);
let mi = points.approx_median(dim, rand);
if mi == hi || mi == lo {
simulated_one_way_scan(&intervals_lr, points, dim, out);
return;
}
let (points_l, points_r) = points.partition(|(p, _)| p.lo(dim) < mi);
let len = intervals_lr.len();
let (mut intervals_l, mut intervals_r) =
(BBoxSet::with_capacity(len), BBoxSet::with_capacity(len));
for &(i, id) in &intervals_lr.boxes {
if i.lo(dim) < mi {
intervals_l.push(id, i);
}
if i.hi(dim) > mi {
intervals_r.push(id, i);
}
}
hybrid::<B, ID, R, CUTOFF>(&intervals_l, &points_l, lo, mi, dim, out, rand); hybrid::<B, ID, R, CUTOFF>(&intervals_r, &points_r, mi, hi, dim, out, rand);
}