#[inline(always)]
pub fn fast_recip(x: f32) -> f32 {
1.0 / x
}
#[inline(always)]
pub fn fast_recip_vec3(v: glam::Vec3) -> glam::Vec3 {
glam::Vec3::new(1.0 / v.x, 1.0 / v.y, 1.0 / v.z)
}
#[inline(always)]
pub fn fast_inv_sqrt(x: f32) -> f32 {
let half = 0.5 * x;
let i = 0x5f375a86u32.wrapping_sub(f32::to_bits(x) >> 1);
let y = f32::from_bits(i);
y * (half * y).mul_add(-y, 1.5)
}
#[inline(always)]
pub fn fast_normalize_2d(gx: f32, gz: f32) -> (f32, f32) {
let len_sq = gx.mul_add(gx, gz * gz);
if len_sq < 1e-12 {
return (0.0, 0.0);
}
let inv_len = fast_inv_sqrt(len_sq);
(gx * inv_len, gz * inv_len)
}
#[inline(always)]
pub fn select_f32(condition: bool, a: f32, b: f32) -> f32 {
let mask = -(condition as i32) as u32; f32::from_bits((f32::to_bits(a) & mask) | (f32::to_bits(b) & !mask))
}
#[inline(always)]
pub fn branchless_min(a: f32, b: f32) -> f32 {
select_f32(a < b, a, b)
}
#[inline(always)]
pub fn branchless_max(a: f32, b: f32) -> f32 {
select_f32(a > b, a, b)
}
#[inline(always)]
pub fn branchless_clamp(x: f32, lo: f32, hi: f32) -> f32 {
branchless_min(branchless_max(x, lo), hi)
}
#[inline(always)]
pub fn branchless_abs(x: f32) -> f32 {
f32::from_bits(f32::to_bits(x) & 0x7FFF_FFFF)
}
#[inline(always)]
pub fn round_half_up(x: f32) -> f32 {
(x + 0.5).floor()
}
#[inline(always)]
pub fn round_half_up_vec3(v: glam::Vec3) -> glam::Vec3 {
glam::Vec3::new(round_half_up(v.x), round_half_up(v.y), round_half_up(v.z))
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct BitMask64(u64);
impl BitMask64 {
pub const EMPTY: Self = Self(0);
pub const FULL: Self = Self(!0u64);
#[inline(always)]
#[must_use]
pub const fn from_raw(bits: u64) -> Self {
Self(bits)
}
#[inline(always)]
#[must_use]
pub const fn as_u64(self) -> u64 {
self.0
}
#[inline(always)]
pub const fn and(self, other: Self) -> Self {
Self(self.0 & other.0)
}
#[inline(always)]
pub const fn or(self, other: Self) -> Self {
Self(self.0 | other.0)
}
#[inline(always)]
pub const fn count_ones(self) -> u32 {
self.0.count_ones()
}
#[inline(always)]
pub const fn test(self, index: u32) -> bool {
(self.0 >> index) & 1 != 0
}
#[inline(always)]
pub const fn set(self, index: u32) -> Self {
Self(self.0 | (1u64 << index))
}
#[inline(always)]
pub const fn clear(self, index: u32) -> Self {
Self(self.0 & !(1u64 << index))
}
#[inline(always)]
pub const fn is_empty(self) -> bool {
self.0 == 0
}
}
impl std::ops::Not for BitMask64 {
type Output = Self;
#[inline(always)]
fn not(self) -> Self::Output {
Self(!self.0)
}
}
impl std::fmt::Display for BitMask64 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:#066b}", self.0)
}
}
impl From<u64> for BitMask64 {
fn from(bits: u64) -> Self {
Self(bits)
}
}
pub struct BloomFilter {
bits: [u8; Self::SIZE_BYTES],
}
impl BloomFilter {
const SIZE_BITS: usize = 32768;
const SIZE_BYTES: usize = Self::SIZE_BITS / 8;
pub const fn new() -> Self {
Self {
bits: [0u8; Self::SIZE_BYTES],
}
}
pub fn from_items<'a>(items: impl IntoIterator<Item = &'a [u8]>) -> Self {
let mut f = Self::new();
for item in items {
f.insert(item);
}
f
}
#[inline]
pub fn insert(&mut self, data: &[u8]) {
let hash = fnv1a_hash(data);
let (h1, h2) = Self::double_hash(hash);
self.bits[h1 >> 3] |= 1 << (h1 & 7);
self.bits[h2 >> 3] |= 1 << (h2 & 7);
}
#[inline(always)]
pub fn test(&self, data: &[u8]) -> bool {
let hash = fnv1a_hash(data);
Self::test_hash(&self.bits, hash)
}
#[inline(always)]
pub const fn test_hash(filter: &[u8; 4096], hash: u64) -> bool {
let (h1, h2) = Self::double_hash(hash);
(filter[h1 >> 3] & (1 << (h1 & 7)) != 0) & (filter[h2 >> 3] & (1 << (h2 & 7)) != 0)
}
#[inline(always)]
const fn double_hash(hash: u64) -> (usize, usize) {
let h1 = (hash & 0x7FFF) as usize;
let h2 = ((hash >> 16) & 0x7FFF) as usize;
(h1, h2)
}
}
impl Default for BloomFilter {
fn default() -> Self {
Self::new()
}
}
#[inline]
pub fn fnv1a_hash(data: &[u8]) -> u64 {
const OFFSET: u64 = 0xcbf29ce484222325;
const PRIME: u64 = 0x00000100000001B3;
let mut hash = OFFSET;
for &byte in data {
hash ^= byte as u64;
hash = hash.wrapping_mul(PRIME);
}
hash
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_half_up_ties_toward_positive_infinity() {
for (x, want) in [
(0.5f32, 1.0f32),
(-0.5, 0.0),
(1.5, 2.0),
(-1.5, -1.0),
(2.5, 3.0),
(-2.5, -2.0),
] {
assert_eq!(round_half_up(x), want, "round_half_up({x})");
}
for x in [0.0f32, 0.25, 0.75, -0.25, -0.75, 3.1, -3.9, 1e5 + 0.3] {
assert_eq!(round_half_up(x), x.round(), "round_half_up({x})");
}
let v = round_half_up_vec3(glam::Vec3::new(0.5, -0.5, 2.5));
assert_eq!(v, glam::Vec3::new(1.0, 0.0, 3.0));
}
#[test]
fn test_fast_inv_sqrt_accuracy() {
let test_values = [0.25f32, 1.0, 4.0, 16.0, 100.0, 0.01];
for x in test_values {
let expected = 1.0 / x.sqrt();
let got = fast_inv_sqrt(x);
let error = ((got - expected) / expected).abs();
assert!(
error < 0.002,
"fast_inv_sqrt({}) = {}, expected {}, error = {:.4}%",
x,
got,
expected,
error * 100.0,
);
}
}
#[test]
fn test_fast_inv_sqrt_large() {
let x = 10000.0f32;
let expected = 1.0 / x.sqrt();
let got = fast_inv_sqrt(x);
assert!((got - expected).abs() / expected < 0.002);
}
#[test]
fn test_fast_normalize_2d() {
let (nx, nz) = fast_normalize_2d(3.0, 4.0);
let len = nx.hypot(nz);
assert!(
(len - 1.0).abs() < 0.01,
"Should be unit length, got {}",
len
);
assert!((nx - 0.6).abs() < 0.01);
assert!((nz - 0.8).abs() < 0.01);
}
#[test]
fn test_fast_normalize_2d_zero() {
let (nx, nz) = fast_normalize_2d(0.0, 0.0);
assert_eq!(nx, 0.0);
assert_eq!(nz, 0.0);
}
#[test]
fn test_fast_normalize_2d_small() {
let (nx, nz) = fast_normalize_2d(1e-7, 0.0);
assert_eq!(nx, 0.0);
assert_eq!(nz, 0.0);
}
#[test]
fn test_select_f32() {
assert_eq!(select_f32(true, 1.0, 2.0), 1.0);
assert_eq!(select_f32(false, 1.0, 2.0), 2.0);
assert_eq!(select_f32(true, -3.5, 7.0), -3.5);
assert_eq!(select_f32(false, -3.5, 7.0), 7.0);
}
#[test]
fn test_branchless_min_max() {
assert_eq!(branchless_min(3.0, 5.0), 3.0);
assert_eq!(branchless_min(-1.0, 1.0), -1.0);
assert_eq!(branchless_max(3.0, 5.0), 5.0);
assert_eq!(branchless_max(-1.0, 1.0), 1.0);
}
#[test]
fn test_branchless_clamp() {
assert_eq!(branchless_clamp(0.5, 0.0, 1.0), 0.5);
assert_eq!(branchless_clamp(-1.0, 0.0, 1.0), 0.0);
assert_eq!(branchless_clamp(2.0, 0.0, 1.0), 1.0);
}
#[test]
fn test_branchless_abs() {
assert_eq!(branchless_abs(3.0), 3.0);
assert_eq!(branchless_abs(-3.0), 3.0);
assert_eq!(branchless_abs(0.0), 0.0);
}
#[test]
fn test_bitmask64_ops() {
let a = BitMask64::from_raw(0b1010);
let b = BitMask64::from_raw(0b1100);
assert_eq!(a.and(b), BitMask64::from_raw(0b1000));
assert_eq!(a.or(b), BitMask64::from_raw(0b1110));
assert_eq!(a.count_ones(), 2);
assert!(a.test(1));
assert!(!a.test(0));
assert_eq!(BitMask64::EMPTY.set(3), BitMask64::from_raw(0b1000));
assert_eq!(a.clear(1), BitMask64::from_raw(0b1000));
assert!(!a.is_empty());
assert!(BitMask64::EMPTY.is_empty());
}
#[test]
fn test_fnv1a_hash_deterministic() {
let h1 = fnv1a_hash(b"Sphere");
let h2 = fnv1a_hash(b"Sphere");
assert_eq!(h1, h2);
let h3 = fnv1a_hash(b"Box");
assert_ne!(h1, h3);
}
#[test]
fn test_bloom_filter_basic() {
let mut bloom = BloomFilter::new();
bloom.insert(b"Sphere");
bloom.insert(b"Box");
bloom.insert(b"Cylinder");
assert!(bloom.test(b"Sphere"));
assert!(bloom.test(b"Box"));
assert!(bloom.test(b"Cylinder"));
assert!(!bloom.test(b"Octahedron"));
assert!(!bloom.test(b"Icosahedron"));
}
#[test]
fn test_bloom_filter_from_items() {
let items: Vec<&[u8]> = vec![b"Union", b"Intersection", b"Subtraction"];
let bloom = BloomFilter::from_items(items);
assert!(bloom.test(b"Union"));
assert!(bloom.test(b"Intersection"));
assert!(bloom.test(b"Subtraction"));
assert!(!bloom.test(b"SmoothUnion"));
}
#[test]
fn test_bloom_filter_many_entries() {
let primitives = [
"Sphere",
"Box",
"Cylinder",
"Torus",
"Capsule",
"Plane",
"Cone",
"Ellipsoid",
"HexPrism",
"Octahedron",
"Link",
"RoundedBox",
"CappedCone",
"CappedTorus",
"RoundedCylinder",
"TriangularPrism",
"CutSphere",
"CutHollowSphere",
"DeathStar",
"SolidAngle",
"Rhombus",
"Horseshoe",
"Vesica",
"InfiniteCylinder",
"InfiniteCone",
"Gyroid",
"Heart",
"Tube",
"Barrel",
"Diamond",
"ChamferedCube",
"SchwarzP",
"Superellipsoid",
"RoundedX",
"Pie",
"Trapezoid",
"Parallelogram",
"Tunnel",
"UnevenCapsule",
"Egg",
"ArcShape",
"Moon",
"CrossShape",
"BlobbyCross",
"ParabolaSegment",
"RegularPolygon",
"StarPolygon",
"Stairs",
"Helix",
"Bezier",
"Pyramid",
"Spring",
"Chain",
];
let items: Vec<&[u8]> = primitives.iter().map(|s| s.as_bytes()).collect();
let bloom = BloomFilter::from_items(items);
for name in &primitives {
assert!(bloom.test(name.as_bytes()), "False negative for '{}'", name);
}
let false_positives: usize = [
"FooBar",
"Teapot",
"Dodecahedron",
"Mobius",
"Klein",
"Trefoil",
"Catmull",
"Nurbs",
"Spline",
"Metaball",
]
.iter()
.filter(|s| bloom.test(s.as_bytes()))
.count();
assert!(
false_positives <= 2,
"Too many false positives: {}/10",
false_positives
);
}
}