From 1a3435fdae8cc2c4850026269cb9bf3b760fcec6 Mon Sep 17 00:00:00 2001 From: Igor Bologov Date: Tue, 9 May 2023 15:30:48 +0300 Subject: [PATCH 1/7] hack that returns indices instead of IDs --- src/internals.rs | 136 ++++++++++++++++++++++++----------------------- src/lib.rs | 2 +- 2 files changed, 70 insertions(+), 68 deletions(-) diff --git a/src/internals.rs b/src/internals.rs index 99b6df5..c61667b 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -15,7 +15,7 @@ pub fn one_way_scan( intervals: &BBoxSet, points: &BBoxSet, max_dim_check: usize, - out: &mut Vec<(ID, ID)>, + out: &mut Vec<(usize, usize)>, ) where B: BBox, ID: Copy + PartialOrd, @@ -25,7 +25,7 @@ pub fn one_way_scan( let mut p_min_idx = 0; // iterate through (sorted) intervals - for i in &intervals.boxes { + for (i_idx, i) in intervals.boxes.iter().enumerate() { let &(i, i_id) = i; let i_min = i.lo(0); let i_max = i.hi(0); @@ -62,7 +62,7 @@ pub fn one_way_scan( continue 'points; } - out.push((i_id, p_id)); + out.push((i_idx, p_idx)); } } } @@ -73,7 +73,7 @@ pub fn simulated_one_way_scan( intervals: &BBoxSet, points: &BBoxSet, max_dim_check: usize, - out: &mut Vec<(ID, ID)>, + out: &mut Vec<(usize, usize)>, ) where B: BBox, ID: Copy + PartialOrd, @@ -86,7 +86,7 @@ pub fn simulated_one_way_scan( /// as intervals and points in turn, as if [`one_way_scan`] was called twice, once with intervals and points switched /// * `a` and `b` must be distinct [`BBoxSet`]s and must be sorted before calling. /// * `out` will contain pairs of `ID`s of intersecting boxes. -pub fn two_way_scan(a: &BBoxSet, b: &BBoxSet, out: &mut Vec<(ID, ID)>) +pub fn two_way_scan(a: &BBoxSet, b: &BBoxSet, out: &mut Vec<(usize, usize)>) where B: BBox, ID: Copy, @@ -100,7 +100,7 @@ fn _two_way_scan( intervals: &BBoxSet, points: &BBoxSet, max_dim_check: usize, - out: &mut Vec<(ID, ID)>, + out: &mut Vec<(usize, usize)>, ) where B: BBox, ID: Copy + PartialOrd, @@ -138,7 +138,8 @@ fn _two_way_scan( continue 'points; } - out.push((p_id, i_min_id)); + //out.push((p_id, i_min_id)); + out.push((p_idx, i_min_idx)); } i_min_idx += 1; @@ -167,7 +168,8 @@ fn _two_way_scan( continue 'intervals; } - out.push((p_min_id, i_id)); +// out.push((p_min_id, i_id)); + out.push((p_min_idx, i_idx)); } p_min_idx += 1; @@ -200,63 +202,63 @@ pub fn hybrid( // https://dl.acm.org/doi/10.1145/336154.336192 // Step 1: return if input is empty - if intervals.empty() || points.empty() || hi <= lo { - return; - } - - // Step 2: first hybridization method: scan if only dimension 0 is left to check - if dim == 0 { - one_way_scan(intervals, points, 0, out); - return; - } - - // Step 3: second hybridization method: scan if size of input is smaller than cutoff - if intervals.len() < CUTOFF || points.len() < CUTOFF { - simulated_one_way_scan(intervals, points, dim, out); - return; - } - - // Step 4: let intervals_m contain the intervals that would be stored at this node of the segment tree - // because they span the segment [lo, hi), meaning it is one of their canonical segments - // let intervals_lr contain the intervals not stored at this node - 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); - - // Step 4: stream two segment trees in the next dimension for the intervals stored at this node - hybrid::(&intervals_m, points, ninfty, infty, dim - 1, out, rand); - hybrid::(points, &intervals_m, ninfty, infty, dim - 1, out, rand); - - // Step 5: divide the segment [lo, hi) into segments [lo, mi) and [mi, hi) by computing an approximate median - let mi = points.approx_median(dim, rand); - - // if we failed to divide the segment into subsegments, just scan instead - if mi == hi || mi == lo { - simulated_one_way_scan(&intervals_lr, points, dim, out); - return; - } - - // let points_l contain the points in the left subsegment [lo, mi), - // points_r those in the right subsegment [mi, hi) - 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)); - - // let intervals_l/r contain the intervals stored somewhere in the left/right subtree - // because they intersect [lo, mi)/[mi, hi) but don't span [lo, hi) - // intervals_l and intervals_r are not usually disjoint! - 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::(&intervals_l, &points_l, lo, mi, dim, out, rand); // Step 6: left subtree - hybrid::(&intervals_r, &points_r, mi, hi, dim, out, rand); - // Step 7: right subtree + // if intervals.empty() || points.empty() || hi <= lo { + // return; + // } + // + // // Step 2: first hybridization method: scan if only dimension 0 is left to check + // if dim == 0 { + // one_way_scan(intervals, points, 0, out); + // return; + // } + // + // // Step 3: second hybridization method: scan if size of input is smaller than cutoff + // if intervals.len() < CUTOFF || points.len() < CUTOFF { + // simulated_one_way_scan(intervals, points, dim, out); + // return; + // } + // + // // Step 4: let intervals_m contain the intervals that would be stored at this node of the segment tree + // // because they span the segment [lo, hi), meaning it is one of their canonical segments + // // let intervals_lr contain the intervals not stored at this node + // 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); + // + // // Step 4: stream two segment trees in the next dimension for the intervals stored at this node + // hybrid::(&intervals_m, points, ninfty, infty, dim - 1, out, rand); + // hybrid::(points, &intervals_m, ninfty, infty, dim - 1, out, rand); + // + // // Step 5: divide the segment [lo, hi) into segments [lo, mi) and [mi, hi) by computing an approximate median + // let mi = points.approx_median(dim, rand); + // + // // if we failed to divide the segment into subsegments, just scan instead + // if mi == hi || mi == lo { + // simulated_one_way_scan(&intervals_lr, points, dim, out); + // return; + // } + // + // // let points_l contain the points in the left subsegment [lo, mi), + // // points_r those in the right subsegment [mi, hi) + // 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)); + // + // // let intervals_l/r contain the intervals stored somewhere in the left/right subtree + // // because they intersect [lo, mi)/[mi, hi) but don't span [lo, hi) + // // intervals_l and intervals_r are not usually disjoint! + // 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::(&intervals_l, &points_l, lo, mi, dim, out, rand); // Step 6: left subtree + // hybrid::(&intervals_r, &points_r, mi, hi, dim, out, rand); + // // Step 7: right subtree } diff --git a/src/lib.rs b/src/lib.rs index b114ec1..60cdc12 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,7 +114,7 @@ pub fn intersect_ze_custom( /// Should perform reasonably up to approximately 1,000 boxes /// * `a` and `b` may be either the same or distinct [`BBoxSet`]s and must be sorted before calling. /// * `out` will contain pairs of `ID`s of intersecting boxes. -pub fn intersect_scan(a: &BBoxSet, b: &BBoxSet, out: &mut Vec<(ID, ID)>) +pub fn intersect_scan(a: &BBoxSet, b: &BBoxSet, out: &mut Vec<(usize, usize)>) where B: BBox, ID: Copy + PartialOrd, From 87767b4a441703aa2320aa68017996ecb04c7802 Mon Sep 17 00:00:00 2001 From: Igor Bologov Date: Fri, 12 May 2023 16:19:00 +0300 Subject: [PATCH 2/7] generic implemented --- Cargo.toml | 4 ++-- src/internals.rs | 51 ++++++++++++++++++++++++++++----------------- src/lib.rs | 54 +++++++++++++++++++++++++++++++++++------------- 3 files changed, 74 insertions(+), 35 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b41d290..7f62c9b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "box_intersect_ze" -version = "0.1.0" +version = "0.1.1" authors = ["derivator "] edition = "2018" license = "GPL-3.0-or-later" @@ -21,4 +21,4 @@ name = "benchmark" required-features = ["rand-crate"] [dev-dependencies] -rand_chacha = "0.3.0" \ No newline at end of file +rand_chacha = "0.3.0" diff --git a/src/internals.rs b/src/internals.rs index c61667b..09259b4 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -4,18 +4,18 @@ use crate::boxes::BBox; use crate::set::BBoxSet; use crate::{HasInfinity, Rng}; - +use crate::ImplKind; /// Reports intersections between `intervals` and `points` by scanning in dimension 0, /// treating boxes in `points` as points: intersections are only reported when the low /// endpoint in dimension 0 of a box in `points` is inside the projection of a box in `intervals`. /// * `intervals` and `points` must be sorted before calling /// * `max_dim_check`: highest dimension that should be checked for intersection /// * `out` will contain pairs of `ID`s of intersecting boxes. -pub fn one_way_scan( +pub fn one_way_scan<'a, B, ID>( intervals: &BBoxSet, points: &BBoxSet, max_dim_check: usize, - out: &mut Vec<(usize, usize)>, + mut out: ImplKind<'a, ID>, ) where B: BBox, ID: Copy + PartialOrd, @@ -62,7 +62,11 @@ pub fn one_way_scan( continue 'points; } - out.push((i_idx, p_idx)); + match out{ + ImplKind::Index(ref mut out) => {out.push((i_idx, p_idx));} + ImplKind::Ident( ref mut out) => {out.push((i_id, p_id));} + ImplKind::Both( ref mut out) => {out.push(((p_idx, p_min_idx),(i_id, p_id)));} + } } } } @@ -79,14 +83,14 @@ pub fn simulated_one_way_scan( ID: Copy + PartialOrd, B::Num: PartialOrd, { - _two_way_scan::(intervals, points, max_dim_check, out); + _two_way_scan::(intervals, points, max_dim_check, ImplKind::Index(out)); } /// Reports intersections between boxes in `a` and `b` by scanning in dimension 0, treating each /// as intervals and points in turn, as if [`one_way_scan`] was called twice, once with intervals and points switched /// * `a` and `b` must be distinct [`BBoxSet`]s and must be sorted before calling. /// * `out` will contain pairs of `ID`s of intersecting boxes. -pub fn two_way_scan(a: &BBoxSet, b: &BBoxSet, out: &mut Vec<(usize, usize)>) +pub fn two_way_scan<'a, B, ID>(a: &BBoxSet, b: &BBoxSet, out: ImplKind<'a, ID>) where B: BBox, ID: Copy, @@ -96,11 +100,11 @@ where _two_way_scan::(a, b, B::DIM - 1, out); } -fn _two_way_scan( +fn _two_way_scan<'a, B, ID, const SIMULATE_ONE_WAY: bool>( intervals: &BBoxSet, points: &BBoxSet, max_dim_check: usize, - out: &mut Vec<(usize, usize)>, + mut out: ImplKind<'a, ID>, ) where B: BBox, ID: Copy + PartialOrd, @@ -137,9 +141,13 @@ fn _two_way_scan( { continue 'points; } - + match out{ + ImplKind::Index(ref mut out) => {out.push((p_idx, i_min_idx));} + ImplKind::Ident(ref mut out) => {out.push((p_min_id, p_id));} + ImplKind::Both(ref mut out) => {out.push(((p_idx, i_min_idx),(p_min_id, p_id)));} + } //out.push((p_id, i_min_id)); - out.push((p_idx, i_min_idx)); + } i_min_idx += 1; @@ -168,8 +176,13 @@ fn _two_way_scan( continue 'intervals; } -// out.push((p_min_id, i_id)); - out.push((p_min_idx, i_idx)); + match out{ + ImplKind::Index(ref mut out) => {out.push((p_min_idx, i_idx));} + ImplKind::Ident(ref mut out) => {out.push((p_min_id, i_id));} + ImplKind::Both(ref mut out) => {out.push(((p_min_idx, i_idx),(p_min_id, i_id)));} + } + + } p_min_idx += 1; @@ -185,13 +198,13 @@ fn _two_way_scan( /// * [`lo`, `hi`) is the segment belonging to this node of the streamed segment tree /// * `out` will contain pairs of `ID`s of intersecting boxes. pub fn hybrid( - intervals: &BBoxSet, - points: &BBoxSet, - lo: B::Num, - hi: B::Num, - dim: usize, - out: &mut Vec<(ID, ID)>, - rand: &mut R, + _intervals: &BBoxSet, + _points: &BBoxSet, + _lo: B::Num, + _hi: B::Num, + _dim: usize, + _out: &mut Vec<(ID, ID)>, + _rand: &mut R, ) where B: BBox, ID: PartialOrd + Copy, diff --git a/src/lib.rs b/src/lib.rs index 60cdc12..98dad5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,24 +114,50 @@ pub fn intersect_ze_custom( /// Should perform reasonably up to approximately 1,000 boxes /// * `a` and `b` may be either the same or distinct [`BBoxSet`]s and must be sorted before calling. /// * `out` will contain pairs of `ID`s of intersecting boxes. -pub fn intersect_scan(a: &BBoxSet, b: &BBoxSet, out: &mut Vec<(usize, usize)>) +pub fn intersect_scan(a: &BBoxSet, b: &BBoxSet, out: &mut Vec<(ID, ID)>) where B: BBox, ID: Copy + PartialOrd, { - let same = a as *const _ == b as *const _; // check if a and b refer to the same BBoxSet - if same { - one_way_scan(a, b, B::DIM - 1, out); - } else { - two_way_scan(a, b, out); + the_way(a, b, ImplKind::Ident(out)); +} + +pub fn intersect_scan_idx(a: &BBoxSet, b: &BBoxSet, out: &mut Vec<(usize, usize)>) +where + B: BBox, + ID: Copy + PartialOrd +{ + the_way(a, b, ImplKind::Index(out)); +} + +fn the_way<'a, B, ID>(a: &BBoxSet, b: &BBoxSet, out: ImplKind<'a, ID>) +where + B: BBox, + ID: Copy + PartialOrd +{ + let same = a as *const _ == b as *const _; + // check if a and b refer to the same BBoxSet + match same{ + true=> { one_way_scan(a, b, B::DIM - 1, out);} + false => {two_way_scan(a, b, out);} + } + +} + +pub enum ImplKind<'a, ID>{ + Index(&'a mut Vec<(usize, usize)>), + Ident(&'a mut Vec<(ID, ID)>), + Both(&'a mut Vec<((usize, usize), (ID,ID))>) } + + /// Finds box intersections by checking every box in `a` against every box in `b`. /// Performs well for on the order of 100 boxes. *O*(*n^2*) /// * `a` and `b` may be either the same or distinct [`BBoxSet`]s /// * `out` will contain pairs of `ID`s of intersecting boxes. -pub fn intersect_brute_force(a: &BBoxSet, b: &BBoxSet, out: &mut Vec<(ID, ID)>) +pub fn intersect_brute_force(a: &BBoxSet, b: &BBoxSet, out: &mut Vec<(usize, usize)>) where B: BBox, ID: Copy, @@ -140,20 +166,20 @@ where if same { // avoid duplicate intersections let mut start = 1; - for &(bbox, id) in &a.boxes { - for idx in start..a.boxes.len() { - let (bbox2, id2) = a.boxes[idx]; + for (aidx, &(bbox, id)) in a.boxes.iter().enumerate() { + for aidx in start..a.boxes.len() { + let (bbox2, id2) = a.boxes[aidx]; if bbox.intersects(&bbox2) { - out.push((id, id2)); + out.push((aidx, aidx)); } } start += 1; } } else { - for &(bbox, id) in &a.boxes { - for &(bbox2, id2) in &b.boxes { + for (aidx, &(bbox, id)) in a.boxes.iter().enumerate() { + for (bidx, &(bbox2, id2)) in b.boxes.iter().enumerate() { if bbox.intersects(&bbox2) { - out.push((id, id2)); + out.push((aidx, bidx)); } } } From 774133b606ea68372e80878735cbbd4f5a5dd13f Mon Sep 17 00:00:00 2001 From: Alex Pyattaev Date: Mon, 15 May 2023 11:32:44 +0300 Subject: [PATCH 3/7] Nicer API to call functions with variable outparameters --- Cargo.toml | 1 + src/internals.rs | 185 ++++++++++++++++++++++++++--------------------- src/lib.rs | 62 +++++++++++++--- src/tests.rs | 6 +- 4 files changed, 160 insertions(+), 94 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7f62c9b..7d3144d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ categories = ["game-development"] [dependencies] rand = { version = "0.8.3", optional = true } +reborrow = {version="0.5.4", features=["derive", "reborrow-derive"]} [features] default = ["rand-crate"] diff --git a/src/internals.rs b/src/internals.rs index 09259b4..3cbe5ba 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -4,7 +4,7 @@ use crate::boxes::BBox; use crate::set::BBoxSet; use crate::{HasInfinity, Rng}; -use crate::ImplKind; +use crate::AnswerFormat; /// Reports intersections between `intervals` and `points` by scanning in dimension 0, /// treating boxes in `points` as points: intersections are only reported when the low /// endpoint in dimension 0 of a box in `points` is inside the projection of a box in `intervals`. @@ -15,7 +15,7 @@ pub fn one_way_scan<'a, B, ID>( intervals: &BBoxSet, points: &BBoxSet, max_dim_check: usize, - mut out: ImplKind<'a, ID>, + mut out: AnswerFormat<'a, ID>, ) where B: BBox, ID: Copy + PartialOrd, @@ -63,9 +63,9 @@ pub fn one_way_scan<'a, B, ID>( } match out{ - ImplKind::Index(ref mut out) => {out.push((i_idx, p_idx));} - ImplKind::Ident( ref mut out) => {out.push((i_id, p_id));} - ImplKind::Both( ref mut out) => {out.push(((p_idx, p_min_idx),(i_id, p_id)));} + AnswerFormat::Index(ref mut out) => {out.push((i_idx, p_idx));} + AnswerFormat::Ident( ref mut out) => {out.push((i_id, p_id));} + AnswerFormat::Both( ref mut out) => {out.push(((p_idx, p_min_idx),(i_id, p_id)));} } } } @@ -73,24 +73,24 @@ pub fn one_way_scan<'a, B, ID>( /// Reports intersections between `intervals` and `points` by scanning in dimension 0 (because that's where boxes are sorted), /// but pretends it was scanning in dimension `max_dim_check` by treating `points` as points there, as in [`one_way_scan`] -pub fn simulated_one_way_scan( +pub fn simulated_one_way_scan<'a, B, ID>( intervals: &BBoxSet, points: &BBoxSet, max_dim_check: usize, - out: &mut Vec<(usize, usize)>, + out: AnswerFormat<'a, ID>, ) where B: BBox, ID: Copy + PartialOrd, B::Num: PartialOrd, { - _two_way_scan::(intervals, points, max_dim_check, ImplKind::Index(out)); + _two_way_scan::(intervals, points, max_dim_check, out); } /// Reports intersections between boxes in `a` and `b` by scanning in dimension 0, treating each /// as intervals and points in turn, as if [`one_way_scan`] was called twice, once with intervals and points switched /// * `a` and `b` must be distinct [`BBoxSet`]s and must be sorted before calling. /// * `out` will contain pairs of `ID`s of intersecting boxes. -pub fn two_way_scan<'a, B, ID>(a: &BBoxSet, b: &BBoxSet, out: ImplKind<'a, ID>) +pub fn two_way_scan<'a, B, ID>(a: &BBoxSet, b: &BBoxSet, out: AnswerFormat<'a, ID>) where B: BBox, ID: Copy, @@ -104,7 +104,7 @@ fn _two_way_scan<'a, B, ID, const SIMULATE_ONE_WAY: bool>( intervals: &BBoxSet, points: &BBoxSet, max_dim_check: usize, - mut out: ImplKind<'a, ID>, + mut out: AnswerFormat<'a, ID>, ) where B: BBox, ID: Copy + PartialOrd, @@ -142,9 +142,9 @@ fn _two_way_scan<'a, B, ID, const SIMULATE_ONE_WAY: bool>( continue 'points; } match out{ - ImplKind::Index(ref mut out) => {out.push((p_idx, i_min_idx));} - ImplKind::Ident(ref mut out) => {out.push((p_min_id, p_id));} - ImplKind::Both(ref mut out) => {out.push(((p_idx, i_min_idx),(p_min_id, p_id)));} + AnswerFormat::Index(ref mut out) => {out.push((p_idx, i_min_idx));} + AnswerFormat::Ident(ref mut out) => {out.push((p_min_id, p_id));} + AnswerFormat::Both(ref mut out) => {out.push(((p_idx, i_min_idx),(p_min_id, p_id)));} } //out.push((p_id, i_min_id)); @@ -177,9 +177,9 @@ fn _two_way_scan<'a, B, ID, const SIMULATE_ONE_WAY: bool>( } match out{ - ImplKind::Index(ref mut out) => {out.push((p_min_idx, i_idx));} - ImplKind::Ident(ref mut out) => {out.push((p_min_id, i_id));} - ImplKind::Both(ref mut out) => {out.push(((p_min_idx, i_idx),(p_min_id, i_id)));} + AnswerFormat::Index(ref mut out) => {out.push((p_min_idx, i_idx));} + AnswerFormat::Ident(ref mut out) => {out.push((p_min_id, i_id));} + AnswerFormat::Both(ref mut out) => {out.push(((p_min_idx, i_idx),(p_min_id, i_id)));} } @@ -198,80 +198,101 @@ fn _two_way_scan<'a, B, ID, const SIMULATE_ONE_WAY: bool>( /// * [`lo`, `hi`) is the segment belonging to this node of the streamed segment tree /// * `out` will contain pairs of `ID`s of intersecting boxes. pub fn hybrid( - _intervals: &BBoxSet, - _points: &BBoxSet, - _lo: B::Num, - _hi: B::Num, - _dim: usize, - _out: &mut Vec<(ID, ID)>, - _rand: &mut R, + intervals: &BBoxSet, + points: &BBoxSet, + 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, { + hybrid_flex::(intervals,points,lo, hi,dim, AnswerFormat::Ident(out), rand); +} + +pub fn hybrid_flex<'a, B, ID, R, const CUTOFF: usize>( + intervals: &BBoxSet, + points: &BBoxSet, + lo: B::Num, + hi: B::Num, + dim: usize, + mut out: AnswerFormat<'a, ID>, + rand: &mut R, +) where +B: BBox, +ID: PartialOrd + Copy, +B::Num: PartialOrd + HasInfinity, +R: Rng, +{ + //use reborrow::ReborrowMut; + //impl <'a, ID> Copy for AnswerFormat<'a, ID>{} // The steps of the algorithm are numbered as in the paper "Fast software for box intersections": // https://dl.acm.org/doi/10.1145/336154.336192 // Step 1: return if input is empty - // if intervals.empty() || points.empty() || hi <= lo { - // return; - // } - // - // // Step 2: first hybridization method: scan if only dimension 0 is left to check - // if dim == 0 { - // one_way_scan(intervals, points, 0, out); - // return; - // } - // - // // Step 3: second hybridization method: scan if size of input is smaller than cutoff - // if intervals.len() < CUTOFF || points.len() < CUTOFF { - // simulated_one_way_scan(intervals, points, dim, out); - // return; - // } - // - // // Step 4: let intervals_m contain the intervals that would be stored at this node of the segment tree - // // because they span the segment [lo, hi), meaning it is one of their canonical segments - // // let intervals_lr contain the intervals not stored at this node - // 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); - // - // // Step 4: stream two segment trees in the next dimension for the intervals stored at this node - // hybrid::(&intervals_m, points, ninfty, infty, dim - 1, out, rand); - // hybrid::(points, &intervals_m, ninfty, infty, dim - 1, out, rand); - // - // // Step 5: divide the segment [lo, hi) into segments [lo, mi) and [mi, hi) by computing an approximate median - // let mi = points.approx_median(dim, rand); - // - // // if we failed to divide the segment into subsegments, just scan instead - // if mi == hi || mi == lo { - // simulated_one_way_scan(&intervals_lr, points, dim, out); - // return; - // } - // - // // let points_l contain the points in the left subsegment [lo, mi), - // // points_r those in the right subsegment [mi, hi) - // 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)); - // - // // let intervals_l/r contain the intervals stored somewhere in the left/right subtree - // // because they intersect [lo, mi)/[mi, hi) but don't span [lo, hi) - // // intervals_l and intervals_r are not usually disjoint! - // 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::(&intervals_l, &points_l, lo, mi, dim, out, rand); // Step 6: left subtree - // hybrid::(&intervals_r, &points_r, mi, hi, dim, out, rand); - // // Step 7: right subtree + if intervals.empty() || points.empty() || hi <= lo { + return; + } + + // Step 2: first hybridization method: scan if only dimension 0 is left to check + if dim == 0 { + one_way_scan(intervals, points, 0, out); + return; + } + + // Step 3: second hybridization method: scan if size of input is smaller than cutoff + if intervals.len() < CUTOFF || points.len() < CUTOFF { + simulated_one_way_scan(intervals, points, dim, out); + return; + } + + // Step 4: let intervals_m contain the intervals that would be stored at this node of the segment tree + // because they span the segment [lo, hi), meaning it is one of their canonical segments + // let intervals_lr contain the intervals not stored at this node + 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); + + // Step 4: stream two segment trees in the next dimension for the intervals stored at this node + + hybrid_flex::(&intervals_m, points, ninfty, infty, dim - 1, out.reborrow(), rand); + + + hybrid_flex::(points, &intervals_m, ninfty, infty, dim - 1, out.reborrow(), rand); + + // Step 5: divide the segment [lo, hi) into segments [lo, mi) and [mi, hi) by computing an approximate median + let mi = points.approx_median(dim, rand); + + // if we failed to divide the segment into subsegments, just scan instead + if mi == hi || mi == lo { + simulated_one_way_scan(&intervals_lr, points, dim, out.reborrow()); + return; + } + + // let points_l contain the points in the left subsegment [lo, mi), + // points_r those in the right subsegment [mi, hi) + 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)); + + // let intervals_l/r contain the intervals stored somewhere in the left/right subtree + // because they intersect [lo, mi)/[mi, hi) but don't span [lo, hi) + // intervals_l and intervals_r are not usually disjoint! + 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_flex::(&intervals_l, &points_l, lo, mi, dim, out.reborrow(), rand); // Step 6: left subtree + hybrid_flex::(&intervals_r, &points_r, mi, hi, dim, out.reborrow(), rand); // Step 7: right subtree } diff --git a/src/lib.rs b/src/lib.rs index 98dad5a..1ac52c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,7 +119,7 @@ where B: BBox, ID: Copy + PartialOrd, { - the_way(a, b, ImplKind::Ident(out)); + intersect_scan_flex(a, b, AnswerFormat::Ident(out)); } pub fn intersect_scan_idx(a: &BBoxSet, b: &BBoxSet, out: &mut Vec<(usize, usize)>) @@ -127,10 +127,11 @@ where B: BBox, ID: Copy + PartialOrd { - the_way(a, b, ImplKind::Index(out)); + intersect_scan_flex(a, b, AnswerFormat::Index(out)); } -fn the_way<'a, B, ID>(a: &BBoxSet, b: &BBoxSet, out: ImplKind<'a, ID>) + +fn intersect_scan_flex<'a, B, ID>(a: &BBoxSet, b: &BBoxSet, out: AnswerFormat<'a, ID>) where B: BBox, ID: Copy + PartialOrd @@ -142,22 +143,56 @@ where false => {two_way_scan(a, b, out);} } - } -pub enum ImplKind<'a, ID>{ + +#[derive(Debug)] +pub enum AnswerFormat<'a, ID>{ Index(&'a mut Vec<(usize, usize)>), Ident(&'a mut Vec<(ID, ID)>), Both(&'a mut Vec<((usize, usize), (ID,ID))>) } +// Allow AnswerFormat to be reborrowed (see reborrow crate for why this is useful) +impl<'__reborrow_lifetime, 'a, ID> AnswerFormat<'a, ID> +{ + #[inline] + pub fn reborrow(&'__reborrow_lifetime mut self) -> AnswerFormat<'__reborrow_lifetime, ID> { + match self{ + AnswerFormat::Index(ref mut s)=> AnswerFormat::<'__reborrow_lifetime>::Index(s), + AnswerFormat::Ident(ref mut s)=> AnswerFormat::<'__reborrow_lifetime>::Ident(s), + AnswerFormat::Both(ref mut s)=> AnswerFormat::<'__reborrow_lifetime>::Both(s), + } + } +} + /// Finds box intersections by checking every box in `a` against every box in `b`. /// Performs well for on the order of 100 boxes. *O*(*n^2*) /// * `a` and `b` may be either the same or distinct [`BBoxSet`]s /// * `out` will contain pairs of `ID`s of intersecting boxes. -pub fn intersect_brute_force(a: &BBoxSet, b: &BBoxSet, out: &mut Vec<(usize, usize)>) +pub fn intersect_brute_force(a: &BBoxSet, b: &BBoxSet, out: &mut Vec<(ID, ID)>) +where +B: BBox, +ID: Copy, +{ + intersect_brute_force_flex(a, b, AnswerFormat::Ident(out)); +} + +/// Finds box intersections by checking every box in `a` against every box in `b`. +/// Performs well for on the order of 100 boxes. *O*(*n^2*) +/// * `a` and `b` may be either the same or distinct [`BBoxSet`]s +/// * `out` will contain pairs of `ID`s of intersecting boxes. +pub fn intersect_brute_force_idx(a: &BBoxSet, b: &BBoxSet, out: &mut Vec<(usize, usize)>) +where +B: BBox, +ID: Copy, +{ + intersect_brute_force_flex(a, b, AnswerFormat::Index(out)); +} + +pub fn intersect_brute_force_flex<'a, B, ID>(a: &BBoxSet, b: &BBoxSet, mut out: AnswerFormat<'a, ID>) where B: BBox, ID: Copy, @@ -167,10 +202,15 @@ where // avoid duplicate intersections let mut start = 1; for (aidx, &(bbox, id)) in a.boxes.iter().enumerate() { - for aidx in start..a.boxes.len() { + for bidx in start..a.boxes.len() { let (bbox2, id2) = a.boxes[aidx]; if bbox.intersects(&bbox2) { - out.push((aidx, aidx)); + match out{ + AnswerFormat::Index(ref mut out)=>out.push((aidx, bidx)), + AnswerFormat::Ident(ref mut out)=>out.push((id, id2)), + AnswerFormat::Both(ref mut out)=>out.push(((aidx, aidx),(id, id2))), + } + } } start += 1; @@ -179,7 +219,11 @@ where for (aidx, &(bbox, id)) in a.boxes.iter().enumerate() { for (bidx, &(bbox2, id2)) in b.boxes.iter().enumerate() { if bbox.intersects(&bbox2) { - out.push((aidx, bidx)); + match out{ + AnswerFormat::Index(ref mut out)=>out.push((aidx, bidx)), + AnswerFormat::Ident(ref mut out)=>out.push((id, id2)), + AnswerFormat::Both(ref mut out)=>out.push(((aidx, bidx),(id, id2))), + } } } } diff --git a/src/tests.rs b/src/tests.rs index e49cf2e..1335dfb 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -83,7 +83,7 @@ fn one_way_scan() { let mut res = Vec::<(usize, usize)>::with_capacity(80); boxes.sort(); - crate::internals::one_way_scan(&boxes, &boxes, 2, &mut res); + crate::internals::one_way_scan(&boxes, &boxes, 2, crate::AnswerFormat::Ident(&mut res)); assert!(same(&boxes_self, &res)); } @@ -94,7 +94,7 @@ fn simulated_one_way_scan() { let mut res = Vec::<(usize, usize)>::with_capacity(80); boxes.sort(); - crate::internals::simulated_one_way_scan(&boxes, &boxes, 2, &mut res); + crate::internals::simulated_one_way_scan(&boxes, &boxes, 2, crate::AnswerFormat::Ident(&mut res)); assert!(same(&boxes_self, &res)); } @@ -106,7 +106,7 @@ fn two_way_scan() { let mut res = Vec::<(usize, usize)>::with_capacity(80); boxes.sort(); boxes2.sort(); - crate::internals::two_way_scan(&boxes, &boxes2, &mut res); + crate::internals::two_way_scan(&boxes, &boxes2, crate::AnswerFormat::Ident(&mut res)); assert!(same(&boxes_boxes2, &res)); } From 91f3542febb9aa5addbd34e47a01cb60fdd4a59c Mon Sep 17 00:00:00 2001 From: Alex Pyattaev Date: Mon, 29 May 2023 22:11:02 +0300 Subject: [PATCH 4/7] fix regression in two-way-scan --- src/internals.rs | 4 ++-- src/lib.rs | 6 +++--- src/tests.rs | 7 +++++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/internals.rs b/src/internals.rs index 24fafb4..b18cf6f 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -150,8 +150,8 @@ fn _two_way_scan<'a, B, ID, const SIMULATE_ONE_WAY: bool>( } match out{ AnswerFormat::Index(ref mut out) => {out.push((p_idx, i_min_idx));} - AnswerFormat::Ident(ref mut out) => {out.push((p_min_id, p_id));} - AnswerFormat::Both(ref mut out) => {out.push(((p_idx, i_min_idx),(p_min_id, p_id)));} + AnswerFormat::Ident(ref mut out) => {out.push((i_min_id, p_id));} + AnswerFormat::Both(ref mut out) => {out.push(((p_idx, i_min_idx),(i_min_id, p_id)));} } //out.push((p_id, i_min_id)); diff --git a/src/lib.rs b/src/lib.rs index 1ac52c6..0b41299 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,8 +25,8 @@ //! let mut result = Vec::with_capacity(2); // set capacity according to expected number of intersections to avoid resizing //! box_intersect_ze::intersect_ze(&boxes, &boxes, &mut result, &mut ChaCha8Rng::seed_from_u64(1234)); // get the intersections //! -//! assert!(result.contains(&(1,0))); -//! assert!(result.contains(&(2,1))); +//! assert!(result.contains(&(0,1))); +//! assert!(result.contains(&(1,2))); //! assert!(!result.contains(&(2,0))); //! assert!(!result.contains(&(0,2))); //! ``` @@ -203,7 +203,7 @@ where let mut start = 1; for (aidx, &(bbox, id)) in a.boxes.iter().enumerate() { for bidx in start..a.boxes.len() { - let (bbox2, id2) = a.boxes[aidx]; + let (bbox2, id2) = a.boxes[bidx]; if bbox.intersects(&bbox2) { match out{ AnswerFormat::Index(ref mut out)=>out.push((aidx, bidx)), diff --git a/src/tests.rs b/src/tests.rs index 67a7b2d..27dba69 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -55,18 +55,21 @@ struct TestData { } static TEST_DATA: Lazy = Lazy::new(|| test_data()); +const N:usize = 10; /// Generates some random boxes and finds their intersections using brute force, as a reference to validate against fn test_data() -> TestData { - let mut boxes1 = random_boxes(150, 0, 12345); - let mut boxes2 = random_boxes(150, boxes1.len(), 54321); + let mut boxes1 = random_boxes(N, 0, 12345); + let mut boxes2 = random_boxes(N, boxes1.len(), 54321); boxes1.sort(); boxes2.sort(); let mut complete = Vec::<(usize, usize)>::with_capacity(80); intersect_brute_force(&boxes1, &boxes1, &mut complete); + println!("complete correct {:?}", &complete); let mut bipartite = Vec::<(usize, usize)>::with_capacity(80); intersect_brute_force(&boxes1, &boxes2, &mut bipartite); + println!("bipartitie correct {:?}", &bipartite); assert_ne!(complete.len(), 0); assert_ne!(bipartite.len(), 0); From 341cf4188f2ae7a4bd7d93c85f61af1576527bba Mon Sep 17 00:00:00 2001 From: Igor Bologov Date: Tue, 30 May 2023 12:54:06 +0300 Subject: [PATCH 5/7] remove clutter from tests.rs --- src/tests.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/tests.rs b/src/tests.rs index 27dba69..ebfcb57 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -55,7 +55,7 @@ struct TestData { } static TEST_DATA: Lazy = Lazy::new(|| test_data()); -const N:usize = 10; +const N:usize = 1234; /// Generates some random boxes and finds their intersections using brute force, as a reference to validate against fn test_data() -> TestData { let mut boxes1 = random_boxes(N, 0, 12345); @@ -66,10 +66,8 @@ fn test_data() -> TestData { let mut complete = Vec::<(usize, usize)>::with_capacity(80); intersect_brute_force(&boxes1, &boxes1, &mut complete); - println!("complete correct {:?}", &complete); let mut bipartite = Vec::<(usize, usize)>::with_capacity(80); intersect_brute_force(&boxes1, &boxes2, &mut bipartite); - println!("bipartitie correct {:?}", &bipartite); assert_ne!(complete.len(), 0); assert_ne!(bipartite.len(), 0); From 4dd8c84a74c4bad176ba93c0fe7b912a577910d2 Mon Sep 17 00:00:00 2001 From: Igor Bologov Date: Fri, 30 Jun 2023 19:51:53 +0300 Subject: [PATCH 6/7] fix index positions --- src/internals.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internals.rs b/src/internals.rs index b18cf6f..4a4d313 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -184,7 +184,7 @@ fn _two_way_scan<'a, B, ID, const SIMULATE_ONE_WAY: bool>( } match out{ - AnswerFormat::Index(ref mut out) => {out.push((p_min_idx, i_idx));} + AnswerFormat::Index(ref mut out) => {out.push((i_idx, p_min_idx));} AnswerFormat::Ident(ref mut out) => {out.push((p_min_id, i_id));} AnswerFormat::Both(ref mut out) => {out.push(((p_min_idx, i_idx),(p_min_id, i_id)));} } From f31c4636e0d11947d9ae1590568786f8ce52318c Mon Sep 17 00:00:00 2001 From: Igor Bologov Date: Fri, 30 Jun 2023 20:22:05 +0300 Subject: [PATCH 7/7] order of returned indices for scan --- src/internals.rs | 98 +++++++++++++++++++++++++++++++++--------------- src/lib.rs | 86 ++++++++++++++++++++++-------------------- 2 files changed, 114 insertions(+), 70 deletions(-) diff --git a/src/internals.rs b/src/internals.rs index 4a4d313..7f2fab8 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -3,8 +3,8 @@ use crate::boxes::BBox; use crate::set::BBoxSet; -use crate::{HasInfinity, Rng}; use crate::AnswerFormat; +use crate::{HasInfinity, Rng}; /// Reports intersections between `intervals` and `points` by scanning in dimension 0, /// treating boxes in `points` as points: intersections are only reported when the low /// endpoint in dimension 0 of a box in `points` is inside the projection of a box in `intervals`. @@ -15,7 +15,7 @@ pub fn one_way_scan<'a, B, ID>( intervals: &BBoxSet, points: &BBoxSet, max_dim_check: usize, - mut out: AnswerFormat<'a, ID>, + mut out: AnswerFormat<'a, ID>, ) where B: BBox, ID: Copy + PartialOrd, @@ -62,11 +62,17 @@ pub fn one_way_scan<'a, B, ID>( continue 'points; } - match out{ - AnswerFormat::Index(ref mut out) => {out.push((i_idx, p_idx));} - AnswerFormat::Ident( ref mut out) => {out.push((i_id, p_id));} - AnswerFormat::Both( ref mut out) => {out.push(((p_idx, p_min_idx),(i_id, p_id)));} + match out { + AnswerFormat::Index(ref mut out) => { + out.push((i_idx, p_idx)); } + AnswerFormat::Ident(ref mut out) => { + out.push((i_id, p_id)); + } + AnswerFormat::Both(ref mut out) => { + out.push(((p_idx, p_min_idx), (i_id, p_id))); + } + } } } } @@ -90,7 +96,7 @@ pub fn simulated_one_way_scan<'a, B, ID>( /// as intervals and points in turn, as if [`one_way_scan`] was called twice, once with intervals and points switched /// * `a` and `b` must be distinct [`BBoxSet`]s and must be sorted before calling. /// * `out` will contain pairs of `ID`s of intersecting boxes. -pub fn two_way_scan<'a, B, ID>(a: &BBoxSet, b: &BBoxSet, out: AnswerFormat<'a, ID>) +pub fn two_way_scan<'a, B, ID>(a: &BBoxSet, b: &BBoxSet, out: AnswerFormat<'a, ID>) where B: BBox, ID: Copy, @@ -148,13 +154,18 @@ fn _two_way_scan<'a, B, ID, const SIMULATE_ONE_WAY: bool>( { continue 'points; } - match out{ - AnswerFormat::Index(ref mut out) => {out.push((p_idx, i_min_idx));} - AnswerFormat::Ident(ref mut out) => {out.push((i_min_id, p_id));} - AnswerFormat::Both(ref mut out) => {out.push(((p_idx, i_min_idx),(i_min_id, p_id)));} + match out { + AnswerFormat::Index(ref mut out) => { + out.push((i_min_idx, p_idx)); + } + AnswerFormat::Ident(ref mut out) => { + out.push((i_min_id, p_id)); + } + AnswerFormat::Both(ref mut out) => { + out.push(((i_min_idx, p_idx), (i_min_id, p_id))); + } } //out.push((p_id, i_min_id)); - } i_min_idx += 1; @@ -183,15 +194,18 @@ fn _two_way_scan<'a, B, ID, const SIMULATE_ONE_WAY: bool>( continue 'intervals; } - match out{ - AnswerFormat::Index(ref mut out) => {out.push((i_idx, p_min_idx));} - AnswerFormat::Ident(ref mut out) => {out.push((p_min_id, i_id));} - AnswerFormat::Both(ref mut out) => {out.push(((p_min_idx, i_idx),(p_min_id, i_id)));} - } - - + match out { + AnswerFormat::Index(ref mut out) => { + out.push((i_idx, p_min_idx)); + } + AnswerFormat::Ident(ref mut out) => { + out.push((i_id, p_min_id)); + } + AnswerFormat::Both(ref mut out) => { + out.push(((i_idx, p_min_idx), (p_min_id, i_id))); + } + } } - p_min_idx += 1; } } @@ -218,7 +232,15 @@ pub fn hybrid( B::Num: PartialOrd + HasInfinity, R: Rng, { - hybrid_flex::(intervals,points,lo, hi,dim, AnswerFormat::Ident(out), rand); + hybrid_flex::( + intervals, + points, + lo, + hi, + dim, + AnswerFormat::Ident(out), + rand, + ); } pub fn hybrid_flex<'a, B, ID, R, const CUTOFF: usize>( @@ -230,10 +252,10 @@ pub fn hybrid_flex<'a, B, ID, R, const CUTOFF: usize>( mut out: AnswerFormat<'a, ID>, rand: &mut R, ) where -B: BBox, -ID: PartialOrd + Copy, -B::Num: PartialOrd + HasInfinity, -R: Rng, + B: BBox, + ID: PartialOrd + Copy, + B::Num: PartialOrd + HasInfinity, + R: Rng, { //use reborrow::ReborrowMut; //impl <'a, ID> Copy for AnswerFormat<'a, ID>{} @@ -266,10 +288,25 @@ R: Rng, // Step 4: stream two segment trees in the next dimension for the intervals stored at this node - hybrid_flex::(&intervals_m, points, ninfty, infty, dim - 1, out.reborrow(), rand); - - - hybrid_flex::(points, &intervals_m, ninfty, infty, dim - 1, out.reborrow(), rand); + hybrid_flex::( + &intervals_m, + points, + ninfty, + infty, + dim - 1, + out.reborrow(), + rand, + ); + + hybrid_flex::( + points, + &intervals_m, + ninfty, + infty, + dim - 1, + out.reborrow(), + rand, + ); // Step 5: divide the segment [lo, hi) into segments [lo, mi) and [mi, hi) by computing an approximate median let mi = points.approx_median(dim, rand); @@ -301,5 +338,6 @@ R: Rng, } hybrid_flex::(&intervals_l, &points_l, lo, mi, dim, out.reborrow(), rand); // Step 6: left subtree - hybrid_flex::(&intervals_r, &points_r, mi, hi, dim, out.reborrow(), rand); // Step 7: right subtree + hybrid_flex::(&intervals_r, &points_r, mi, hi, dim, out.reborrow(), rand); + // Step 7: right subtree } diff --git a/src/lib.rs b/src/lib.rs index 0b41299..094250d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,63 +119,64 @@ where B: BBox, ID: Copy + PartialOrd, { - intersect_scan_flex(a, b, AnswerFormat::Ident(out)); + intersect_scan_flex(a, b, AnswerFormat::Ident(out)); } -pub fn intersect_scan_idx(a: &BBoxSet, b: &BBoxSet, out: &mut Vec<(usize, usize)>) -where +pub fn intersect_scan_idx( + a: &BBoxSet, + b: &BBoxSet, + out: &mut Vec<(usize, usize)>, +) where B: BBox, - ID: Copy + PartialOrd + ID: Copy + PartialOrd, { - intersect_scan_flex(a, b, AnswerFormat::Index(out)); + intersect_scan_flex(a, b, AnswerFormat::Index(out)); } - fn intersect_scan_flex<'a, B, ID>(a: &BBoxSet, b: &BBoxSet, out: AnswerFormat<'a, ID>) where B: BBox, - ID: Copy + PartialOrd + ID: Copy + PartialOrd, { let same = a as *const _ == b as *const _; // check if a and b refer to the same BBoxSet - match same{ - true=> { one_way_scan(a, b, B::DIM - 1, out);} - false => {two_way_scan(a, b, out);} - + match same { + true => { + one_way_scan(a, b, B::DIM - 1, out); + } + false => { + two_way_scan(a, b, out); + } } } - #[derive(Debug)] -pub enum AnswerFormat<'a, ID>{ +pub enum AnswerFormat<'a, ID> { Index(&'a mut Vec<(usize, usize)>), Ident(&'a mut Vec<(ID, ID)>), - Both(&'a mut Vec<((usize, usize), (ID,ID))>) + Both(&'a mut Vec<((usize, usize), (ID, ID))>), } - // Allow AnswerFormat to be reborrowed (see reborrow crate for why this is useful) -impl<'__reborrow_lifetime, 'a, ID> AnswerFormat<'a, ID> -{ +impl<'__reborrow_lifetime, 'a, ID> AnswerFormat<'a, ID> { #[inline] pub fn reborrow(&'__reborrow_lifetime mut self) -> AnswerFormat<'__reborrow_lifetime, ID> { - match self{ - AnswerFormat::Index(ref mut s)=> AnswerFormat::<'__reborrow_lifetime>::Index(s), - AnswerFormat::Ident(ref mut s)=> AnswerFormat::<'__reborrow_lifetime>::Ident(s), - AnswerFormat::Both(ref mut s)=> AnswerFormat::<'__reborrow_lifetime>::Both(s), + match self { + AnswerFormat::Index(ref mut s) => AnswerFormat::<'__reborrow_lifetime>::Index(s), + AnswerFormat::Ident(ref mut s) => AnswerFormat::<'__reborrow_lifetime>::Ident(s), + AnswerFormat::Both(ref mut s) => AnswerFormat::<'__reborrow_lifetime>::Both(s), } } } - /// Finds box intersections by checking every box in `a` against every box in `b`. /// Performs well for on the order of 100 boxes. *O*(*n^2*) /// * `a` and `b` may be either the same or distinct [`BBoxSet`]s /// * `out` will contain pairs of `ID`s of intersecting boxes. pub fn intersect_brute_force(a: &BBoxSet, b: &BBoxSet, out: &mut Vec<(ID, ID)>) where -B: BBox, -ID: Copy, + B: BBox, + ID: Copy, { intersect_brute_force_flex(a, b, AnswerFormat::Ident(out)); } @@ -184,16 +185,22 @@ ID: Copy, /// Performs well for on the order of 100 boxes. *O*(*n^2*) /// * `a` and `b` may be either the same or distinct [`BBoxSet`]s /// * `out` will contain pairs of `ID`s of intersecting boxes. -pub fn intersect_brute_force_idx(a: &BBoxSet, b: &BBoxSet, out: &mut Vec<(usize, usize)>) -where -B: BBox, -ID: Copy, +pub fn intersect_brute_force_idx( + a: &BBoxSet, + b: &BBoxSet, + out: &mut Vec<(usize, usize)>, +) where + B: BBox, + ID: Copy, { intersect_brute_force_flex(a, b, AnswerFormat::Index(out)); } -pub fn intersect_brute_force_flex<'a, B, ID>(a: &BBoxSet, b: &BBoxSet, mut out: AnswerFormat<'a, ID>) -where +pub fn intersect_brute_force_flex<'a, B, ID>( + a: &BBoxSet, + b: &BBoxSet, + mut out: AnswerFormat<'a, ID>, +) where B: BBox, ID: Copy, { @@ -205,24 +212,23 @@ where for bidx in start..a.boxes.len() { let (bbox2, id2) = a.boxes[bidx]; if bbox.intersects(&bbox2) { - match out{ - AnswerFormat::Index(ref mut out)=>out.push((aidx, bidx)), - AnswerFormat::Ident(ref mut out)=>out.push((id, id2)), - AnswerFormat::Both(ref mut out)=>out.push(((aidx, aidx),(id, id2))), + match out { + AnswerFormat::Index(ref mut out) => out.push((aidx, bidx)), + AnswerFormat::Ident(ref mut out) => out.push((id, id2)), + AnswerFormat::Both(ref mut out) => out.push(((aidx, bidx), (id, id2))), } - } } start += 1; } } else { for (aidx, &(bbox, id)) in a.boxes.iter().enumerate() { - for (bidx, &(bbox2, id2)) in b.boxes.iter().enumerate() { + for (bidx, &(bbox2, id2)) in b.boxes.iter().enumerate() { if bbox.intersects(&bbox2) { - match out{ - AnswerFormat::Index(ref mut out)=>out.push((aidx, bidx)), - AnswerFormat::Ident(ref mut out)=>out.push((id, id2)), - AnswerFormat::Both(ref mut out)=>out.push(((aidx, bidx),(id, id2))), + match out { + AnswerFormat::Index(ref mut out) => out.push((aidx, bidx)), + AnswerFormat::Ident(ref mut out) => out.push((id, id2)), + AnswerFormat::Both(ref mut out) => out.push(((aidx, bidx), (id, id2))), } } }