Skip to main content

mathverse_core/
traits.rs

1//! Numeric abstraction traits.
2//!
3//! Every `MathVerse` crate is generic over these traits. The hierarchy is:
4//!
5//! ```text
6//!                         ┌─────────┐
7//!                         │   Num   │   Integer + Float
8//!                         └────┬────┘
9//!                              │
10//!                     ┌────────┴────────┐
11//!                     │                 │
12//!                ┌────┴────┐     ┌─────┴─────┐
13//!                │ Signed  │     │ Unsigned* │  (*marker, not enforced)
14//!                └────┬────┘     └───────────┘
15//!                     │
16//!            ┌────────┴────────┐
17//!            │                 │
18//!       ┌────┴────┐     ┌──────┴──────┐
19//!       │  Field  │     │   (marker)  │
20//!       └────┬────┘     └─────────────┘
21//!            │
22//!       ┌────┴────┐
23//!       │  Real   │   f32 / f64 blanket impl
24//!       └─────────┘
25//! ```
26
27use core::fmt::Debug;
28use core::ops::{Add, Div, Mul, Neg, Rem, Sub};
29
30/// Foundation: anything that can add, subtract, multiply, and has 0/1.
31pub trait Num:
32    Copy + PartialEq + Debug + Add<Output = Self> + Sub<Output = Self> + Mul<Output = Self>
33{
34    /// Additive identity.
35    fn zero() -> Self;
36    /// Multiplicative identity.
37    fn one() -> Self;
38    /// Convert from a signed integer (lossy for floats below 2^53).
39    fn from_i64(v: i64) -> Self;
40}
41
42/// Signed numbers: negation, absolute value, sign.
43pub trait Signed: Num + Neg<Output = Self> + PartialOrd {
44    /// Absolute value.
45    fn abs(self) -> Self;
46    /// -1, 0, or +1.
47    fn signum(self) -> Self;
48    /// Returns `true` if the number is negative.
49    fn is_negative(self) -> bool;
50}
51
52/// Division ring: everything [`Num`] is, plus division and reciprocal.
53pub trait Field: Num + Div<Output = Self> + Rem<Output = Self> {
54    /// Divide self by other.
55    fn div(self, other: Self) -> Self;
56    /// `1 / self`.
57    fn reciprocal(self) -> Self;
58}
59
60/// Real numbers: transcendental functions, powers, rounding, classification.
61///
62/// Implemented for `f32` and `f64`. Functions take radians unless a `*_deg`
63/// variant exists.
64pub trait Real: Num + Signed + Div<Output = Self> + Rem<Output = Self> + PartialOrd {
65    /// Convert from `f64`.
66    fn from_f64(v: f64) -> Self;
67    /// Convert to `f64`.
68    fn to_f64(self) -> f64;
69
70    /// Square root.
71    fn sqrt(self) -> Self;
72    /// Cube root.
73    fn cbrt(self) -> Self;
74    /// Raise to a floating-point power.
75    fn powf(self, e: Self) -> Self;
76    /// Raise to an integer power.
77    fn powi(self, e: i32) -> Self;
78    /// `e^self`.
79    fn exp(self) -> Self;
80    /// `e^self - 1`, accurate for small `self`.
81    fn exp_m1(self) -> Self;
82    /// Natural logarithm.
83    fn ln(self) -> Self;
84    /// `ln(1 + self)`, accurate for small `self`.
85    fn ln_1p(self) -> Self;
86    /// Logarithm with arbitrary base.
87    fn log(self, base: Self) -> Self;
88    /// Log base 10.
89    fn log10(self) -> Self;
90    /// Log base 2.
91    fn log2(self) -> Self;
92
93    /// Sine (radians).
94    fn sin(self) -> Self;
95    /// Cosine (radians).
96    fn cos(self) -> Self;
97    /// Tangent (radians).
98    fn tan(self) -> Self;
99    /// Arcsine.
100    fn asin(self) -> Self;
101    /// Arccosine.
102    fn acos(self) -> Self;
103    /// Arctangent.
104    fn atan(self) -> Self;
105    /// Two-argument arctangent: `atan(self / other)`.
106    fn atan2(self, other: Self) -> Self;
107
108    /// Hyperbolic sine.
109    fn sinh(self) -> Self;
110    /// Hyperbolic cosine.
111    fn cosh(self) -> Self;
112    /// Hyperbolic tangent.
113    fn tanh(self) -> Self;
114    /// Inverse hyperbolic sine.
115    fn asinh(self) -> Self;
116    /// Inverse hyperbolic cosine.
117    fn acosh(self) -> Self;
118    /// Inverse hyperbolic tangent.
119    fn atanh(self) -> Self;
120
121    /// Sine and cosine simultaneously.
122    fn sin_cos(self) -> (Self, Self);
123    /// Hypotenuse: `sqrt(self^2 + other^2)`.
124    fn hypot(self, other: Self) -> Self;
125    /// Copy the sign of `other` to the magnitude of `self`.
126    fn copysign(self, other: Self) -> Self;
127    /// `max(self - other, 0)`.
128    fn abs_sub(self, other: Self) -> Self;
129    /// Reciprocal: `1 / self`.
130    fn recip(self) -> Self;
131
132    /// Floor: largest integer <= self.
133    fn floor(self) -> Self;
134    /// Ceiling: smallest integer >= self.
135    fn ceil(self) -> Self;
136    /// Round to nearest integer.
137    fn round(self) -> Self;
138    /// Truncate toward zero.
139    fn trunc(self) -> Self;
140    /// Fractional part: `self - floor(self)`.
141    fn fract(self) -> Self;
142
143    /// Minimum of self and other.
144    fn min(self, other: Self) -> Self;
145    /// Maximum of self and other.
146    fn max(self, other: Self) -> Self;
147
148    /// Returns `true` if self is neither infinite nor NaN.
149    fn is_finite(self) -> bool;
150    /// Returns `true` if self is NaN.
151    fn is_nan(self) -> bool;
152    /// Returns `true` if self is positive or negative infinity.
153    fn is_infinite(self) -> bool;
154    /// Returns `true` if self is neither zero, infinite, subnormal, nor NaN.
155    fn is_normal(self) -> bool;
156    /// Returns `true` if self is subnormal (denormal).
157    fn is_subnormal(self) -> bool;
158    /// Returns `true` if the sign bit is negative.
159    fn is_sign_negative(self) -> bool;
160    /// Returns `true` if the sign bit is positive (or zero).
161    fn is_sign_positive(self) -> bool;
162
163    /// Returns -1, 0, or +1.
164    fn signum(self) -> Self;
165}
166
167/// Normed type: has an absolute value satisfying the triangle inequality.
168///
169/// `norm(x) >= 0`, `norm(x) == 0` iff `x == 0`, `norm(x + y) <= norm(x) + norm(y)`.
170pub trait Normed: Signed {
171    /// The norm (absolute value) of this value.
172    fn norm(self) -> Self;
173}
174
175macro_rules! impl_num_int {
176    ($($t:ty),* $(,)?) => {$(
177        impl Num for $t {
178            fn zero() -> Self { 0 }
179            fn one() -> Self { 1 }
180            fn from_i64(v: i64) -> Self { v as $t }
181        }
182    )*};
183}
184impl_num_int!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
185
186macro_rules! impl_num_float {
187    ($($t:ty),* $(,)?) => {$(
188        impl Num for $t {
189            fn zero() -> Self { 0.0 }
190            fn one() -> Self { 1.0 }
191            fn from_i64(v: i64) -> Self { v as $t }
192        }
193    )*};
194}
195impl_num_float!(f32, f64);
196
197macro_rules! impl_signed_int {
198    ($($t:ty),* $(,)?) => {$(
199        impl Signed for $t {
200            fn abs(self) -> Self { self.abs() }
201            fn signum(self) -> Self { self.signum() }
202            fn is_negative(self) -> bool { self < 0 }
203        }
204    )*};
205}
206impl_signed_int!(i8, i16, i32, i64, isize);
207
208macro_rules! impl_signed_float {
209    ($($t:ty),* $(,)?) => {$(
210        impl Signed for $t {
211            fn abs(self) -> Self { self.abs() }
212            fn signum(self) -> Self { self.signum() }
213            fn is_negative(self) -> bool { self.is_sign_negative() }
214        }
215    )*};
216}
217impl_signed_float!(f32, f64);
218
219macro_rules! impl_field_float {
220    ($($t:ty),* $(,)?) => {$(
221        impl Field for $t {
222            fn div(self, other: Self) -> Self { self / other }
223            fn reciprocal(self) -> Self { 1.0 / self }
224        }
225    )*};
226}
227impl_field_float!(f32, f64);
228
229macro_rules! impl_real {
230    ($($t:ty),* $(,)?) => {$(
231        impl Real for $t {
232            fn from_f64(v: f64) -> Self { v as $t }
233            fn to_f64(self) -> f64 { f64::from(self) }
234            fn sqrt(self) -> Self { self.sqrt() }
235            fn cbrt(self) -> Self { self.cbrt() }
236            fn powf(self, e: Self) -> Self { self.powf(e) }
237            fn powi(self, e: i32) -> Self { self.powi(e) }
238            fn exp(self) -> Self { self.exp() }
239            fn exp_m1(self) -> Self { self.exp_m1() }
240            fn ln(self) -> Self { self.ln() }
241            fn ln_1p(self) -> Self { self.ln_1p() }
242            fn log(self, base: Self) -> Self { self.log(base) }
243            fn log10(self) -> Self { self.log10() }
244            fn log2(self) -> Self { self.log2() }
245            fn sin(self) -> Self { self.sin() }
246            fn cos(self) -> Self { self.cos() }
247            fn tan(self) -> Self { self.tan() }
248            fn asin(self) -> Self { self.asin() }
249            fn acos(self) -> Self { self.acos() }
250            fn atan(self) -> Self { self.atan() }
251            fn atan2(self, other: Self) -> Self { self.atan2(other) }
252            fn sinh(self) -> Self { self.sinh() }
253            fn cosh(self) -> Self { self.cosh() }
254            fn tanh(self) -> Self { self.tanh() }
255            fn asinh(self) -> Self { self.asinh() }
256            fn acosh(self) -> Self { self.acosh() }
257            fn atanh(self) -> Self { self.atanh() }
258            fn sin_cos(self) -> (Self, Self) { self.sin_cos() }
259            fn hypot(self, other: Self) -> Self { self.hypot(other) }
260            fn copysign(self, other: Self) -> Self { self.copysign(other) }
261            fn abs_sub(self, other: Self) -> Self { (self - other).max(Self::zero()) }
262            fn recip(self) -> Self { self.recip() }
263            fn floor(self) -> Self { self.floor() }
264            fn ceil(self) -> Self { self.ceil() }
265            fn round(self) -> Self { self.round() }
266            fn trunc(self) -> Self { self.trunc() }
267            fn fract(self) -> Self { self.fract() }
268            fn min(self, other: Self) -> Self { self.min(other) }
269            fn max(self, other: Self) -> Self { self.max(other) }
270            fn is_finite(self) -> bool { self.is_finite() }
271            fn is_nan(self) -> bool { self.is_nan() }
272            fn is_infinite(self) -> bool { self.is_infinite() }
273            fn is_normal(self) -> bool { self.is_normal() }
274            fn is_subnormal(self) -> bool { self.is_subnormal() }
275            fn is_sign_negative(self) -> bool { self.is_sign_negative() }
276            fn is_sign_positive(self) -> bool { self.is_sign_positive() }
277            fn signum(self) -> Self { self.signum() }
278        }
279    )*};
280}
281
282// std impl: uses hardware-accelerated float methods
283#[cfg(feature = "std")]
284impl_real!(f32, f64);
285
286// no_std + libm impl: uses libm software implementations
287#[cfg(all(not(feature = "std"), feature = "libm"))]
288macro_rules! impl_real_libm {
289    ($($t:ty),* $(,)?) => {$(
290        impl Real for $t {
291            fn from_f64(v: f64) -> Self { v as $t }
292            fn to_f64(self) -> f64 { f64::from(self) }
293            fn sqrt(self) -> Self { crate::libm_fallback::sqrt(self as f64) as $t }
294            fn cbrt(self) -> Self { crate::libm_fallback::cbrt(self as f64) as $t }
295            fn powf(self, e: Self) -> Self { crate::libm_fallback::powf(self as f64, e as f64) as $t }
296            fn powi(self, e: i32) -> Self { crate::libm_fallback::powi(self as f64, e) as $t }
297            fn exp(self) -> Self { crate::libm_fallback::exp(self as f64) as $t }
298            fn exp_m1(self) -> Self { crate::libm_fallback::exp_m1(self as f64) as $t }
299            fn ln(self) -> Self { crate::libm_fallback::ln(self as f64) as $t }
300            fn ln_1p(self) -> Self { crate::libm_fallback::ln_1p(self as f64) as $t }
301            fn log(self, base: Self) -> Self { crate::libm_fallback::log(self as f64, base as f64) as $t }
302            fn log10(self) -> Self { crate::libm_fallback::log10(self as f64) as $t }
303            fn log2(self) -> Self { crate::libm_fallback::log2(self as f64) as $t }
304            fn sin(self) -> Self { crate::libm_fallback::sin(self as f64) as $t }
305            fn cos(self) -> Self { crate::libm_fallback::cos(self as f64) as $t }
306            fn tan(self) -> Self { crate::libm_fallback::tan(self as f64) as $t }
307            fn asin(self) -> Self { crate::libm_fallback::asin(self as f64) as $t }
308            fn acos(self) -> Self { crate::libm_fallback::acos(self as f64) as $t }
309            fn atan(self) -> Self { crate::libm_fallback::atan(self as f64) as $t }
310            fn atan2(self, other: Self) -> Self { crate::libm_fallback::atan2(self as f64, other as f64) as $t }
311            fn sinh(self) -> Self { crate::libm_fallback::sinh(self as f64) as $t }
312            fn cosh(self) -> Self { crate::libm_fallback::cosh(self as f64) as $t }
313            fn tanh(self) -> Self { crate::libm_fallback::tanh(self as f64) as $t }
314            fn asinh(self) -> Self { crate::libm_fallback::asinh(self as f64) as $t }
315            fn acosh(self) -> Self { crate::libm_fallback::acosh(self as f64) as $t }
316            fn atanh(self) -> Self { crate::libm_fallback::atanh(self as f64) as $t }
317            fn sin_cos(self) -> (Self, Self) {
318                let (s, c) = crate::libm_fallback::sin_cos(self as f64);
319                (s as $t, c as $t)
320            }
321            fn hypot(self, other: Self) -> Self { crate::libm_fallback::hypot(self as f64, other as f64) as $t }
322            fn copysign(self, other: Self) -> Self {
323                if other.is_sign_negative() {
324                    self.abs().neg()
325                } else {
326                    self.abs()
327                }
328            }
329            fn abs_sub(self, other: Self) -> Self { (self - other).max(Self::zero()) }
330            fn recip(self) -> Self { 1.0 / self }
331            fn floor(self) -> Self { crate::libm_fallback::floor(self as f64) as $t }
332            fn ceil(self) -> Self { crate::libm_fallback::ceil(self as f64) as $t }
333            fn round(self) -> Self { crate::libm_fallback::round(self as f64) as $t }
334            fn trunc(self) -> Self { crate::libm_fallback::trunc(self as f64) as $t }
335            fn fract(self) -> Self { self - crate::libm_fallback::floor(self as f64) as $t }
336            fn min(self, other: Self) -> Self { if self < other { self } else { other } }
337            fn max(self, other: Self) -> Self { if self > other { self } else { other } }
338            fn is_finite(self) -> bool { self.is_finite() }
339            fn is_nan(self) -> bool { self.is_nan() }
340            fn is_infinite(self) -> bool { self.is_infinite() }
341            fn is_normal(self) -> bool { self.is_normal() }
342            fn is_subnormal(self) -> bool { self.is_finite() && self != 0.0 && self.abs() < Self::MIN_POSITIVE }
343            fn is_sign_negative(self) -> bool { self.is_sign_negative() }
344            fn is_sign_positive(self) -> bool { self.is_sign_positive() }
345            fn signum(self) -> Self { self.signum() }
346        }
347    )*};
348}
349#[cfg(all(not(feature = "std"), feature = "libm"))]
350impl_real_libm!(f32, f64);
351
352impl Normed for f32 {
353    fn norm(self) -> Self {
354        self.abs()
355    }
356}
357impl Normed for f64 {
358    fn norm(self) -> Self {
359        self.abs()
360    }
361}
362impl Normed for i8 {
363    fn norm(self) -> Self {
364        self.abs()
365    }
366}
367impl Normed for i16 {
368    fn norm(self) -> Self {
369        self.abs()
370    }
371}
372impl Normed for i32 {
373    fn norm(self) -> Self {
374        self.abs()
375    }
376}
377impl Normed for i64 {
378    fn norm(self) -> Self {
379        self.abs()
380    }
381}
382impl Normed for isize {
383    fn norm(self) -> Self {
384        self.abs()
385    }
386}
387
388#[cfg(test)]
389#[allow(clippy::suboptimal_flops, clippy::imprecise_flops)]
390mod tests {
391    use super::*;
392
393    #[test]
394    fn identity_laws() {
395        for a in 0..100i64 {
396            assert_eq!(a + <i64 as Num>::zero(), a);
397            assert_eq!(a * <i64 as Num>::one(), a);
398        }
399    }
400
401    #[test]
402    fn signed_abs() {
403        assert_eq!((-5i32).abs(), 5);
404        assert_eq!((-2.5f64).abs(), 2.5);
405        assert_eq!((-2.5f64).signum(), -1.0);
406    }
407
408    #[test]
409    fn real_ops() {
410        let x = 9.0f64;
411        assert_eq!(x.sqrt(), 3.0);
412        assert_eq!(x.cbrt(), 2.080083823051904);
413        assert_eq!(x.powi(2), 81.0);
414        assert_eq!(x.powf(0.5), 3.0);
415        assert_eq!(x.ln(), 2.0 * 3.0f64.ln());
416        assert!((1.0f64.sin().powi(2) + 1.0f64.cos().powi(2) - 1.0).abs() < 1e-12);
417        assert!((0.5f64.asin() - 0.5f64.asin()).abs() < 1e-12);
418        assert!((0.5f64.acos() - 0.5f64.acos()).abs() < 1e-12);
419        assert!((1.0f64.atan() - core::f64::consts::FRAC_PI_4).abs() < 1e-12);
420        assert!((1.0f64.atan2(1.0) - core::f64::consts::FRAC_PI_4).abs() < 1e-12);
421        assert!((1.0f64.cosh().powi(2) - 1.0f64.sinh().powi(2) - 1.0).abs() < 1e-12);
422        assert!((0.5f64.tanh() - 0.5f64.tanh()).abs() < 1e-12);
423        assert!((1.0f64.asinh() - 1.0f64.asinh()).abs() < 1e-12);
424        assert!((0.001f64.exp_m1() - (0.001f64.exp() - 1.0)).abs() < 1e-15);
425        assert!((0.001f64.ln_1p() - 0.001f64.ln_1p()).abs() < 1e-15);
426        let (s, c) = 0.0f64.sin_cos();
427        assert!((s - 0.0f64.sin()).abs() < 1e-15);
428        assert!((c - 0.0f64.cos()).abs() < 1e-15);
429        assert!((3.0f64.hypot(4.0) - 5.0).abs() < 1e-12);
430        assert_eq!(3.0f64.copysign(-1.0), -3.0);
431        assert_eq!(Real::abs_sub(5.0f64, 3.0), 2.0);
432        assert_eq!(Real::abs_sub(4.0f64, 5.0), 0.0);
433        assert_eq!(4.0f64.recip(), 0.25);
434        assert!((8.0f64.log2() - 3.0).abs() < 1e-12);
435        assert_eq!(2.7f64.trunc(), 2.0);
436        assert!((2.7f64.fract() - 0.7).abs() < 1e-12);
437        assert_eq!((-2.5f64).signum(), -1.0);
438        assert!(1.0f64.is_normal());
439        assert!(!0.0f64.is_normal());
440        assert!(1.0f64.is_sign_positive());
441        assert!((-0.0f64).is_sign_negative());
442    }
443
444    #[test]
445    fn field_reciprocal() {
446        assert_eq!(4.0f64.reciprocal(), 0.25);
447        assert_eq!(Field::div(10.0f32, 4.0), 2.5);
448    }
449}