use crate::column::Column;
use crate::dtype::DType;
pub trait Numeric: Copy + PartialOrd {
const DTYPE: DType;
const ZERO: Self;
const ONE: Self;
fn into_column(values: Vec<Self>) -> Column;
fn is_missing(self) -> bool;
fn to_f64(self) -> f64;
fn try_from_f64(x: f64) -> Option<Self>;
fn wrapping_add(self, other: Self) -> Self;
fn wrapping_sub(self, other: Self) -> Self;
fn wrapping_mul(self, other: Self) -> Self;
fn wrapping_abs(self) -> Self;
}
impl Numeric for f64 {
const DTYPE: DType = DType::F64;
const ZERO: Self = 0.0;
const ONE: Self = 1.0;
#[inline]
fn into_column(values: Vec<Self>) -> Column {
Column::f64(values)
}
#[inline]
fn is_missing(self) -> bool {
self.is_nan()
}
#[inline]
fn to_f64(self) -> f64 {
self
}
#[inline]
fn try_from_f64(x: f64) -> Option<Self> {
Some(x)
}
#[inline]
fn wrapping_add(self, other: Self) -> Self {
self + other
}
#[inline]
fn wrapping_sub(self, other: Self) -> Self {
self - other
}
#[inline]
fn wrapping_mul(self, other: Self) -> Self {
self * other
}
#[inline]
fn wrapping_abs(self) -> Self {
self.abs()
}
}
impl Numeric for f32 {
const DTYPE: DType = DType::F32;
const ZERO: Self = 0.0;
const ONE: Self = 1.0;
#[inline]
fn into_column(values: Vec<Self>) -> Column {
Column::f32(values)
}
#[inline]
fn is_missing(self) -> bool {
self.is_nan()
}
#[inline]
fn to_f64(self) -> f64 {
self as f64
}
#[inline]
fn try_from_f64(x: f64) -> Option<Self> {
Some(x as f32) }
#[inline]
fn wrapping_add(self, other: Self) -> Self {
self + other
}
#[inline]
fn wrapping_sub(self, other: Self) -> Self {
self - other
}
#[inline]
fn wrapping_mul(self, other: Self) -> Self {
self * other
}
#[inline]
fn wrapping_abs(self) -> Self {
self.abs()
}
}
impl Numeric for i32 {
const DTYPE: DType = DType::I32;
const ZERO: Self = 0;
const ONE: Self = 1;
#[inline]
fn into_column(values: Vec<Self>) -> Column {
Column::i32(values)
}
#[inline]
fn is_missing(self) -> bool {
false
}
#[inline]
fn to_f64(self) -> f64 {
self as f64
}
#[inline]
fn try_from_f64(x: f64) -> Option<Self> {
if x.is_finite() && x.fract() == 0.0 && x >= i32::MIN as f64 && x <= i32::MAX as f64 {
Some(x as i32)
} else {
None
}
}
#[inline]
fn wrapping_add(self, other: Self) -> Self {
i32::wrapping_add(self, other)
}
#[inline]
fn wrapping_sub(self, other: Self) -> Self {
i32::wrapping_sub(self, other)
}
#[inline]
fn wrapping_mul(self, other: Self) -> Self {
i32::wrapping_mul(self, other)
}
#[inline]
fn wrapping_abs(self) -> Self {
i32::wrapping_abs(self)
}
}
impl Numeric for i64 {
const DTYPE: DType = DType::I64;
const ZERO: Self = 0;
const ONE: Self = 1;
#[inline]
fn into_column(values: Vec<Self>) -> Column {
Column::i64(values)
}
#[inline]
fn is_missing(self) -> bool {
false
}
#[inline]
fn to_f64(self) -> f64 {
self as f64
}
#[inline]
fn try_from_f64(x: f64) -> Option<Self> {
if x.is_finite() && x.fract() == 0.0 && (-9_223_372_036_854_775_808.0..9_223_372_036_854_775_808.0).contains(&x) {
Some(x as i64)
} else {
None
}
}
#[inline]
fn wrapping_add(self, other: Self) -> Self {
i64::wrapping_add(self, other)
}
#[inline]
fn wrapping_sub(self, other: Self) -> Self {
i64::wrapping_sub(self, other)
}
#[inline]
fn wrapping_mul(self, other: Self) -> Self {
i64::wrapping_mul(self, other)
}
#[inline]
fn wrapping_abs(self) -> Self {
i64::wrapping_abs(self)
}
}
pub fn binary_supertype(a: DType, b: DType) -> DType {
use DType::{Bool, F32, F64, I32, I64};
if a == b && matches!(a, F64 | F32 | I64 | I32) {
return a;
}
let int_like = |d| matches!(d, I64 | I32 | Bool);
if a.is_float() || b.is_float() {
if a.is_float() && b.is_float() {
if a == F64 || b == F64 { F64 } else { F32 }
} else {
F64 }
} else if int_like(a) && int_like(b) {
if a == I64 || b == I64 { I64 } else { I32 }
} else {
F64
}
}
pub fn fits(target: DType, x: f64) -> bool {
match target {
DType::F64 | DType::F32 => true, DType::I64 => i64::try_from_f64(x).is_some(),
DType::I32 => i32::try_from_f64(x).is_some(),
_ => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn f64_element_semantics() {
assert_eq!(f64::DTYPE, DType::F64);
assert!(f64::NAN.is_missing() && !1.0_f64.is_missing());
assert_eq!(2.5_f64.to_f64(), 2.5);
assert_eq!(f64::try_from_f64(2.5), Some(2.5)); assert_eq!(3.0_f64.wrapping_add(4.0), 7.0);
assert_eq!((-2.0_f64).wrapping_abs(), 2.0);
}
#[test]
fn i64_element_semantics() {
assert_eq!(i64::DTYPE, DType::I64);
assert!(!5_i64.is_missing()); assert_eq!(5_i64.to_f64(), 5.0);
assert_eq!(3_i32.to_f64(), 3.0);
assert_eq!(i64::try_from_f64(5.0), Some(5));
assert_eq!(i64::try_from_f64(2.5), None); assert_eq!(i64::try_from_f64(f64::NAN), None); assert_eq!(i64::try_from_f64(1e30), None); assert_eq!(i64::try_from_f64(9.223372036854776e18), None); assert_eq!(i64::MAX.wrapping_add(1), i64::MIN);
assert_eq!(i64::MIN.wrapping_abs(), i64::MIN);
assert_eq!(3_i64.wrapping_mul(4), 12);
assert_eq!(7_i64.wrapping_sub(10), -3);
}
#[test]
fn f32_i32_element_semantics() {
assert_eq!(f32::DTYPE, DType::F32);
assert!(f32::NAN.is_missing() && !1.5_f32.is_missing());
assert_eq!(2.5_f32.to_f64(), 2.5);
assert_eq!(f32::try_from_f64(2.5), Some(2.5_f32));
assert_eq!(f32::try_from_f64(0.1).map(|x| x.is_finite()), Some(true)); assert_eq!(3.0_f32.wrapping_add(4.0), 7.0);
assert_eq!((-2.0_f32).wrapping_abs(), 2.0);
assert!(matches!(f32::into_column(vec![1.0_f32]), Column::F32(_)));
assert_eq!(i32::DTYPE, DType::I32);
assert!(!5_i32.is_missing());
assert_eq!(i32::try_from_f64(5.0), Some(5));
assert_eq!(i32::try_from_f64(2.5), None); assert_eq!(i32::try_from_f64(3e9), None); assert_eq!(i32::MAX.wrapping_add(1), i32::MIN); assert_eq!(i32::MIN.wrapping_abs(), i32::MIN);
assert_eq!(7_i32.wrapping_sub(10), -3);
assert_eq!(3_i32.wrapping_mul(4), 12);
assert!(matches!(i32::into_column(vec![1_i32]), Column::I32(_, _)));
}
#[test]
fn promotion_rules() {
use DType::{Bool, F32, F64, I32, I64, Utf8};
assert_eq!(binary_supertype(I64, I64), I64);
assert_eq!(binary_supertype(F32, F32), F32);
assert_eq!(binary_supertype(I32, I32), I32);
assert_eq!(binary_supertype(F32, F64), F64);
assert_eq!(binary_supertype(I32, I64), I64);
assert_eq!(binary_supertype(I64, F64), F64);
assert_eq!(binary_supertype(I32, F32), F64);
assert_eq!(binary_supertype(Bool, I32), I32);
assert_eq!(binary_supertype(Bool, I64), I64);
assert_eq!(binary_supertype(I64, Utf8), F64); assert!(fits(I64, 3.0) && !fits(I64, 2.5));
assert!(fits(I32, 3.0) && !fits(I32, 1e20)); assert!(fits(F64, 2.5) && fits(F32, 2.5)); assert!(!fits(Bool, 1.0));
}
}