1#[derive(Debug, thiserror::Error, Clone, Copy)]
4pub enum MathError {
5 #[error("Operation {0:?} + {1:?} failed")]
6 AddFailed(Option<i128>, Option<i128>),
7 #[error("Operation {0:?} - {1:?} failed")]
8 SubFailed(Option<i128>, Option<i128>),
9 #[error("Operation {0:?} * {1:?} failed")]
10 MulFailed(Option<i128>, Option<i128>),
11 #[error("Operation {0:?} / {1:?} failed")]
12 DivFailed(Option<i128>, Option<i128>),
13 #[error("Conversion failed for value {0:?}")]
14 ConversionFailed(Option<i128>),
15 #[error("Division {0:?} / {1:?} not finite")]
16 DivisionNotFinite(Option<f64>, Option<f64>),
17 #[error("Negation overflowed for {0:?}")]
18 NegationOverflow(Option<i128>),
19}
20
21#[derive(Debug, Clone, Copy)]
32pub struct Checked<T>(Result<T, MathError>);
33
34impl<T> Checked<T> {
35 pub fn new(val: T) -> Self {
36 Self(Ok(val))
37 }
38
39 pub fn check(self) -> Result<T, MathError> {
41 self.0
42 }
43}
44
45impl<T> From<T> for Checked<T> {
46 fn from(val: T) -> Self {
47 Self(Ok(val))
48 }
49}
50
51impl<T> std::ops::Deref for Checked<T> {
52 type Target = Result<T, MathError>;
53
54 fn deref(&self) -> &Self::Target {
55 &self.0
56 }
57}
58
59pub fn cheq<T>(val: T) -> Checked<T> {
60 Checked::new(val)
61}
62
63#[macro_export]
64macro_rules! checked [
96 ($($v:ident$(,)?)*) => {
97 $( let $v = $crate::math::Checked::new($v); )*
98 };
99];
100
101#[doc(hidden)]
103#[macro_export]
104macro_rules! mut_checked [
105 ($v:ident) => {
106 let mut $v = $crate::math::Checked::new($v);
107 };
108 ($($v:ident,)*) => {
109 $( let mut $v = $crate::math::Checked::new($v); )*
110 };
111];
112
113pub use checked;
114pub use mut_checked;
115
116macro_rules! impl_operator {
117 ($op:ident, $f:ident, $t:ty) => {
118 paste::paste! {
119 impl [< Safe $op >] for $t {
120 fn [< safe_ $f >](self, rhs: $t) -> Result<$t, MathError> {
121 let err = || MathError:: [< $op Failed >] (self.try_into().ok(), rhs.try_into().ok());
122 self.[< checked_ $f >](rhs)
123 .ok_or_else(err)
124 }
125 }
126 }
127
128 impl<R: Into<Self> + Copy> std::ops::$op<R> for Checked<$t>
129 {
130 type Output = Self;
131
132 #[inline]
133 fn $f(self, rhs: R) -> Self::Output {
134 let Checked(Ok(x)) = self else { return self };
135 let Checked(Ok(y)) = rhs.into() else { return rhs.into() };
136 paste::paste! {
137 let res = x.[< safe_ $f >](y);
138 }
139 Checked(res)
140 }
141 }
142
143 impl std::ops::$op<Checked<$t>> for $t
144 {
145 type Output = Checked<$t>;
146
147 #[inline]
148 fn $f(self, rhs: Checked<$t>) -> Self::Output {
149 let y = match rhs.0 {
150 Err(err) => return Checked(Err(err)),
151 Ok(y) => y.try_into(),
152 };
153 let y: $t = match y {
154 Err(_) => return Checked(Err(MathError::ConversionFailed(Some(0)))),
155 Ok(y) => y,
156 };
157 paste::paste! {
158 let res = self.[< safe_ $f >](y);
159 }
160 Checked(res)
161 }
162 }
163 };
164}
165
166macro_rules! impl_binary_operators {
167 ($t:ty) => {
168 impl_operator!(Add, add, $t);
169 impl_operator!(Sub, sub, $t);
170 impl_operator!(Mul, mul, $t);
171 impl_operator!(Div, div, $t);
172 };
173}
174
175macro_rules! impl_cast {
176 ($t:ty, $target:ident) => {
177 impl Checked<$t> {
178 pub fn $target(self) -> Checked<$target> {
179 let x = match self.0 {
180 Err(err) => return Checked(Err(err)),
181 Ok(v) => v,
182 };
183 Checked(
184 x.try_into()
185 .map_err(|_| MathError::ConversionFailed(x.try_into().ok())),
186 )
187 }
188 }
189 };
190}
191
192macro_rules! impl_casts {
193 ($t:ty) => {
194 impl_cast!($t, u8);
195 impl_cast!($t, u16);
196 impl_cast!($t, u32);
197 impl_cast!($t, u64);
198 impl_cast!($t, i16);
199 impl_cast!($t, i32);
200 impl_cast!($t, i64);
201 impl_cast!($t, usize);
202
203 impl ToU8 for $t {}
204 impl ToU16 for $t {}
205 impl ToU32 for $t {}
206 impl ToU64 for $t {}
207 impl ToI64 for $t {}
208 impl ToUsize for $t {}
209 };
210}
211
212impl_binary_operators!(u8);
213impl_binary_operators!(u16);
214impl_binary_operators!(u32);
215impl_binary_operators!(u64);
216impl_binary_operators!(i16);
217impl_binary_operators!(i32);
218impl_binary_operators!(i64);
219impl_binary_operators!(usize);
220
221impl_casts!(u8);
222impl_casts!(u16);
223impl_casts!(u32);
224impl_casts!(u64);
225impl_casts!(i16);
226impl_casts!(i32);
227impl_casts!(i64);
228impl_casts!(usize);
229
230pub trait ToU8: Sized + TryInto<u8> + TryInto<i128> + Copy {
231 fn u8(self) -> Result<u8, MathError> {
232 self.try_into()
233 .map_err(|_| MathError::ConversionFailed(self.try_into().ok()))
234 }
235}
236
237pub trait ToU16: Sized + TryInto<u16> + TryInto<i128> + Copy {
238 fn u16(self) -> Result<u16, MathError> {
239 self.try_into()
240 .map_err(|_| MathError::ConversionFailed(self.try_into().ok()))
241 }
242}
243
244pub trait ToU32: Sized + TryInto<u32> + TryInto<i128> + Copy {
245 fn u32(self) -> Result<u32, MathError> {
246 self.try_into()
247 .map_err(|_| MathError::ConversionFailed(self.try_into().ok()))
248 }
249}
250
251pub trait ToU64: Sized + TryInto<u64> + TryInto<i128> + Copy {
252 fn u64(self) -> Result<u64, MathError> {
253 self.try_into()
254 .map_err(|_| MathError::ConversionFailed(self.try_into().ok()))
255 }
256}
257
258pub trait ToI16: Sized + TryInto<i16> + TryInto<i128> + Copy {
259 fn i16(self) -> Result<i16, MathError> {
260 self.try_into()
261 .map_err(|_| MathError::ConversionFailed(self.try_into().ok()))
262 }
263}
264
265pub trait ToI32: Sized + TryInto<i32> + TryInto<i128> + Copy {
266 fn i32(self) -> Result<i32, MathError> {
267 self.try_into()
268 .map_err(|_| MathError::ConversionFailed(self.try_into().ok()))
269 }
270}
271
272pub trait ToI64: Sized + TryInto<i64> + TryInto<i128> + Copy {
273 fn i64(self) -> Result<i64, MathError> {
274 self.try_into()
275 .map_err(|_| MathError::ConversionFailed(self.try_into().ok()))
276 }
277}
278
279pub trait ToUsize: Sized + TryInto<usize> + TryInto<i128> + Copy {
280 fn usize(self) -> Result<usize, MathError> {
281 self.try_into()
282 .map_err(|_| MathError::ConversionFailed(self.try_into().ok()))
283 }
284}
285
286pub trait SafeAdd: Sized {
288 fn safe_add(self, rhs: Self) -> Result<Self, MathError>;
289}
290
291pub trait SafeSub: Sized {
293 fn safe_sub(self, rhs: Self) -> Result<Self, MathError>;
294}
295
296pub trait SafeMul: Sized {
298 fn safe_mul(self, rhs: Self) -> Result<Self, MathError>;
299}
300
301pub trait SafeDiv: Sized {
303 fn safe_div(self, rhs: Self) -> Result<Self, MathError>;
304}
305
306impl SafeDiv for f64 {
307 fn safe_div(self, rhs: Self) -> Result<Self, MathError> {
308 let value = self / rhs;
309
310 if value.is_infinite() {
311 Err(MathError::DivisionNotFinite(Some(self), Some(rhs)))
312 } else {
313 Ok(value)
314 }
315 }
316}
317
318pub trait SafeNeg: Sized {
320 fn safe_neg(self) -> Result<Self, MathError>;
321}
322
323impl SafeNeg for i64 {
324 fn safe_neg(self) -> Result<Self, MathError> {
325 self.checked_neg()
326 .ok_or_else(|| MathError::NegationOverflow(Some(self.into())))
327 }
328}
329
330pub fn apex_to_f_number(apex: f32) -> f32 {
334 f32::sqrt(1.4).powf(apex)
335}