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
59#[macro_export]
60macro_rules! checked [
92 ($($v:ident$(,)?)*) => {
93 $( let $v = $crate::math::Checked::new($v); )*
94 };
95];
96
97#[doc(hidden)]
99#[macro_export]
100macro_rules! mut_checked [
101 ($v:ident) => {
102 let mut $v = $crate::math::Checked::new($v);
103 };
104 ($($v:ident,)*) => {
105 $( let mut $v = $crate::math::Checked::new($v); )*
106 };
107];
108
109pub use {checked, mut_checked};
110
111macro_rules! impl_operator {
112 ($op:ident, $f:ident, $t:ty) => {
113 paste::paste! {
114 impl [< Safe $op >] for $t {
115 fn [< safe_ $f >](self, rhs: $t) -> Result<$t, MathError> {
116 let err = || MathError:: [< $op Failed >] (self.try_into().ok(), rhs.try_into().ok());
117 self.[< checked_ $f >](rhs)
118 .ok_or_else(err)
119 }
120 }
121 }
122
123 impl<R: Into<Self> + Copy> std::ops::$op<R> for Checked<$t>
124 {
125 type Output = Self;
126
127 #[inline]
128 fn $f(self, rhs: R) -> Self::Output {
129 let Checked(Ok(x)) = self else { return self };
130 let Checked(Ok(y)) = rhs.into() else { return rhs.into() };
131 paste::paste! {
132 let res = x.[< safe_ $f >](y);
133 }
134 Checked(res)
135 }
136 }
137
138 impl std::ops::$op<Checked<$t>> for $t
139 {
140 type Output = Checked<$t>;
141
142 #[inline]
143 fn $f(self, rhs: Checked<$t>) -> Self::Output {
144 let y = match rhs.0 {
145 Err(err) => return Checked(Err(err)),
146 Ok(y) => y.try_into(),
147 };
148 let y: $t = match y {
149 Err(_) => return Checked(Err(MathError::ConversionFailed(Some(0)))),
150 Ok(y) => y,
151 };
152 paste::paste! {
153 let res = self.[< safe_ $f >](y);
154 }
155 Checked(res)
156 }
157 }
158 };
159}
160
161macro_rules! impl_binary_operators {
162 ($t:ty) => {
163 impl_operator!(Add, add, $t);
164 impl_operator!(Sub, sub, $t);
165 impl_operator!(Mul, mul, $t);
166 impl_operator!(Div, div, $t);
167 };
168}
169
170macro_rules! impl_cast {
171 ($t:ty, $target:ident) => {
172 impl Checked<$t> {
173 pub fn $target(self) -> Checked<$target> {
174 let x = match self.0 {
175 Err(err) => return Checked(Err(err)),
176 Ok(v) => v,
177 };
178 Checked(
179 x.try_into()
180 .map_err(|_| MathError::ConversionFailed(x.try_into().ok())),
181 )
182 }
183 }
184 };
185}
186
187macro_rules! impl_casts {
188 ($t:ty) => {
189 impl_cast!($t, u8);
190 impl_cast!($t, u16);
191 impl_cast!($t, u32);
192 impl_cast!($t, u64);
193 impl_cast!($t, i16);
194 impl_cast!($t, i32);
195 impl_cast!($t, i64);
196 impl_cast!($t, usize);
197
198 impl ToU8 for $t {}
199 impl ToU16 for $t {}
200 impl ToU32 for $t {}
201 impl ToU64 for $t {}
202 impl ToI64 for $t {}
203 impl ToUsize for $t {}
204 };
205}
206
207impl_binary_operators!(u8);
208impl_binary_operators!(u16);
209impl_binary_operators!(u32);
210impl_binary_operators!(u64);
211impl_binary_operators!(i16);
212impl_binary_operators!(i32);
213impl_binary_operators!(i64);
214impl_binary_operators!(usize);
215
216impl_casts!(u8);
217impl_casts!(u16);
218impl_casts!(u32);
219impl_casts!(u64);
220impl_casts!(i16);
221impl_casts!(i32);
222impl_casts!(i64);
223impl_casts!(usize);
224
225pub trait ToU8: Sized + TryInto<u8> + TryInto<i128> + Copy {
226 fn u8(self) -> Result<u8, MathError> {
227 self.try_into()
228 .map_err(|_| MathError::ConversionFailed(self.try_into().ok()))
229 }
230}
231
232pub trait ToU16: Sized + TryInto<u16> + TryInto<i128> + Copy {
233 fn u16(self) -> Result<u16, MathError> {
234 self.try_into()
235 .map_err(|_| MathError::ConversionFailed(self.try_into().ok()))
236 }
237}
238
239pub trait ToU32: Sized + TryInto<u32> + TryInto<i128> + Copy {
240 fn u32(self) -> Result<u32, MathError> {
241 self.try_into()
242 .map_err(|_| MathError::ConversionFailed(self.try_into().ok()))
243 }
244}
245
246pub trait ToU64: Sized + TryInto<u64> + TryInto<i128> + Copy {
247 fn u64(self) -> Result<u64, MathError> {
248 self.try_into()
249 .map_err(|_| MathError::ConversionFailed(self.try_into().ok()))
250 }
251}
252
253pub trait ToI16: Sized + TryInto<i16> + TryInto<i128> + Copy {
254 fn i16(self) -> Result<i16, MathError> {
255 self.try_into()
256 .map_err(|_| MathError::ConversionFailed(self.try_into().ok()))
257 }
258}
259
260pub trait ToI32: Sized + TryInto<i32> + TryInto<i128> + Copy {
261 fn i32(self) -> Result<i32, MathError> {
262 self.try_into()
263 .map_err(|_| MathError::ConversionFailed(self.try_into().ok()))
264 }
265}
266
267pub trait ToI64: Sized + TryInto<i64> + TryInto<i128> + Copy {
268 fn i64(self) -> Result<i64, MathError> {
269 self.try_into()
270 .map_err(|_| MathError::ConversionFailed(self.try_into().ok()))
271 }
272}
273
274pub trait ToUsize: Sized + TryInto<usize> + TryInto<i128> + Copy {
275 fn usize(self) -> Result<usize, MathError> {
276 self.try_into()
277 .map_err(|_| MathError::ConversionFailed(self.try_into().ok()))
278 }
279}
280
281pub trait SafeAdd: Sized {
283 fn safe_add(self, rhs: Self) -> Result<Self, MathError>;
284}
285
286pub trait SafeSub: Sized {
288 fn safe_sub(self, rhs: Self) -> Result<Self, MathError>;
289}
290
291pub trait SafeMul: Sized {
293 fn safe_mul(self, rhs: Self) -> Result<Self, MathError>;
294}
295
296pub trait SafeDiv: Sized {
298 fn safe_div(self, rhs: Self) -> Result<Self, MathError>;
299}
300
301impl SafeDiv for f64 {
302 fn safe_div(self, rhs: Self) -> Result<Self, MathError> {
303 let value = self / rhs;
304
305 if value.is_infinite() {
306 Err(MathError::DivisionNotFinite(Some(self), Some(rhs)))
307 } else {
308 Ok(value)
309 }
310 }
311}
312
313pub trait SafeNeg: Sized {
315 fn safe_neg(self) -> Result<Self, MathError>;
316}
317
318impl SafeNeg for i64 {
319 fn safe_neg(self) -> Result<Self, MathError> {
320 self.checked_neg()
321 .ok_or_else(|| MathError::NegationOverflow(Some(self.into())))
322 }
323}
324
325pub fn apex_to_f_number(apex: f32) -> f32 {
329 f32::sqrt(1.4).powf(apex)
330}