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