diff --git a/src/moc/range/borrowed.rs b/src/moc/range/borrowed.rs new file mode 100644 index 0000000..ab9479f --- /dev/null +++ b/src/moc/range/borrowed.rs @@ -0,0 +1,47 @@ +use std::{marker::PhantomData, ops::Range}; + +use crate::{ + elemset::range::BorrowedMocRanges, + idx::Idx, + moc::{range::RangeRefMocIter, HasMaxDepth, NonOverlapping, RangeMOCIntoIterator, ZSorted}, + qty::MocQty, +}; + +pub struct BorrowedRangeMOC<'a, T: Idx, Q: MocQty> { + depth_max: u8, + ranges: BorrowedMocRanges<'a, T, Q>, +} + +impl<'a, T: Idx, Q: MocQty> BorrowedRangeMOC<'a, T, Q> { + pub fn new(depth_max: u8, ranges: BorrowedMocRanges<'a, T, Q>) -> Self { + Self { depth_max, ranges } + } +} + +impl<'a, T: Idx, Q: MocQty> HasMaxDepth for BorrowedRangeMOC<'a, T, Q> { + fn depth_max(&self) -> u8 { + self.depth_max + } +} +impl<'a, T: Idx, Q: MocQty> ZSorted for BorrowedRangeMOC<'a, T, Q> {} +impl<'a, T: Idx, Q: MocQty> NonOverlapping for BorrowedRangeMOC<'a, T, Q> {} + +impl<'a, T: Idx, Q: MocQty> RangeMOCIntoIterator for BorrowedRangeMOC<'a, T, Q> { + type Qty = Q; + type IntoRangeMOCIter = RangeRefMocIter<'a, T, Self::Qty>; + + fn into_range_moc_iter(self) -> Self::IntoRangeMOCIter { + let l = self.ranges.0 .0.len(); + let last: Option> = if l > 0 { + Some(self.ranges.0 .0[l - 1].clone()) + } else { + None + }; + RangeRefMocIter { + depth_max: self.depth_max, + iter: self.ranges.0 .0.iter(), + last, + _qty: PhantomData, + } + } +} diff --git a/src/moc/range/mod.rs b/src/moc/range/mod.rs index 63cec68..b50a989 100644 --- a/src/moc/range/mod.rs +++ b/src/moc/range/mod.rs @@ -1,4 +1,5 @@ use std::{ + cmp::Ordering, convert::{TryFrom, TryInto}, error::Error, fs::File, @@ -7,7 +8,7 @@ use std::{ num::TryFromIntError, ops::Range, path::Path, - slice, + slice::SliceIndex, vec::IntoIter, }; @@ -41,13 +42,17 @@ use crate::{ maxdepth_range::RangeMocBuilder, }, cell::CellMOC, - range::op::{ - and::{and, AndRangeIter}, - merge::merge_sorted, - minus::{minus, MinusRangeIter}, - multi_op::kway_or, - or::{or, OrRangeIter}, - xor::xor, + range::{ + borrowed::BorrowedRangeMOC, + op::{ + and::{and, AndRangeIter}, + merge::merge_sorted, + minus::{minus, MinusRangeIter}, + multi_op::kway_or, + or::{or, OrRangeIter}, + overlap::{overlapped_by, OverlapRangeIter}, + xor::xor, + }, }, CellMOCIntoIterator, CellMOCIterator, CellOrCellRangeMOCIterator, HasMaxDepth, MOCProperties, NonOverlapping, RangeMOCIntoIterator, RangeMOCIterator, ZSorted, @@ -55,7 +60,7 @@ use crate::{ qty::{Bounded, Frequency, Hpx, MocQty, Time}, ranges::{BorrowedRanges, Ranges, SNORanges}, }; - +pub mod borrowed; pub mod op; /// Structure made to draw MOCs in AladinLite. @@ -144,6 +149,17 @@ impl> RangeMOC { ranges: Ranges::new_unchecked(vec![range]).into(), } } + + /// Similar to what's Vec does: https://doc.rust-lang.org/src/core/slice/mod.rs.html#617-619 + pub fn select<'a, I>(&'a self, index: I) -> BorrowedRangeMOC<'a, T, Q> + where + I: SliceIndex<[Range], Output = [Range]>, + { + let r = &self.ranges.0 .0[index]; + let br = BorrowedRanges(r); + BorrowedRangeMOC::new(self.depth_max, br.into()) + } + pub fn depth_max(&self) -> u8 { self.depth_max } @@ -329,6 +345,43 @@ impl> RangeMOC { RangeMOC::new(depth_max, ranges) } + /// Returns an iterator over the ranges of self that are overlapping the rhs range MOC + pub fn overlapped_by_iter<'a>(&'a self, rhs: &'a RangeMOC) -> OverlappedByIter<'a, T, Q> { + let l = &self.ranges.0 .0; + let r = &rhs.ranges.0 .0; + + // Quick rejection test + let (il, ir) = if l.is_empty() + || r.is_empty() + || l[0].start >= r[r.len() - 1].end + || l[l.len() - 1].end <= r[0].start + { + (l.len() - 1, r.len() - 1) + } else { + // Use binary search to find the starting indices + match l[0].start.cmp(&r[0].start) { + Ordering::Less => { + let il = match l.binary_search_by(|l_range| l_range.start.cmp(&r[0].start)) { + Ok(i) => i, + Err(i) => i - 1, + }; + (il, 0) + } + Ordering::Greater => { + let ir = match r.binary_search_by(|r_range| r_range.start.cmp(&l[0].start)) { + Ok(i) => i, + Err(i) => i - 1, + }; + (0, ir) + } + Ordering::Equal => (0, 0), + } + }; + + let (left, right) = (self.select(il..), rhs.select(ir..)); + overlapped_by(left.into_range_moc_iter(), right.into_range_moc_iter()) + } + // CONTAINS: union that stops at first elem found // OVERLAP (=!CONTAINS on the COMPLEMENT ;) ) @@ -428,6 +481,10 @@ pub type ExtBorderIter<'a, T> = pub type IntBorderIter<'a, T> = AndRangeIter, RangeMocIter>, RangeRefMocIter<'a, T, Hpx>>; +/// Complex type returned by the `internal_border_iter` method. +pub type OverlappedByIter<'a, T, Q> = + OverlapRangeIter, RangeRefMocIter<'a, T, Q>>; + impl RangeMOC> { /// Returns `true` if the given coordinates (in radians) is in the MOC pub fn is_in(&self, lon: f64, lat: f64) -> bool { @@ -1433,7 +1490,7 @@ impl> RangeMOCIntoIterator for RangeMOC { /// Iterator borrowing the `RangeMOC` it iterates over. pub struct RangeRefMocIter<'a, T: Idx, Q: MocQty> { depth_max: u8, - iter: slice::Iter<'a, Range>, + iter: std::slice::Iter<'a, Range>, last: Option>, _qty: PhantomData, } @@ -1462,6 +1519,7 @@ impl<'a, T: Idx, Q: MocQty> RangeMOCIterator for RangeRefMocIter<'a, T, Q> self.last.as_ref() } } + impl<'a, T: Idx, Q: MocQty> RangeMOCIntoIterator for &'a RangeMOC { type Qty = Q; type IntoRangeMOCIter = RangeRefMocIter<'a, T, Self::Qty>; diff --git a/src/moc/range/op/mod.rs b/src/moc/range/op/mod.rs index 22c5bdd..b5de68a 100644 --- a/src/moc/range/op/mod.rs +++ b/src/moc/range/op/mod.rs @@ -13,3 +13,5 @@ pub mod or; // <=> union pub mod xor; // <=> Aladin Difference pub mod multi_op; + +pub mod overlap; diff --git a/src/moc/range/op/overlap.rs b/src/moc/range/op/overlap.rs new file mode 100644 index 0000000..2bf1025 --- /dev/null +++ b/src/moc/range/op/overlap.rs @@ -0,0 +1,230 @@ +use std::ops::Range; + +use crate::idx::Idx; +use crate::moc::{HasMaxDepth, MOCProperties, NonOverlapping, RangeMOCIterator, ZSorted}; +use crate::qty::MocQty; + +/// Performs a logical `OR` between the two input iterators of ranges. +pub fn overlapped_by(left_it: I1, right_it: I2) -> OverlapRangeIter +where + T: Idx, + Q: MocQty, + I1: RangeMOCIterator, + I2: RangeMOCIterator, +{ + OverlapRangeIter::new(left_it, right_it) +} + +/// Returns in a on-the-fly manner the ranges of `left_it` that overlap those in I2. +pub struct OverlapRangeIter +where + T: Idx, + Q: MocQty, + I1: RangeMOCIterator, + I2: RangeMOCIterator, +{ + left_it: I1, + right_it: I2, + left: Option>, + right: Option>, +} + +impl OverlapRangeIter +where + T: Idx, + Q: MocQty, + I1: RangeMOCIterator, + I2: RangeMOCIterator, +{ + fn new(mut left_it: I1, mut right_it: I2) -> OverlapRangeIter { + let left = left_it.next(); + let right = right_it.next(); + + // Quick rejection tests + if let (Some(up_left), Some(low_right)) = (left_it.peek_last(), &right) { + if up_left.end <= low_right.start { + return OverlapRangeIter { + left_it, + right_it, + left: None, + right: None, + }; + } + } + if let (Some(low_left), Some(up_right)) = (&left, right_it.peek_last()) { + if up_right.end <= low_left.start { + return OverlapRangeIter { + left_it, + right_it, + left: None, + right: None, + }; + } + } + // Normal behaviour + OverlapRangeIter { + left_it, + right_it, + left, + right, + } + } +} + +impl HasMaxDepth for OverlapRangeIter +where + T: Idx, + Q: MocQty, + I1: RangeMOCIterator, + I2: RangeMOCIterator, +{ + fn depth_max(&self) -> u8 { + self.left_it.depth_max() + } +} + +impl ZSorted for OverlapRangeIter +where + T: Idx, + Q: MocQty, + I1: RangeMOCIterator, + I2: RangeMOCIterator, +{ +} +impl NonOverlapping for OverlapRangeIter +where + T: Idx, + Q: MocQty, + I1: RangeMOCIterator, + I2: RangeMOCIterator, +{ +} + +impl MOCProperties for OverlapRangeIter +where + T: Idx, + Q: MocQty, + I1: RangeMOCIterator, + I2: RangeMOCIterator, +{ +} + +impl Iterator for OverlapRangeIter +where + T: Idx, + Q: MocQty, + I1: RangeMOCIterator, + I2: RangeMOCIterator, +{ + type Item = Range; + + fn next(&mut self) -> Option { + while let (Some(l), Some(r)) = (&self.left, &self.right) { + if l.end <= r.start { + // |--l--| |--r--| + self.left = self.left_it.next(); + while let Some(l) = &self.left { + if l.end <= r.start { + self.left = self.left_it.next(); + } else { + break; + } + } + } else if r.end <= l.start { + // |--r--| |--l--| + self.right = self.right_it.next(); + while let Some(r) = &self.right { + if r.end <= l.start { + self.right = self.right_it.next(); + } else { + break; + } + } + } else { + // Overlapping case between l and r + // TODO: I had to clone it because I return Range but here this iterator could just return a &Range + let range = l.clone(); + self.left = self.left_it.next(); + // We return that range from left_it, it should give us all the ranges from left_it + // that are overlapping with right_it + return Some(range); + } + } + None + } + + fn size_hint(&self) -> (usize, Option) { + self.left_it.size_hint() + } +} + +impl RangeMOCIterator for OverlapRangeIter +where + T: Idx, + Q: MocQty, + I1: RangeMOCIterator, + I2: RangeMOCIterator, +{ + type Qty = Q; + + fn peek_last(&self) -> Option<&Range> { + // We could have considered the case in which the upper bound is the same for both inputs + None + } +} + +#[cfg(test)] +mod tests { + use std::fs::File; + use std::io::BufReader; + use std::path::PathBuf; + + use crate::deser::fits::{from_fits_ivoa, MocIdxType, MocQtyType, MocType}; + use crate::moc::range::op::overlap::overlapped_by; + use crate::moc::range::RangeMOC; + use crate::moc::{CellMOCIntoIterator, CellMOCIterator, HasMaxDepth, RangeMOCIntoIterator}; + use crate::qty::Hpx; + + fn load_moc(filename: &str) -> RangeMOC> { + let path_buf1 = PathBuf::from(format!("resources/{}", filename)); + let path_buf2 = PathBuf::from(format!("../resources/{}", filename)); + let file = File::open(&path_buf1) + .or_else(|_| File::open(&path_buf2)) + .unwrap(); + let reader = BufReader::new(file); + match from_fits_ivoa(reader) { + Ok(MocIdxType::U32(MocQtyType::Hpx(MocType::Ranges(moc)))) => { + let moc = RangeMOC::new(moc.depth_max(), moc.collect()); + moc + } + Ok(MocIdxType::U32(MocQtyType::Hpx(MocType::Cells(moc)))) => { + let moc = RangeMOC::new(moc.depth_max(), moc.into_cell_moc_iter().ranges().collect()); + moc + } + _ => unreachable!(), + } + } + + fn load_mocs() -> (RangeMOC>, RangeMOC>) { + let sdss = load_moc("V_147_sdss12.moc.fits"); + let other = load_moc("CDS-I-125A-catalog_MOC.fits"); + (sdss, other) + } + + // we could also perform the operation without having first collected the iteartor we obtain from + // the FITS file + #[test] + fn overlap_it() { + let (moc_l, moc_r) = load_mocs(); + let overlap_it = overlapped_by( + (&moc_l).into_range_moc_iter(), + (&moc_r).into_range_moc_iter(), + ); + let overlap = RangeMOC::>::new(overlap_it.depth_max(), overlap_it.collect()); + + assert_eq!( + overlap.moc_ranges().0 .0.len() <= moc_l.moc_ranges().0 .0.len(), + true + ); + } +}