use crate::data::Point;
#[derive(Debug)]
pub struct ZHashBox<'a, T> {
pub min_x: &'a T,
pub max_x: &'a T,
pub min_y: &'a T,
pub max_y: &'a T,
}
impl<T> Copy for ZHashBox<'_, T> {}
impl<'a, T> Clone for ZHashBox<'a, T> {
fn clone(&self) -> ZHashBox<'a, T> {
*self
}
}
pub trait ZHashable: Sized {
type ZHashKey: Copy;
fn zhash_key(zbox: ZHashBox<'_, Self>) -> Self::ZHashKey;
fn zhash_fn(key: Self::ZHashKey, point: &Point<Self>) -> u64;
}
impl ZHashable for f64 {
type ZHashKey = (f64, f64, f64, f64);
fn zhash_key(zbox: ZHashBox<'_, f64>) -> Self::ZHashKey {
let width = zbox.max_x - zbox.min_x;
let height = zbox.max_y - zbox.min_y;
(*zbox.min_x, *zbox.min_y, width, height)
}
fn zhash_fn(key: Self::ZHashKey, point: &Point<Self>) -> u64 {
let (min_x, min_y, width, height) = key;
let z_hash_max = f64::from(u32::MAX);
let x = ((point.x_coord() - min_x) / width * z_hash_max) as u32;
let y = ((point.y_coord() - min_y) / height * z_hash_max) as u32;
zhash_pair(x, y)
}
}
impl ZHashable for i64 {
type ZHashKey = (i64, i64, u32, u32);
fn zhash_key(zbox: ZHashBox<'_, i64>) -> Self::ZHashKey {
let width = zbox.max_x.wrapping_sub(*zbox.min_x) as u64;
let height = zbox.max_y.wrapping_sub(*zbox.min_y) as u64;
let x_r_shift = 32u32.saturating_sub(width.leading_zeros());
let y_r_shift = 32u32.saturating_sub(height.leading_zeros());
(*zbox.min_x, *zbox.min_y, x_r_shift, y_r_shift)
}
fn zhash_fn(key: Self::ZHashKey, point: &Point<Self>) -> u64 {
let (min_x, min_y, x_r_shift, y_r_shift) = key;
let x = ((point.x_coord().wrapping_sub(min_x) as u64) >> x_r_shift) as u32;
let y = ((point.y_coord().wrapping_sub(min_y) as u64) >> y_r_shift) as u32;
zhash_pair(x, y)
}
}
impl ZHashable for i8 {
type ZHashKey = (i8, i8);
fn zhash_key(zbox: ZHashBox<'_, i8>) -> Self::ZHashKey {
(*zbox.min_x, *zbox.min_y)
}
fn zhash_fn(key: Self::ZHashKey, point: &Point<Self>) -> u64 {
let (min_x, min_y) = key;
let x = point.x_coord().abs_diff(min_x) as u32;
let y = point.y_coord().abs_diff(min_y) as u32;
zhash_pair(x, y)
}
}
impl ZHashable for u64 {
type ZHashKey = (u64, u64, u32, u32);
fn zhash_key(zbox: ZHashBox<'_, u64>) -> Self::ZHashKey {
let width = zbox.max_x - zbox.min_x;
let height = zbox.max_y - zbox.min_y;
let x_r_shift = 32u32.saturating_sub(width.leading_zeros());
let y_r_shift = 32u32.saturating_sub(height.leading_zeros());
(*zbox.min_x, *zbox.min_y, x_r_shift, y_r_shift)
}
fn zhash_fn(key: Self::ZHashKey, point: &Point<Self>) -> u64 {
let (min_x, min_y, x_r_shift, y_r_shift) = key;
let x = ((*point.x_coord() - min_x) >> x_r_shift) as u32;
let y = ((*point.y_coord() - min_y) >> y_r_shift) as u32;
zhash_pair(x, y)
}
}
impl ZHashable for u32 {
type ZHashKey = ();
fn zhash_key(_zbox: ZHashBox<'_, u32>) -> Self::ZHashKey {}
fn zhash_fn(_key: Self::ZHashKey, point: &Point<Self>) -> u64 {
zhash_pair(*point.x_coord(), *point.y_coord())
}
}
pub fn zunhash_pair(w: u64) -> (u32, u32) {
(zunhash_u32(w), zunhash_u32(w >> 1))
}
fn zunhash_u32(w: u64) -> u32 {
let w = w & 0x5555555555555555;
let w = (w | w >> 1) & 0x3333333333333333;
let w = (w | w >> 2) & 0x0F0F0F0F0F0F0F0F;
let w = (w | w >> 4) & 0x00FF00FF00FF00FF;
let w = (w | w >> 8) & 0x0000FFFF0000FFFF;
let w = (w | w >> 16) & 0x00000000FFFFFFFF;
w as u32
}
pub fn zhash_pair(a: u32, b: u32) -> u64 {
zhash_u32(a) | zhash_u32(b) << 1
}
fn zhash_u32(w: u32) -> u64 {
let w = w as u64; let w = (w | w << 16) & 0x0000FFFF0000FFFF;
let w = (w | w << 8) & 0x00FF00FF00FF00FF;
let w = (w | w << 4) & 0x0F0F0F0F0F0F0F0F;
let w = (w | w << 2) & 0x3333333333333333;
(w | w << 1) & 0x5555555555555555
}
#[cfg(test)]
#[cfg(not(tarpaulin_include))]
mod tests {
use super::*;
use crate::data::Triangle;
use proptest::prelude::*;
use test_strategy::proptest;
#[proptest]
fn hash_unhash_prop(a: u32, b: u32) {
prop_assert_eq!(zunhash_pair(zhash_pair(a, b)), (a, b))
}
#[proptest]
fn cmp_prop_i8(trig: Triangle<i8>) {
let (min, max) = trig.view().bounding_box();
let middle = Point::new([
((i16::from(*min.x_coord()) + i16::from(*max.x_coord())) / 2) as i8,
((i16::from(*min.y_coord()) + i16::from(*max.y_coord())) / 2) as i8,
]);
let zbox = ZHashBox {
min_x: min.x_coord(),
max_x: max.x_coord(),
min_y: min.y_coord(),
max_y: max.y_coord(),
};
let key = ZHashable::zhash_key(zbox);
let min_hash = ZHashable::zhash_fn(key, &min);
let max_hash = ZHashable::zhash_fn(key, &max);
let mid_hash = ZHashable::zhash_fn(key, &middle);
prop_assert!(min_hash <= mid_hash);
prop_assert!(mid_hash <= max_hash);
}
#[proptest]
fn cmp_prop_i64(trig: Triangle<i64>) {
let (min, max) = trig.view().bounding_box();
let middle = Point::new([
((i128::from(*min.x_coord()) + i128::from(*max.x_coord())) / 2) as i64,
((i128::from(*min.y_coord()) + i128::from(*max.y_coord())) / 2) as i64,
]);
let zbox = ZHashBox {
min_x: min.x_coord(),
max_x: max.x_coord(),
min_y: min.y_coord(),
max_y: max.y_coord(),
};
let key = ZHashable::zhash_key(zbox);
let min_hash = ZHashable::zhash_fn(key, &min);
let max_hash = ZHashable::zhash_fn(key, &max);
let mid_hash = ZHashable::zhash_fn(key, &middle);
prop_assert!(min_hash <= mid_hash);
prop_assert!(mid_hash <= max_hash);
}
}