use std::fmt::Debug;
use crate::common::types::PointOffsetType;
use num_traits::Num;
use ordered_float::OrderedFloat;
use serde::Serialize;
pub use self::point::Point;
use crate::segment::types::{FloatPayloadType, Range};
#[expect(clippy::multiple_bound_locations)]
mod point {
use crate::common::types::PointOffsetType;
use super::Numericable;
#[expect(clippy::derive_ord_xor_partial_ord)]
#[derive(
PartialEq,
PartialOrd,
Debug,
Clone,
Copy,
serde::Serialize,
serde::Deserialize,
bytemuck::Pod,
bytemuck::Zeroable,
)]
#[repr(C, packed)]
pub struct Point<T: Numericable> {
pub val: T,
pub idx: PointOffsetType,
#[serde(skip)]
_padding: T::PointPadding,
}
impl<T: Numericable> Point<T> {
pub fn new(val: T, idx: PointOffsetType) -> Self {
Self {
val,
idx,
_padding: bytemuck::Zeroable::zeroed(),
}
}
}
impl<T: PartialEq + Numericable> Eq for Point<T> {}
impl<T: PartialOrd + Copy + Numericable> Ord for Point<T> {
fn cmp(&self, other: &Point<T>) -> std::cmp::Ordering {
(self.val, self.idx)
.partial_cmp(&(other.val, other.idx))
.unwrap()
}
}
}
const fn derive_point_padding<T: bytemuck::Pod>() -> usize {
struct Point<T> {
_t: T,
_idx: PointOffsetType,
}
let align = std::mem::align_of::<Point<T>>();
assert!(std::mem::align_of::<T>() >= std::mem::align_of::<PointOffsetType>());
align - (std::mem::size_of::<T>() + std::mem::size_of::<PointOffsetType>()) % align
}
pub trait Numericable: Num + PartialEq + PartialOrd + Copy + bytemuck::Pod + Send {
type PointPadding: bytemuck::Pod
+ Debug
+ Default
+ PartialEq
+ PartialOrd
+ Serialize
+ for<'de> serde::Deserialize<'de>
+ Send;
fn min_value() -> Self;
fn max_value() -> Self;
fn to_f64(self) -> f64;
fn from_f64(x: f64) -> Self;
fn from_u128(x: u128) -> Self;
fn min(self, b: Self) -> Self {
if self < b { self } else { b }
}
fn max(self, b: Self) -> Self {
if self > b { self } else { b }
}
fn abs_diff(self, b: Self) -> Self {
if self > b { self - b } else { b - self }
}
fn from_f64_range(range: Range<OrderedFloat<FloatPayloadType>>) -> Range<Self> {
range.map(|x| Self::from_f64(x.0))
}
}
impl Numericable for i64 {
type PointPadding = [u8; derive_point_padding::<Self>()];
fn min_value() -> Self {
i64::MIN
}
fn max_value() -> Self {
i64::MAX
}
fn to_f64(self) -> f64 {
self as f64
}
fn from_f64(x: f64) -> Self {
x as Self
}
fn from_u128(x: u128) -> Self {
x as i64
}
fn abs_diff(self, b: Self) -> Self {
i64::abs_diff(self, b) as i64
}
fn from_f64_range(range: Range<OrderedFloat<FloatPayloadType>>) -> Range<Self> {
Range {
lt: range.lt.map(|f| f.0.ceil() as Self),
gt: range.gt.map(|f| f.0.floor() as Self),
gte: range.gte.map(|f| f.0.ceil() as Self),
lte: range.lte.map(|f| f.0.floor() as Self),
}
}
}
impl Numericable for f64 {
type PointPadding = [u8; derive_point_padding::<Self>()];
fn min_value() -> Self {
f64::MIN
}
fn max_value() -> Self {
f64::MAX
}
fn to_f64(self) -> f64 {
self
}
fn from_f64(x: f64) -> Self {
x
}
fn from_u128(x: u128) -> Self {
x as Self
}
}
impl Numericable for u128 {
type PointPadding = [u8; derive_point_padding::<Self>()];
fn min_value() -> Self {
u128::MIN
}
fn max_value() -> Self {
u128::MAX
}
fn to_f64(self) -> f64 {
self as f64
}
fn from_f64(x: f64) -> Self {
x as u128
}
fn from_u128(x: u128) -> Self {
x
}
fn abs_diff(self, b: Self) -> Self {
u128::abs_diff(self, b)
}
fn from_f64_range(range: Range<OrderedFloat<FloatPayloadType>>) -> Range<Self> {
Range {
lt: range.lt.map(|f| f.0.ceil() as Self),
gt: range.gt.map(|f| f.0.floor() as Self),
gte: range.gte.map(|f| f.0.ceil() as Self),
lte: range.lte.map(|f| f.0.floor() as Self),
}
}
}