#![allow(clippy::redundant_pub_crate)]
use core::cmp::Ordering;
use core::ops::{Bound, RangeBounds};
use crate::word::Word;
pub(crate) trait Quadrants {
type Context<W>;
fn digit(frame: u8, dx: u8, dy: u8) -> (u8, u8);
fn child(k: u32, frame: u8, p: u32) -> (u8, u8, u8);
fn cells(k: u32, frame: u8, cols: usize, rows: usize) -> u64;
fn context<W: Word>(r: &Rect<W>) -> Self::Context<W>;
fn runs<W: Word>(levels: u32, r: &Rect<W>, s: u32, context: &Self::Context<W>) -> W;
}
pub(crate) struct Masks<const F: usize> {
child: [[(u8, u8, u8); 84]; F],
cols: [[u64; 276]; F],
rows: [[u64; 276]; F],
}
const CHILD: [usize; 4] = [0, 0, 4, 20];
const COLS: [usize; 4] = [0, 0, 4, 20];
impl<const F: usize> Masks<F> {
#[allow(clippy::cast_possible_truncation, clippy::many_single_char_names)]
pub(crate) const fn build(digit: [[(u8, u8); 4]; F]) -> Self {
let mut t = Self {
child: [[(0, 0, 0); 84]; F],
cols: [[0; 276]; F],
rows: [[0; 276]; F],
};
let mut k = 1;
while k <= 3 {
let side = 1u8 << k;
let mut f = 0;
while f < F {
let (mut col, mut row) = ([0u64; 8], [0u64; 8]);
let mut cx = 0;
while cx < side {
let mut cy = 0;
while cy < side {
let (mut p, mut g) = (0, f as u8);
let mut l = k;
while l > 0 {
l -= 1;
let q = ((cx >> l) & 1) | ((cy >> l) & 1) << 1;
let (d, below) = digit[g as usize][q as usize];
p = p << 2 | d as usize;
g = below;
}
t.child[f][CHILD[k] + p] = (cx, cy, g);
col[cx as usize] |= 1 << p;
row[cy as usize] |= 1 << p;
cy += 1;
}
cx += 1;
}
let mut m = 0;
while m < 1 << side {
let mut c = 0;
while c < side as usize {
if m >> c & 1 == 1 {
t.cols[f][COLS[k] + m] |= col[c];
t.rows[f][COLS[k] + m] |= row[c];
}
c += 1;
}
m += 1;
}
f += 1;
}
k += 1;
}
t
}
#[inline]
pub(crate) const fn child(&self, k: u32, frame: u8, p: u32) -> (u8, u8, u8) {
self.child[frame as usize][CHILD[k as usize] + p as usize]
}
#[inline]
pub(crate) const fn cells(&self, k: u32, frame: u8, cols: usize, rows: usize) -> u64 {
let (f, o) = (frame as usize, COLS[k as usize]);
self.cols[f][o + cols] & self.rows[f][o + rows]
}
}
trait Sink<W: Word> {
fn emit(&mut self, first: W, last: W);
#[inline]
fn emit_mask(&mut self, key: W, mut m: u64, span: u32) {
while m != 0 {
let p = m.trailing_zeros();
let len = (m >> p).trailing_ones();
let first = key.or(small::<W>(p).shl(span));
let last = key
.or(small::<W>(p + len - 1).shl(span))
.or(W::low_ones(span));
self.emit(first, last);
m &= !(u64::MAX >> (64 - len) << p);
}
}
}
struct Gaps<'a, W> {
out: &'a mut [(W, W)],
runs: usize,
last: Option<W>,
lengths: [u32; 129],
least: u32,
}
impl<W: Word> Sink<W> for Gaps<'_, W> {
fn emit(&mut self, first: W, last: W) {
match self.last {
Some(end) if end != W::ONES && end.wrapping_add(W::ONE) == first => {}
Some(end) => {
let gap = first.wrapping_sub(end);
set(self.out, self.runs - 1, gap);
let bits = bitlen(gap);
self.lengths[bits as usize] += 1;
self.least = self.least.min(bits);
self.runs += 1;
}
None => self.runs += 1,
}
self.last = Some(last);
}
}
const fn get<W: Copy>(v: &[(W, W)], i: usize) -> W {
if i.is_multiple_of(2) {
v[i / 2].0
} else {
v[i / 2].1
}
}
fn set<W>(v: &mut [(W, W)], i: usize, x: W) {
if i.is_multiple_of(2) {
v[i / 2].0 = x;
} else {
v[i / 2].1 = x;
}
}
#[allow(clippy::many_single_char_names)]
fn select<W: Copy + Ord>(v: &mut [(W, W)], len: usize, m: usize) -> W {
let (mut lo, mut hi) = (0, len - 1);
loop {
if lo == hi {
return get(v, lo);
}
let pivot = get(v, lo + (hi - lo) / 2);
let (mut lt, mut i, mut gt) = (lo, lo, hi);
while i <= gt {
let x = get(v, i);
match x.cmp(&pivot) {
Ordering::Less => {
let y = get(v, lt);
set(v, lt, x);
set(v, i, y);
lt += 1;
i += 1;
}
Ordering::Greater => {
let y = get(v, gt);
set(v, gt, x);
set(v, i, y);
gt -= 1;
}
Ordering::Equal => i += 1,
}
}
if m < lt {
hi = lt - 1;
} else if m > gt {
lo = gt + 1;
} else {
return pivot;
}
}
}
#[allow(clippy::many_single_char_names, clippy::cast_possible_truncation)]
fn threshold<W: Word>(
v: &mut [(W, W)],
gaps: usize,
(lengths, least): (&[u32; 129], u32),
close: usize,
) -> (W, usize) {
let (mut below, mut b) = (0, least as usize);
while below + (lengths[b] as usize) < close {
below += lengths[b] as usize;
b += 1;
}
let mut need = close - below;
let bits = b as u32;
let class = |g: W| usize::from(if bits > 4 { g.shr(bits - 4) } else { g }.low_byte() & 7);
let (mut count, mut lo, mut hi) = ([0usize; 8], [W::ONES; 8], [W::ZERO; 8]);
for i in 0..gaps {
let g = get(v, i);
if bitlen(g) == bits {
let c = class(g);
count[c] += 1;
lo[c] = lo[c].min(g);
hi[c] = hi[c].max(g);
}
}
let mut c = 0;
while count[c] < need {
need -= count[c];
c += 1;
}
if lo[c] == hi[c] {
return (lo[c], need);
}
let mut k = 0;
for i in 0..gaps {
let g = get(v, i);
if bitlen(g) == bits && class(g) == c {
set(v, i, get(v, k));
set(v, k, g);
k += 1;
}
}
let t = select(v, k, need - 1);
let less = (0..k).filter(|&i| get(v, i) < t).count();
(t, need - less)
}
struct Merge<'a, W> {
out: &'a mut [(W, W)],
n: usize,
threshold: W,
ties: usize,
}
impl<W: Word> Sink<W> for Merge<'_, W> {
fn emit(&mut self, first: W, last: W) {
if self.n > 0 {
let end = self.out[self.n - 1].1;
let touch = end != W::ONES && end.wrapping_add(W::ONE) == first;
let gap = first.wrapping_sub(end);
let close = touch
|| gap < self.threshold
|| (gap == self.threshold && self.ties > 0 && {
self.ties -= 1;
true
});
if close {
self.out[self.n - 1].1 = last;
return;
}
}
self.out[self.n] = (first, last);
self.n += 1;
}
}
#[allow(clippy::cast_possible_truncation)]
#[inline]
pub(crate) fn small<W: Word>(v: u32) -> W {
debug_assert!(v < 256);
W::splat_byte(v as u8).and(W::low_ones(8))
}
#[inline]
pub(crate) fn bitlen<W: Word>(v: W) -> u32 {
W::BITS - v.leading_zeros()
}
#[allow(clippy::cast_possible_truncation)]
fn from_usize<W: Word>(v: usize) -> W {
if usize::BITS - v.leading_zeros() > W::BITS {
return W::ONES;
}
let mut w = W::ZERO;
let mut i = 0;
while i < usize::BITS && i < W::BITS {
w = w.or(small::<W>(((v >> i) & 0xFF) as u32).shl(i));
i += 8;
}
w
}
pub(crate) struct Rect<W> {
pub(crate) x0: W,
pub(crate) x1: W,
pub(crate) y0: W,
pub(crate) y1: W,
}
#[allow(clippy::many_single_char_names)]
#[inline]
fn axis<W: Word>(o: W, (a, b): (W, W), below: u32, k: u32) -> (usize, usize) {
if b < o {
return (0, 0);
}
let n = 1u32 << k;
let clamp = |v: W| {
if v >= small(n) {
n
} else {
u32::from(v.low_byte())
}
};
let span = |lo: u32, hi: u32| {
if lo > hi {
0
} else {
((2usize << hi) - 1) & !((1usize << lo) - 1)
}
};
let d = b.wrapping_sub(o);
let met = clamp(d.shr(below)).min(n - 1);
let past = clamp(d.wrapping_add(W::ONE).shr(below)).min(n);
let (first_met, first_in) = if a > o {
let d = a.wrapping_sub(o);
(
clamp(d.shr(below)),
clamp(d.wrapping_sub(W::ONE).shr(below)) + 1,
)
} else {
(0, 0)
};
let inside = if past == 0 {
0
} else {
span(first_in, past - 1)
};
(span(first_met, met), inside)
}
#[allow(clippy::too_many_arguments)]
fn walk<W: Word, C: Quadrants, S: Sink<W>>(
r: &Rect<W>,
level: u32,
stop: u32,
key: W,
(ox, oy): (W, W),
frame: u8,
sink: &mut S,
) {
let k = match (level - stop) % 3 {
0 => 3,
k => k,
};
let below = level - k;
let (cols, cols_in) = axis(ox, (r.x0, r.x1), below, k);
let (rows, rows_in) = axis(oy, (r.y0, r.y1), below, k);
let met = C::cells(k, frame, cols, rows);
let span = 2 * below;
if below == stop {
return sink.emit_mask(key, met, span);
}
let inside = C::cells(k, frame, cols_in, rows_in);
let mut rest = met;
while rest != 0 {
let p = rest.trailing_zeros();
if inside >> p & 1 == 1 {
let run = (inside >> p).trailing_ones();
let first = key.or(small::<W>(p).shl(span));
let last = key
.or(small::<W>(p + run - 1).shl(span))
.or(W::low_ones(span));
sink.emit(first, last);
rest &= !(u64::MAX >> (64 - run) << p);
} else {
let (cx, cy, next) = C::child(k, frame, p);
let child_key = key.or(small::<W>(p).shl(span));
let child = (
ox.or(small::<W>(cx.into()).shl(below)),
oy.or(small::<W>(cy.into()).shl(below)),
);
walk::<W, C, S>(r, below, stop, child_key, child, next, sink);
rest &= rest - 1;
}
}
}
fn depth<W: Word, C: Quadrants>(
levels: u32,
r: &Rect<W>,
top: u32,
limit: usize,
context: &C::Context<W>,
) -> u32 {
let limit = from_usize::<W>(limit);
let fits = |s: u32| C::runs(levels, r, s, context) <= limit;
let perimeter =
r.x1.wrapping_sub(r.x0)
.wrapping_add(r.y1.wrapping_sub(r.y0));
let mut s = bitlen(perimeter).saturating_sub(bitlen(limit)).min(top);
if fits(s) {
while s > 0 && fits(s - 1) {
s -= 1;
}
} else {
s += 1;
while s < top && !fits(s) {
s += 1;
}
}
s
}
pub(crate) fn cover<W: Word, C: Quadrants>(
levels: u32,
(x0, x1): (W, W),
(y0, y1): (W, W),
out: &mut [(W, W)],
) -> usize {
let side = W::low_ones(levels);
let (x1, y1) = (x1.min(side), y1.min(side));
if x0 > x1 || y0 > y1 {
return 0;
}
assert!(
!out.is_empty(),
"a nonempty rectangle needs room for a range"
);
let r = Rect { x0, x1, y0, y1 };
let top_level = bitlen(x0.xor(x1).or(y0.xor(y1)));
let (mut key, mut frame) = (W::ZERO, 0);
let mut level = levels;
while level > top_level {
level -= 1;
let bit = |v: W| u8::from(v.bit(level));
let (digit, below) = C::digit(frame, bit(x0), bit(y0));
key = key.or(small::<W>(digit.into()).shl(2 * level));
frame = below;
}
let context = C::context(&r);
let stop = depth::<W, C>(levels, &r, top_level, out.len().saturating_mul(2), &context);
if stop == top_level {
out[0] = (key, key.or(W::low_ones(2 * top_level)));
return 1;
}
let above = W::low_ones(top_level).not();
let origin = (x0.and(above), y0.and(above));
let mut gaps = Gaps {
out: &mut *out,
runs: 0,
last: None,
lengths: [0; 129],
least: u32::MAX,
};
walk::<W, C, _>(&r, top_level, stop, key, origin, frame, &mut gaps);
let (runs, lengths, least) = (gaps.runs, gaps.lengths, gaps.least);
let budget = out.len();
let (threshold, ties) = if runs > budget {
threshold(out, runs - 1, (&lengths, least), runs - budget)
} else {
(W::ZERO, 0)
};
let mut merge = Merge {
out,
n: 0,
threshold,
ties,
};
walk::<W, C, _>(&r, top_level, stop, key, origin, frame, &mut merge);
merge.n
}
#[inline]
const fn bits(lo: u32, hi: u32) -> u64 {
if lo > hi {
0
} else {
(u64::MAX >> (63 - hi)) & (u64::MAX << lo)
}
}
#[allow(clippy::many_single_char_names)]
fn meets<W: Word, C: Quadrants>(
r: &Rect<W>,
(a, b): (W, W),
level: u32,
key: W,
(ox, oy): (W, W),
frame: u8,
) -> bool {
let k = level.min(3);
let below = level - k;
let span = 2 * below;
let n = 1u32 << (2 * k);
let clamp = |v: W| {
if v >= small(n) {
n
} else {
u32::from(v.low_byte())
}
};
let (first_met, first_in) = if a > key {
let d = a.wrapping_sub(key);
(
clamp(d.shr(span)),
clamp(d.wrapping_sub(W::ONE).shr(span)) + 1,
)
} else {
(0, 0)
};
let d = b.wrapping_sub(key);
let last_met = clamp(d.shr(span)).min(n - 1);
let past = if d >= W::low_ones(2 * level) {
n
} else {
clamp(d.wrapping_add(W::ONE).shr(span)).min(n)
};
let keys_met = bits(first_met, last_met);
let keys_in = if past == 0 {
0
} else {
bits(first_in, past - 1)
};
let (cols, cols_in) = axis(ox, (r.x0, r.x1), below, k);
let (rows, rows_in) = axis(oy, (r.y0, r.y1), below, k);
let met = C::cells(k, frame, cols, rows);
let inside = C::cells(k, frame, cols_in, rows_in);
if met & keys_in != 0 || inside & keys_met != 0 {
return true;
}
let mut rest = met & keys_met;
while rest != 0 {
let p = rest.trailing_zeros();
let (cx, cy, next) = C::child(k, frame, p);
let child_key = key.or(small::<W>(p).shl(span));
let child = (
ox.or(small::<W>(cx.into()).shl(below)),
oy.or(small::<W>(cy.into()).shl(below)),
);
if meets::<W, C>(r, (a, b), below, child_key, child, next) {
return true;
}
rest &= rest - 1;
}
false
}
pub(crate) fn intersects<W: Word, C: Quadrants>(
levels: u32,
(a, b): (W, W),
(x0, x1): (W, W),
(y0, y1): (W, W),
) -> bool {
let side = W::low_ones(levels);
let (x1, y1) = (x1.min(side), y1.min(side));
if a > b || x0 > x1 || y0 > y1 {
return false;
}
let r = Rect { x0, x1, y0, y1 };
meets::<W, C>(&r, (a, b), levels, W::ZERO, (W::ZERO, W::ZERO), 0)
}
pub(crate) fn inclusive<W: Word>(r: &impl RangeBounds<W>) -> Option<(W, W)> {
let lo = match r.start_bound() {
Bound::Included(&a) => a,
Bound::Excluded(&a) if a == W::ONES => return None,
Bound::Excluded(&a) => a.wrapping_add(W::ONE),
Bound::Unbounded => W::ZERO,
};
let hi = match r.end_bound() {
Bound::Included(&b) => b,
Bound::Excluded(&b) if b == W::ZERO => return None,
Bound::Excluded(&b) => b.wrapping_sub(W::ONE),
Bound::Unbounded => W::ONES,
};
(lo <= hi).then_some((lo, hi))
}
pub(crate) fn cover_ranges<W: Word, C: Quadrants>(
levels: u32,
x: impl RangeBounds<W>,
y: impl RangeBounds<W>,
out: &mut [(W, W)],
) -> &[(W, W)] {
let n = match (inclusive(&x), inclusive(&y)) {
(Some(x), Some(y)) => cover::<W, C>(levels, x, y, out),
_ => 0,
};
&out[..n]
}
pub(crate) fn intersects_ranges<W: Word, C: Quadrants>(
levels: u32,
keys: impl RangeBounds<W>,
x: impl RangeBounds<W>,
y: impl RangeBounds<W>,
) -> bool {
match (inclusive(&keys), inclusive(&x), inclusive(&y)) {
(Some(keys), Some(x), Some(y)) => intersects::<W, C>(levels, keys, x, y),
_ => false,
}
}
#[cfg(test)]
mod tests {
use super::{Quadrants, Rect, Sink, walk};
use crate::dilated::ZQuadrants;
use crate::hilbert::HilbertQuadrants;
use crate::word::Word;
struct Count<W> {
n: usize,
last: Option<W>,
}
impl<W: Word> Sink<W> for Count<W> {
fn emit(&mut self, first: W, last: W) {
match self.last {
Some(end) if end != W::ONES && end.wrapping_add(W::ONE) == first => {}
_ => self.n += 1,
}
self.last = Some(last);
}
}
fn agree<W: Word, C: Quadrants>(r: &Rect<W>) -> bool {
let levels = W::BITS / 2;
let top = super::bitlen(r.x0.xor(r.x1).or(r.y0.xor(r.y1)));
let context = C::context(r);
(0..top).all(|s| {
let mut count = Count { n: 0, last: None };
walk::<W, C, _>(r, levels, s, W::ZERO, (W::ZERO, W::ZERO), 0, &mut count);
let runs = C::runs(levels, r, s, &context);
runs == super::from_usize(count.n)
})
}
#[allow(clippy::cast_possible_truncation)]
#[test]
fn threshold_matches_sorting() {
let mut state = 0x2545_F491_4F6C_DD1Du64;
let mut next = || {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
state
};
let pool = [
2u64,
3,
5,
7,
15,
16,
17,
18,
24,
31,
32,
35,
40,
64,
65,
1000,
1001,
1 << 40,
];
for _ in 0..20_000 {
let n = 1 + (next() % 70) as usize;
let mut v = [(0u64, 0u64); 35];
let (mut lengths, mut least) = ([0u32; 129], u32::MAX);
let mut all = [0u64; 70];
for (i, slot) in all.iter_mut().enumerate().take(n) {
let g = if next() % 4 == 0 {
2 + next() % 5000
} else {
pool[(next() % pool.len() as u64) as usize]
};
*slot = g;
super::set(&mut v, i, g);
lengths[super::bitlen(g) as usize] += 1;
least = least.min(super::bitlen(g));
}
let close = 1 + (next() % n as u64) as usize;
let (t, ties) = super::threshold(&mut v, n, (&lengths, least), close);
let sorted = &mut all[..n];
sorted.sort_unstable();
let want = sorted[close - 1];
let below = sorted.iter().filter(|&&g| g < want).count();
assert_eq!((t, ties), (want, close - below), "{sorted:?} close {close}");
}
}
#[test]
#[allow(clippy::reversed_empty_ranges)]
fn ranges_become_inclusive() {
use core::ops::Bound::{Excluded, Included, Unbounded};
use super::inclusive;
assert_eq!(inclusive(&(..)), Some((0u8, 255)));
assert_eq!(inclusive(&(3u8..)), Some((3, 255)));
assert_eq!(inclusive(&(..7u8)), Some((0, 6)));
assert_eq!(inclusive(&(2u8..=9)), Some((2, 9)));
assert_eq!(inclusive(&(5u8..5)), None);
assert_eq!(inclusive(&(..0u8)), None);
assert_eq!(inclusive(&(9u8..=2)), None);
assert_eq!(inclusive(&(Excluded(255u8), Unbounded)), None);
assert_eq!(inclusive(&(Excluded(3u8), Included(4))), Some((4, 4)));
}
#[test]
fn runs_every_u8_rectangle() {
for x0 in 0..16u8 {
for x1 in x0..16 {
for y0 in 0..16u8 {
for y1 in y0..16 {
let r = Rect { x0, x1, y0, y1 };
assert!(agree::<u8, ZQuadrants>(&r), "Z {x0}..={x1} × {y0}..={y1}");
assert!(
agree::<u8, HilbertQuadrants>(&r),
"H {x0}..={x1} × {y0}..={y1}"
);
}
}
}
}
}
#[test]
fn runs_random_u32_rectangles() {
let mut state = 0x9E37_79B9_7F4A_7C15u64;
let mut next = || {
state = state
.wrapping_mul(6_364_136_223_846_793_005)
.wrapping_add(1_442_695_040_888_963_407);
(state >> 32) as u32
};
for i in 0..4000 {
let side = |v: u32, n: u32| v & (u32::MAX >> (32 - n.max(1)));
let (x0, y0) = (next() >> 16, next() >> 16);
let n = i % 11;
let r = Rect {
x0,
x1: (x0 + side(next(), n)).min(0xFFFF),
y0,
y1: (y0 + side(next(), n)).min(0xFFFF),
};
assert!(
agree::<u32, ZQuadrants>(&r),
"Z {}..={} × {}..={}",
r.x0,
r.x1,
r.y0,
r.y1
);
assert!(
agree::<u32, HilbertQuadrants>(&r),
"H {}..={} × {}..={}",
r.x0,
r.x1,
r.y0,
r.y1
);
}
}
}