use triomphe::Arc;
use crate::coords::LonLatT;
pub mod iter;
mod ops;
#[cfg(test)]
mod tests;
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
pub struct MutableBmoc<const VALID: bool = true> {
max_depth: u8,
entries: Vec<u64>,
}
impl Bmoc for MutableBmoc<true> {
#[inline(always)]
fn max_depth(&self) -> u8 {
self.max_depth
}
#[inline(always)]
fn entries(&self) -> &[u64] {
&self.entries
}
}
impl<const VALID: bool> MutableBmoc<VALID> {
#[inline(always)]
pub const fn new_empty(depth: u8) -> Self {
Self {
max_depth: depth,
entries: Vec::new(),
}
}
pub fn new_allsky(depth: u8) -> Self {
let mut out = MutableBmoc::<false>::new_empty(depth);
out.push_all_unchecked(0, 0, 12, true);
out.into_whatever_unchecked()
}
pub fn with_capacity(max_depth: u8, capacity: usize) -> Self {
Self {
max_depth,
entries: Vec::with_capacity(capacity),
}
}
pub const fn max_depth(&self) -> u8 {
self.max_depth
}
pub fn entries(&self) -> &[u64] {
&self.entries
}
pub fn take(&mut self) -> Self {
std::mem::take(self)
}
pub const fn as_valid_unchecked(&self) -> &MutableBmoc<true> {
unsafe { std::mem::transmute(self) }
}
pub const fn as_invalid(&self) -> &MutableBmoc<false> {
unsafe { std::mem::transmute(self) }
}
pub const fn as_valid_unchecked_mut(&mut self) -> &mut MutableBmoc<true> {
unsafe { std::mem::transmute(self) }
}
pub const fn as_invalid_unchecked_mut(&mut self) -> &mut MutableBmoc<false> {
unsafe { std::mem::transmute(self) }
}
pub const fn into_invalid(self) -> MutableBmoc<false> {
unsafe { std::mem::transmute(self) }
}
pub const fn into_valid_unchecked(self) -> MutableBmoc<true> {
unsafe { std::mem::transmute(self) }
}
pub const fn into_whatever_unchecked<const V2: bool>(self) -> MutableBmoc<V2> {
unsafe { std::mem::transmute(self) }
}
pub fn pack(&mut self) {
if !VALID {
self.entries.sort();
let mut prev_to_index = 0_usize;
let mut curr_to_index = self.entries.len();
while prev_to_index != curr_to_index {
prev_to_index = curr_to_index;
let mut i_prev_moc = 0_usize;
let mut i_curr_moc = 0_usize;
while i_prev_moc < prev_to_index {
let mut curr_cell = self.entries[i_prev_moc];
i_prev_moc += 1;
let mut curr_cell_depth = get_depth(curr_cell, self.max_depth);
let mut curr_cell_hash =
get_hash_from_delta_depth(curr_cell, self.max_depth - curr_cell_depth);
while i_prev_moc < prev_to_index
&& (curr_cell_depth == 0
|| is_partial(curr_cell)
|| is_not_first_cell_of_larger_cell(curr_cell_hash))
{
if i_curr_moc != i_prev_moc {
self.entries[i_curr_moc] = curr_cell;
i_curr_moc += 1;
}
curr_cell = self.entries[i_prev_moc];
i_prev_moc += 1;
curr_cell_depth = get_depth(curr_cell, self.max_depth);
curr_cell_hash =
get_hash_from_delta_depth(curr_cell, self.max_depth - curr_cell_depth);
}
if i_prev_moc + 2 < prev_to_index
&& self.entries[i_prev_moc]
== encode_raw_value(
curr_cell_depth,
curr_cell_hash | 1,
true,
self.max_depth,
)
&& self.entries[i_prev_moc + 1]
== encode_raw_value(
curr_cell_depth,
curr_cell_hash | 2,
true,
self.max_depth,
)
&& self.entries[i_prev_moc + 2]
== encode_raw_value(
curr_cell_depth,
curr_cell_hash | 3,
true,
self.max_depth,
)
{
self.entries[i_curr_moc] = encode_raw_value(
curr_cell_depth - 1,
curr_cell_hash >> 2,
true,
self.max_depth,
);
i_curr_moc += 1;
i_prev_moc += 3;
} else if i_curr_moc != i_prev_moc {
self.entries[i_curr_moc] = curr_cell;
i_curr_moc += 1;
}
}
curr_to_index = i_curr_moc;
}
self.entries.truncate(curr_to_index);
}
}
pub fn into_packed(mut self) -> MutableBmoc<true> {
self.pack();
self.into_valid_unchecked()
}
}
impl MutableBmoc<true> {
pub fn into_shared(self) -> SharedBmoc {
SharedBmoc {
max_depth: self.max_depth,
entries: self.entries.into(),
}
}
fn low_depth_raw_val_at_lower_depth(&self, raw_value: u64, new_depth: u8) -> u64 {
debug_assert!(get_depth(raw_value, self.max_depth) <= new_depth);
debug_assert!(new_depth <= self.max_depth);
let twice_delta_depth = (self.max_depth - new_depth) << 1;
(raw_value >> twice_delta_depth) | (raw_value & 1_u64)
}
pub fn lower_depth(&mut self, new_depth: u8) -> &mut Self {
assert!(
new_depth < self.max_depth,
"The given depth must be lower than the depth max of the BMOC"
);
let mut i_new = 0_usize;
let mut prev_hash_at_new_depth = loop {
if i_new == self.entries.len() {
break None;
}
let raw_value = self.entries[i_new];
let depth = get_depth(raw_value, self.max_depth);
if depth <= new_depth {
self.entries[i_new] = self.low_depth_raw_val_at_lower_depth(raw_value, new_depth);
i_new += 1;
} else {
break Some(get_hash_from_delta_depth(
raw_value,
self.max_depth - new_depth,
));
}
};
for i in (i_new + 1)..self.entries.len() {
let raw_value = self.entries[i];
let depth = get_depth(raw_value, self.max_depth);
if depth <= new_depth {
if prev_hash_at_new_depth.is_some() {
self.entries[i_new] = (prev_hash_at_new_depth.take().unwrap() << 2) | 2_u64;
i_new += 1;
}
self.entries[i_new] = self.low_depth_raw_val_at_lower_depth(raw_value, new_depth);
i_new += 1;
} else {
let curr_hash_at_new_depth =
get_hash_from_delta_depth(raw_value, self.max_depth - new_depth);
if let Some(prev_val_at_new_depth) = prev_hash_at_new_depth {
if prev_val_at_new_depth != curr_hash_at_new_depth {
self.entries[i_new] = (prev_val_at_new_depth << 2) | 2_u64; i_new += 1;
prev_hash_at_new_depth.replace(curr_hash_at_new_depth);
}
} else {
prev_hash_at_new_depth.replace(curr_hash_at_new_depth);
}
}
}
if prev_hash_at_new_depth.is_some() {
self.entries[i_new] = (prev_hash_at_new_depth.take().unwrap() << 2) | 2_u64;
i_new += 1;
}
self.entries.truncate(i_new);
self.max_depth = new_depth;
self
}
#[inline(always)]
pub fn into_cells(self) -> iter::CellIter<iter::VecIter> {
iter::CellIter::new(self.max_depth, self.entries.into_iter())
}
#[inline(always)]
pub fn into_flat_cells(self) -> iter::FlatCellIter<iter::VecIter> {
iter::FlatCellIter::new(self.max_depth, self.deep_size(), self.entries.into_iter())
}
#[inline(always)]
pub fn into_flat_iter(self) -> iter::FlatIter<iter::VecIter> {
iter::FlatIter::new(self.max_depth, self.deep_size(), self.entries.into_iter())
}
#[inline(always)]
pub fn into_ranges(self) -> iter::RangeIter<iter::VecIter> {
iter::RangeIter::new(self.max_depth, self.entries.into_iter())
}
#[inline(always)]
pub fn into_flagged_ranges(self) -> iter::FlaggedRangeIter<iter::VecIter> {
iter::FlaggedRangeIter::new(self.max_depth, self.entries.into_iter())
}
}
impl MutableBmoc<false> {
pub const fn new(max_depth: u8, entries: Vec<u64>) -> Self {
Self { max_depth, entries }
}
pub const fn set_max_depth(&mut self, depth: u8) -> &mut Self {
self.max_depth = depth;
self
}
pub const fn entries_mut(&mut self) -> &mut Vec<u64> {
&mut self.entries
}
pub fn push_unchecked(&mut self, depth: u8, hash: u64, is_full: bool) -> &mut Self {
self.entries
.push(encode_raw_value(depth, hash, is_full, self.max_depth));
self
}
pub fn push_all_unchecked(
&mut self,
depth: u8,
hash_min: u64,
hash_max: u64,
is_full: bool,
) -> &mut Self {
debug_assert!(
hash_max >= hash_min,
"Invalid hash range: {hash_min} > {hash_max}"
);
self.entries.reserve((hash_max - hash_min) as _);
self.entries.extend(
(hash_min..hash_max).map(|hash| encode_raw_value(depth, hash, is_full, self.max_depth)),
);
self
}
pub fn push_raw_unchecked(&mut self, raw_value: u64) -> &mut Self {
self.entries.push(raw_value);
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SharedBmoc {
pub max_depth: u8,
pub entries: Arc<[u64]>,
}
impl Bmoc for SharedBmoc {
#[inline(always)]
fn max_depth(&self) -> u8 {
self.max_depth
}
#[inline(always)]
fn entries(&self) -> &[u64] {
&self.entries
}
}
impl From<MutableBmoc> for SharedBmoc {
fn from(value: MutableBmoc) -> Self {
value.into_shared()
}
}
impl Default for SharedBmoc {
fn default() -> Self {
Self {
max_depth: 0,
entries: std::iter::empty().collect(),
}
}
}
impl SharedBmoc {
#[inline(always)]
pub fn new_empty(depth: u8) -> Self {
MutableBmoc::new_empty(depth).into_shared()
}
#[inline(always)]
pub fn new_allsky(depth: u8) -> Self {
MutableBmoc::new_allsky(depth).into_shared()
}
#[inline(always)]
pub fn into_cells(self) -> iter::CellIter<iter::ArcIter> {
iter::CellIter::new(self.max_depth, iter::ArcIter::new(self.entries))
}
#[inline(always)]
pub fn into_flat_cells(self) -> iter::FlatCellIter<iter::ArcIter> {
iter::FlatCellIter::new(
self.max_depth,
self.deep_size(),
iter::ArcIter::new(self.entries),
)
}
#[inline(always)]
pub fn into_flat_iter(self) -> iter::FlatIter<iter::ArcIter> {
iter::FlatIter::new(
self.max_depth,
self.deep_size(),
iter::ArcIter::new(self.entries),
)
}
#[inline(always)]
pub fn into_ranges(self) -> iter::RangeIter<iter::ArcIter> {
iter::RangeIter::new(self.max_depth, iter::ArcIter::new(self.entries))
}
#[inline(always)]
pub fn into_flagged_ranges(self) -> iter::FlaggedRangeIter<iter::ArcIter> {
iter::FlaggedRangeIter::new(self.max_depth, iter::ArcIter::new(self.entries))
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BorrowedBmoc<'a> {
pub max_depth: u8,
pub entries: &'a [u64],
}
impl Bmoc for BorrowedBmoc<'_> {
#[inline(always)]
fn max_depth(&self) -> u8 {
self.max_depth
}
#[inline(always)]
fn entries(&self) -> &[u64] {
self.entries
}
}
impl<'a> BorrowedBmoc<'a> {
#[inline(always)]
pub fn new<B: Bmoc + ?Sized>(from: &'a B) -> Self {
Self {
max_depth: from.max_depth(),
entries: from.entries(),
}
}
#[inline(always)]
pub fn into_cells(&self) -> iter::CellIter<iter::SliceIter<'a>> {
iter::CellIter::new(self.max_depth, self.entries.iter().copied())
}
#[inline(always)]
pub fn into_flat_cells(&self) -> iter::FlatCellIter<iter::SliceIter<'a>> {
iter::FlatCellIter::new(
self.max_depth,
self.deep_size(),
self.entries.iter().copied(),
)
}
#[inline(always)]
pub fn into_flat_iter(&self) -> iter::FlatIter<iter::SliceIter<'a>> {
iter::FlatIter::new(
self.max_depth,
self.deep_size(),
self.entries.iter().copied(),
)
}
#[inline(always)]
pub fn into_ranges(&self) -> iter::RangeIter<iter::SliceIter<'a>> {
iter::RangeIter::new(self.max_depth, self.entries.iter().copied())
}
#[inline(always)]
pub fn into_flagged_ranges(&self) -> iter::FlaggedRangeIter<iter::SliceIter<'a>> {
iter::FlaggedRangeIter::new(self.max_depth, self.entries.iter().copied())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Status {
Out,
Unknown,
In,
}
pub trait Bmoc {
fn max_depth(&self) -> u8;
fn entries(&self) -> &[u64];
#[inline(always)]
fn as_borrowed(&self) -> BorrowedBmoc<'_> {
BorrowedBmoc::new(self)
}
#[inline(always)]
fn cells(&self) -> iter::CellIter<iter::SliceIter<'_>> {
iter::CellIter::new(self.max_depth(), self.entries().iter().copied())
}
#[inline(always)]
fn flat_cells(&self) -> iter::FlatCellIter<iter::SliceIter<'_>> {
iter::FlatCellIter::new(
self.max_depth(),
self.deep_size(),
self.entries().iter().copied(),
)
}
#[inline(always)]
fn flat_iter(&self) -> iter::FlatIter<iter::SliceIter<'_>> {
iter::FlatIter::new(
self.max_depth(),
self.deep_size(),
self.entries().iter().copied(),
)
}
#[inline(always)]
fn ranges(&self) -> iter::RangeIter<iter::SliceIter<'_>> {
iter::RangeIter::new(self.max_depth(), self.entries().iter().copied())
}
#[inline(always)]
fn flagged_ranges(&self) -> iter::FlaggedRangeIter<iter::SliceIter<'_>> {
iter::FlaggedRangeIter::new(self.max_depth(), self.entries().iter().copied())
}
#[inline(always)]
fn size(&self) -> usize {
self.entries().len()
}
#[inline(always)]
fn deep_size(&self) -> usize {
let max_depth = self.max_depth();
self.entries()
.iter()
.map(|&raw| {
crate::unchecked::nside_square(max_depth - get_depth(raw, max_depth)) as usize
})
.sum()
}
#[inline(always)]
fn gen_eq<B2: Bmoc + ?Sized>(&self, other: &B2) -> bool
where
Self: Sized,
{
self.max_depth() == other.max_depth() && self.entries() == other.entries()
}
#[inline(always)]
fn test_coo_dyn(&self, lon: f64, lat: f64) -> Status {
test_coo(self.max_depth(), self.entries(), lon, lat)
}
#[inline(always)]
fn test_coo(&self, coords: impl LonLatT) -> Status
where
Self: Sized,
{
test_coo(self.max_depth(), self.entries(), coords.lon(), coords.lat())
}
#[inline(always)]
fn test_cell(&self, depth: u8, hash: u64) -> Status {
test_cell(self.max_depth(), self.entries(), depth, hash)
}
#[inline(always)]
fn not(&self) -> MutableBmoc {
ops::not(self.max_depth(), self.entries())
}
#[inline(always)]
fn and_dyn(&self, rhs: BorrowedBmoc) -> MutableBmoc {
ops::and(self.as_borrowed(), rhs)
}
#[inline(always)]
fn and<B2: Bmoc + ?Sized>(&self, rhs: &B2) -> MutableBmoc
where
Self: Sized,
{
ops::and(self.as_borrowed(), rhs.as_borrowed())
}
#[inline(always)]
fn or_dyn(&self, rhs: BorrowedBmoc) -> MutableBmoc {
ops::or(self.as_borrowed(), rhs)
}
fn or<B2: Bmoc + ?Sized>(&self, rhs: &B2) -> MutableBmoc
where
Self: Sized,
{
ops::or(self.as_borrowed(), rhs.as_borrowed())
}
#[inline(always)]
fn xor_dyn(&self, rhs: BorrowedBmoc) -> MutableBmoc {
ops::xor(self.as_borrowed(), rhs)
}
fn xor<B2: Bmoc + ?Sized>(&self, rhs: &B2) -> MutableBmoc
where
Self: Sized,
{
ops::xor(self.as_borrowed(), rhs.as_borrowed())
}
#[inline(always)]
fn minus_dyn(&self, rhs: BorrowedBmoc) -> MutableBmoc {
ops::minus(self.as_borrowed(), rhs)
}
fn minus<B2: Bmoc + ?Sized>(&self, rhs: &B2) -> MutableBmoc
where
Self: Sized,
{
ops::minus(self.as_borrowed(), rhs.as_borrowed())
}
}
impl<T: Bmoc> Bmoc for &T {
fn max_depth(&self) -> u8 {
T::max_depth(self)
}
fn entries(&self) -> &[u64] {
T::entries(self)
}
}
impl PartialEq for dyn Bmoc {
fn eq(&self, other: &Self) -> bool {
self.max_depth() == other.max_depth() && self.entries() == other.entries()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Cell {
pub raw_value: u64,
pub depth: u8,
pub hash: u64,
pub is_full: bool,
}
impl Cell {
pub const fn decode(raw_value: u64, max_depth: u8) -> Cell {
let is_full = (raw_value & 1_u64) == 1_u64;
let delta_depth = ((raw_value >> 1).trailing_zeros() >> 1) as u8;
let hash = raw_value >> (2 + (delta_depth << 1));
let depth = max_depth - delta_depth;
Cell {
raw_value,
depth,
hash,
is_full,
}
}
}
#[inline]
pub const fn encode_raw_value(depth: u8, hash: u64, is_full: bool, max_depth: u8) -> u64 {
let mut hash = (hash << 1) | 1_u64;
hash <<= 1 + ((max_depth - depth) << 1);
hash | (is_full as u64)
}
#[inline]
const fn rm_flag(raw_value: u64) -> u64 {
raw_value >> 1
}
#[inline]
const fn is_partial(raw_value: u64) -> bool {
(raw_value & 1_u64) == 0_u64
}
#[inline]
const fn is_not_first_cell_of_larger_cell(hash: u64) -> bool {
(hash & 3_u64) != 0_u64
}
#[inline]
const fn get_depth(raw_value: u64, max_depth: u8) -> u8 {
get_depth_no_flag(rm_flag(raw_value), max_depth)
}
#[inline]
const fn get_depth_no_flag(raw_value_no_flag: u64, max_depth: u8) -> u8 {
max_depth - (raw_value_no_flag.trailing_zeros() >> 1) as u8
}
#[inline]
const fn get_hash_from_delta_depth(raw_value: u64, delta_depth: u8) -> u64 {
raw_value >> (2 + (delta_depth << 1))
}
#[inline]
fn is_in(low_resolution: &Cell, high_resolution: &Cell) -> bool {
low_resolution.depth <= high_resolution.depth
&& low_resolution.hash
== (high_resolution.hash >> ((high_resolution.depth - low_resolution.depth) << 1))
}
#[inline(never)]
fn test_coo(max_depth: u8, entries: &[u64], lon: f64, lat: f64) -> Status {
let h_raw = encode_raw_value(
max_depth,
crate::Layer::get(max_depth).hash([lon, lat]),
true,
max_depth,
);
match entries.binary_search(&h_raw) {
Ok(i) => {
if is_partial(entries[i]) {
Status::Unknown
} else {
Status::In
}
}
Err(i) => {
let cell = Cell::decode(h_raw, max_depth);
if i > 0 && is_in(&Cell::decode(entries[i - 1], max_depth), &cell) {
if is_partial(entries[i - 1]) {
Status::Unknown
} else {
Status::In
}
} else if i < entries.len() && is_in(&Cell::decode(entries[i], max_depth), &cell) {
if is_partial(entries[i]) {
Status::Unknown
} else {
Status::In
}
} else {
Status::Out
}
}
}
}
#[inline(never)]
fn test_cell(max_depth: u8, entries: &[u64], depth: u8, hash: u64) -> Status {
let h_raw = encode_raw_value(depth, hash, true, max_depth);
match entries.binary_search(&h_raw) {
Ok(i) => {
if is_partial(entries[i]) {
Status::Unknown
} else {
Status::In
}
}
Err(i) => {
let cell = Cell::decode(h_raw, max_depth);
if i > 0 && is_in(&Cell::decode(entries[i - 1], max_depth), &cell) {
if is_partial(entries[i - 1]) {
Status::Unknown
} else {
Status::In
}
} else if i < entries.len() && is_in(&Cell::decode(entries[i], max_depth), &cell) {
if is_partial(entries[i]) {
Status::Unknown
} else {
Status::In
}
} else {
Status::Out
}
}
}
}