pax_runtime_api/pax_value/
numeric.rs1use std::{cmp::Ordering, fmt::Display};
2
3use crate::{Interpolatable, Size};
4use serde::{Deserialize, Serialize};
5use std::hash::Hash;
6
7#[derive(Clone, Serialize, Deserialize)]
11#[serde(crate = "crate::serde")]
12#[derive(Debug, Copy)]
13pub enum Numeric {
14 I8(i8),
15 I16(i16),
16 I32(i32),
17 I64(i64),
18 U8(u8),
19 U16(u16),
20 U32(u32),
21 U64(u64),
22 F64(f64),
23 F32(f32),
24 ISize(isize),
25 USize(usize),
26}
27
28impl Hash for Numeric {
29 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
30 match self {
31 Numeric::I8(v) => v.hash(state),
32 Numeric::I16(v) => v.hash(state),
33 Numeric::I32(v) => v.hash(state),
34 Numeric::I64(v) => v.hash(state),
35 Numeric::U8(v) => v.hash(state),
36 Numeric::U16(v) => v.hash(state),
37 Numeric::U32(v) => v.hash(state),
38 Numeric::U64(v) => v.hash(state),
39 Numeric::F64(v) => v.to_bits().hash(state),
40 Numeric::F32(v) => v.to_bits().hash(state),
41 Numeric::ISize(v) => v.hash(state),
42 Numeric::USize(v) => v.hash(state),
43 }
44 }
45}
46
47impl Display for Numeric {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 fn fmt_num<V: Display>(
50 f: &mut std::fmt::Formatter<'_>,
51 v: V,
52 ) -> Result<(), std::fmt::Error> {
53 write!(f, "{:.2}", v)
54 }
55 match self {
56 Numeric::I8(v) => fmt_num(f, v),
57 Numeric::I16(v) => fmt_num(f, v),
58 Numeric::I32(v) => fmt_num(f, v),
59 Numeric::I64(v) => fmt_num(f, v),
60 Numeric::U8(v) => fmt_num(f, v),
61 Numeric::U16(v) => fmt_num(f, v),
62 Numeric::U32(v) => fmt_num(f, v),
63 Numeric::U64(v) => fmt_num(f, v),
64 Numeric::F64(v) => fmt_num(f, v),
65 Numeric::F32(v) => fmt_num(f, v),
66 Numeric::ISize(v) => fmt_num(f, v),
67 Numeric::USize(v) => fmt_num(f, v),
68 }
69 }
70}
71
72impl PartialEq for Numeric {
73 fn eq(&self, rhs: &Self) -> bool {
74 match (self.is_float(), rhs.is_float()) {
75 (false, false) => self.to_int() == rhs.to_int(),
76 _ => (self.to_float() - rhs.to_float()).abs() < 1e-6,
77 }
78 }
79}
80
81impl Default for Numeric {
82 fn default() -> Self {
83 Self::F64(0.0)
84 }
85}
86
87macro_rules! impl_numeric_arith {
88 ($trait:ident, $method:ident, $op:tt) => {
89 impl std::ops::$trait for &Numeric {
90 type Output = Numeric;
91
92 fn $method(self, rhs: Self) -> Self::Output {
93
94 match (self.is_float(), rhs.is_float()) {
96 (false, false) => Numeric::I64(self.to_int() $op rhs.to_int()),
97 _ => Numeric::F64(self.to_float() $op rhs.to_float()),
98 }
99 }
100 }
101 impl std::ops::$trait for Numeric {
102 type Output = Numeric;
103
104 fn $method(self, rhs: Self) -> Self::Output {
105 &self $op &rhs
106 }
107 }
108 };
109}
110
111impl_numeric_arith!(Add, add, +);
112impl_numeric_arith!(Sub, sub, -);
113impl_numeric_arith!(Mul, mul, *);
114impl_numeric_arith!(Div, div, /);
115impl_numeric_arith!(Rem, rem, %);
116
117impl std::ops::Neg for Numeric {
118 type Output = Self;
119
120 fn neg(self) -> Self::Output {
121 use Numeric::*;
122 match self {
123 I8(a) => I8(-a),
124 I16(a) => I16(-a),
125 I32(a) => I32(-a),
126 I64(a) => I64(-a),
127 F32(a) => F32(-a),
128 F64(a) => F64(-a),
129 ISize(a) => ISize(-a),
130 _ => panic!("tried to negate numeric that is unsigned"),
131 }
132 }
133}
134
135impl PartialOrd for Numeric {
136 fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
137 match (self.is_float(), rhs.is_float()) {
138 (false, false) => self.to_int().partial_cmp(&rhs.to_int()),
139 _ => self.to_float().partial_cmp(&rhs.to_float()),
140 }
141 }
142}
143
144macro_rules! impl_to_from {
145 ($return_type:ty, $Variant:path) => {
146 impl From<&Numeric> for $return_type {
147 fn from(value: &Numeric) -> Self {
148 use Numeric::*;
149 match *value {
150 I8(a) => a as $return_type,
151 I16(a) => a as $return_type,
152 I32(a) => a as $return_type,
153 I64(a) => a as $return_type,
154 U8(a) => a as $return_type,
155 U16(a) => a as $return_type,
156 U32(a) => a as $return_type,
157 U64(a) => a as $return_type,
158 F32(a) => a as $return_type,
159 F64(a) => a as $return_type,
160 ISize(a) => a as $return_type,
161 USize(a) => a as $return_type,
162 }
163 }
164 }
165
166 impl From<Numeric> for $return_type {
167 fn from(value: Numeric) -> Self {
168 (&value).into()
169 }
170 }
171
172 impl From<$return_type> for Numeric {
173 fn from(value: $return_type) -> Self {
174 $Variant(value)
175 }
176 }
177 impl From<&$return_type> for Numeric {
178 fn from(value: &$return_type) -> Self {
179 $Variant(*value)
180 }
181 }
182 };
183}
184
185impl_to_from!(f32, Numeric::F32);
186impl_to_from!(f64, Numeric::F64);
187impl_to_from!(i8, Numeric::I8);
188impl_to_from!(i16, Numeric::I16);
189impl_to_from!(i32, Numeric::I32);
190impl_to_from!(i64, Numeric::I64);
191impl_to_from!(u8, Numeric::U8);
192impl_to_from!(u16, Numeric::U16);
193impl_to_from!(u32, Numeric::U32);
194impl_to_from!(u64, Numeric::U64);
195impl_to_from!(isize, Numeric::ISize);
196impl_to_from!(usize, Numeric::USize);
197
198impl Numeric {
199 pub fn to_float(&self) -> f64 {
201 self.into()
202 }
203
204 pub fn to_int(&self) -> i64 {
206 self.into()
207 }
208
209 pub fn is_float(&self) -> bool {
211 match self {
212 Numeric::F64(_) | Numeric::F32(_) => true,
213 _ => false,
214 }
215 }
216
217 pub fn pow(self, exp: Self) -> Self {
219 match (self.is_float(), exp.is_float()) {
220 (false, false) => Numeric::I64(self.to_int().pow(exp.into())),
221 _ => Numeric::F64(self.to_float().powf(exp.to_float())),
222 }
223 }
224
225 pub fn min(self, other: Self) -> Self {
227 match (self.is_float(), other.is_float()) {
228 (false, false) => Numeric::I64(self.to_int().min(other.to_int())),
229 _ => Numeric::F64(self.to_float().min(other.to_float())),
230 }
231 }
232
233 pub fn max(self, other: Self) -> Self {
235 match (self.is_float(), other.is_float()) {
236 (false, false) => Numeric::I64(self.to_int().max(other.to_int())),
237 _ => Numeric::F64(self.to_float().max(other.to_float())),
238 }
239 }
240}
241
242impl Interpolatable for Numeric {
243 fn interpolate(&self, other: &Self, t: f64) -> Self {
244 Numeric::F64(Into::<f64>::into(self).interpolate(&other.into(), t))
245 }
246}
247
248impl From<Size> for Numeric {
249 fn from(value: Size) -> Self {
250 match value {
251 Size::Pixels(x) | Size::Percent(x) => x,
252 Size::Combined(_, _) => unreachable!("Cannot coerce a combined size to a numeric"),
253 }
254 }
255}