1use crate::column::Column;
12use crate::dtype::DType;
13
14pub trait Numeric: Copy + PartialOrd {
17 const DTYPE: DType;
19 const ZERO: Self;
21 const ONE: Self;
23
24 fn into_column(values: Vec<Self>) -> Column;
26
27 fn is_missing(self) -> bool;
29 fn to_f64(self) -> f64;
31 fn try_from_f64(x: f64) -> Option<Self>;
35
36 fn wrapping_add(self, other: Self) -> Self;
38 fn wrapping_sub(self, other: Self) -> Self;
40 fn wrapping_mul(self, other: Self) -> Self;
42 fn wrapping_abs(self) -> Self;
44}
45
46impl Numeric for f64 {
47 const DTYPE: DType = DType::F64;
48 const ZERO: Self = 0.0;
49 const ONE: Self = 1.0;
50
51 #[inline]
52 fn into_column(values: Vec<Self>) -> Column {
53 Column::f64(values)
54 }
55 #[inline]
56 fn is_missing(self) -> bool {
57 self.is_nan()
58 }
59 #[inline]
60 fn to_f64(self) -> f64 {
61 self
62 }
63 #[inline]
64 fn try_from_f64(x: f64) -> Option<Self> {
65 Some(x)
66 }
67 #[inline]
68 fn wrapping_add(self, other: Self) -> Self {
69 self + other
70 }
71 #[inline]
72 fn wrapping_sub(self, other: Self) -> Self {
73 self - other
74 }
75 #[inline]
76 fn wrapping_mul(self, other: Self) -> Self {
77 self * other
78 }
79 #[inline]
80 fn wrapping_abs(self) -> Self {
81 self.abs()
82 }
83}
84
85impl Numeric for f32 {
86 const DTYPE: DType = DType::F32;
87 const ZERO: Self = 0.0;
88 const ONE: Self = 1.0;
89
90 #[inline]
91 fn into_column(values: Vec<Self>) -> Column {
92 Column::f32(values)
93 }
94 #[inline]
95 fn is_missing(self) -> bool {
96 self.is_nan()
97 }
98 #[inline]
99 fn to_f64(self) -> f64 {
100 self as f64
101 }
102 #[inline]
103 fn try_from_f64(x: f64) -> Option<Self> {
104 Some(x as f32) }
106 #[inline]
107 fn wrapping_add(self, other: Self) -> Self {
108 self + other
109 }
110 #[inline]
111 fn wrapping_sub(self, other: Self) -> Self {
112 self - other
113 }
114 #[inline]
115 fn wrapping_mul(self, other: Self) -> Self {
116 self * other
117 }
118 #[inline]
119 fn wrapping_abs(self) -> Self {
120 self.abs()
121 }
122}
123
124impl Numeric for i32 {
125 const DTYPE: DType = DType::I32;
126 const ZERO: Self = 0;
127 const ONE: Self = 1;
128
129 #[inline]
130 fn into_column(values: Vec<Self>) -> Column {
131 Column::i32(values)
132 }
133 #[inline]
134 fn is_missing(self) -> bool {
135 false
136 }
137 #[inline]
138 fn to_f64(self) -> f64 {
139 self as f64
140 }
141 #[inline]
142 fn try_from_f64(x: f64) -> Option<Self> {
143 if x.is_finite() && x.fract() == 0.0 && x >= i32::MIN as f64 && x <= i32::MAX as f64 {
144 Some(x as i32)
145 } else {
146 None
147 }
148 }
149 #[inline]
150 fn wrapping_add(self, other: Self) -> Self {
151 i32::wrapping_add(self, other)
152 }
153 #[inline]
154 fn wrapping_sub(self, other: Self) -> Self {
155 i32::wrapping_sub(self, other)
156 }
157 #[inline]
158 fn wrapping_mul(self, other: Self) -> Self {
159 i32::wrapping_mul(self, other)
160 }
161 #[inline]
162 fn wrapping_abs(self) -> Self {
163 i32::wrapping_abs(self)
164 }
165}
166
167impl Numeric for i64 {
168 const DTYPE: DType = DType::I64;
169 const ZERO: Self = 0;
170 const ONE: Self = 1;
171
172 #[inline]
173 fn into_column(values: Vec<Self>) -> Column {
174 Column::i64(values)
175 }
176 #[inline]
177 fn is_missing(self) -> bool {
178 false
179 }
180 #[inline]
181 fn to_f64(self) -> f64 {
182 self as f64
183 }
184 #[inline]
185 fn try_from_f64(x: f64) -> Option<Self> {
186 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) {
189 Some(x as i64)
190 } else {
191 None
192 }
193 }
194 #[inline]
195 fn wrapping_add(self, other: Self) -> Self {
196 i64::wrapping_add(self, other)
197 }
198 #[inline]
199 fn wrapping_sub(self, other: Self) -> Self {
200 i64::wrapping_sub(self, other)
201 }
202 #[inline]
203 fn wrapping_mul(self, other: Self) -> Self {
204 i64::wrapping_mul(self, other)
205 }
206 #[inline]
207 fn wrapping_abs(self) -> Self {
208 i64::wrapping_abs(self)
209 }
210}
211
212pub fn binary_supertype(a: DType, b: DType) -> DType {
221 use DType::{Bool, F32, F64, I32, I64};
222 if a == b && matches!(a, F64 | F32 | I64 | I32) {
223 return a;
224 }
225 let int_like = |d| matches!(d, I64 | I32 | Bool);
226 if a.is_float() || b.is_float() {
227 if a.is_float() && b.is_float() {
228 if a == F64 || b == F64 { F64 } else { F32 }
230 } else {
231 F64 }
233 } else if int_like(a) && int_like(b) {
234 if a == I64 || b == I64 { I64 } else { I32 }
236 } else {
237 F64
238 }
239}
240
241pub fn fits(target: DType, x: f64) -> bool {
244 match target {
245 DType::F64 | DType::F32 => true, DType::I64 => i64::try_from_f64(x).is_some(),
247 DType::I32 => i32::try_from_f64(x).is_some(),
248 _ => false,
249 }
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255
256 #[test]
257 fn f64_element_semantics() {
258 assert_eq!(f64::DTYPE, DType::F64);
259 assert!(f64::NAN.is_missing() && !1.0_f64.is_missing());
260 assert_eq!(2.5_f64.to_f64(), 2.5);
261 assert_eq!(f64::try_from_f64(2.5), Some(2.5)); assert_eq!(3.0_f64.wrapping_add(4.0), 7.0);
263 assert_eq!((-2.0_f64).wrapping_abs(), 2.0);
264 }
265
266 #[test]
267 fn i64_element_semantics() {
268 assert_eq!(i64::DTYPE, DType::I64);
269 assert!(!5_i64.is_missing()); assert_eq!(5_i64.to_f64(), 5.0);
271 assert_eq!(3_i32.to_f64(), 3.0);
272 assert_eq!(i64::try_from_f64(5.0), Some(5));
274 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);
280 assert_eq!(i64::MIN.wrapping_abs(), i64::MIN);
281 assert_eq!(3_i64.wrapping_mul(4), 12);
282 assert_eq!(7_i64.wrapping_sub(10), -3);
283 }
284
285 #[test]
286 fn f32_i32_element_semantics() {
287 assert_eq!(f32::DTYPE, DType::F32);
289 assert!(f32::NAN.is_missing() && !1.5_f32.is_missing());
290 assert_eq!(2.5_f32.to_f64(), 2.5);
291 assert_eq!(f32::try_from_f64(2.5), Some(2.5_f32));
292 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);
294 assert_eq!((-2.0_f32).wrapping_abs(), 2.0);
295 assert!(matches!(f32::into_column(vec![1.0_f32]), Column::F32(_)));
296 assert_eq!(i32::DTYPE, DType::I32);
298 assert!(!5_i32.is_missing());
299 assert_eq!(i32::try_from_f64(5.0), Some(5));
300 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);
304 assert_eq!(7_i32.wrapping_sub(10), -3);
305 assert_eq!(3_i32.wrapping_mul(4), 12);
306 assert!(matches!(i32::into_column(vec![1_i32]), Column::I32(_, _)));
307 }
308
309 #[test]
310 fn promotion_rules() {
311 use DType::{Bool, F32, F64, I32, I64, Utf8};
312 assert_eq!(binary_supertype(I64, I64), I64);
314 assert_eq!(binary_supertype(F32, F32), F32);
315 assert_eq!(binary_supertype(I32, I32), I32);
316 assert_eq!(binary_supertype(F32, F64), F64);
318 assert_eq!(binary_supertype(I32, I64), I64);
319 assert_eq!(binary_supertype(I64, F64), F64);
321 assert_eq!(binary_supertype(I32, F32), F64);
322 assert_eq!(binary_supertype(Bool, I32), I32);
324 assert_eq!(binary_supertype(Bool, I64), I64);
325 assert_eq!(binary_supertype(I64, Utf8), F64); assert!(fits(I64, 3.0) && !fits(I64, 2.5));
328 assert!(fits(I32, 3.0) && !fits(I32, 1e20)); assert!(fits(F64, 2.5) && fits(F32, 2.5)); assert!(!fits(Bool, 1.0));
331 }
332}