Skip to main content

rill_core/math/
num.rs

1use core::fmt;
2use core::ops::*;
3
4/// Base numeric trait for all scalar types.
5///
6/// Includes arithmetic, min/max/clamp, and abs operations.
7/// Implemented for f32, f64 and all integer types (i8/i16/i32/i64, u8/u16/u32/u64).
8pub trait Scalar:
9    Copy
10    + Clone
11    + Send
12    + Sync
13    + 'static
14    + Default
15    + PartialOrd
16    + Add<Output = Self>
17    + Sub<Output = Self>
18    + Mul<Output = Self>
19    + Div<Output = Self>
20    + Rem<Output = Self>
21    + Neg<Output = Self>
22    + AddAssign
23    + SubAssign
24    + MulAssign
25    + DivAssign
26    + fmt::Debug
27{
28    /// Additive identity (zero).
29    const ZERO: Self;
30    /// Multiplicative identity (one).
31    const ONE: Self;
32    /// Minimum value typically used in normalized ranges (e.g. -1.0 for floats).
33    const MIN: Self;
34    /// Maximum value typically used in normalized ranges (e.g. 1.0 for floats).
35    const MAX: Self;
36
37    /// Compute the absolute value.
38    fn abs(self) -> Self;
39    /// Return the smaller of two values.
40    fn min(self, other: Self) -> Self;
41    /// Return the larger of two values.
42    fn max(self, other: Self) -> Self;
43    /// Constrain a value to the inclusive range `[min, max]`.
44    fn clamp(self, min: Self, max: Self) -> Self;
45
46    /// Create a value from a `usize`. For floats, this is `n as f32`/`n as f64`.
47    /// For integers, this is `n as $ty`.
48    fn from_usize(n: usize) -> Self;
49}
50
51/// Transcendental operations (sin, cos, sqrt, exp, ln).
52///
53/// Extends `Scalar` with functions available only for floating-point
54/// types (f32, f64).
55pub trait Transcendental: Scalar {
56    /// The constant π (3.14159...).
57    const PI: Self;
58
59    /// Convert to f32.
60    fn to_f32(self) -> f32;
61    /// Create from an f32 value.
62    fn from_f32(value: f32) -> Self;
63
64    /// Convert to f64.
65    fn to_f64(self) -> f64 {
66        self.to_f32() as f64
67    }
68
69    /// Create from an f64 value.
70    fn from_f64(value: f64) -> Self {
71        Self::from_f32(value as f32)
72    }
73
74    /// Compute the square root.
75    fn sqrt(self) -> Self;
76    /// Compute e raised to the power of `self`.
77    fn exp(self) -> Self;
78    /// Compute the natural logarithm.
79    fn ln(self) -> Self;
80    /// Compute the sine (input in radians).
81    fn sin(self) -> Self;
82    /// Compute the cosine (input in radians).
83    fn cos(self) -> Self;
84    /// Compute the tangent (input in radians).
85    fn tan(self) -> Self;
86
87    /// Compute the hyperbolic tangent.
88    fn tanh(self) -> Self {
89        let e2x = (self + self).exp();
90        (e2x - Self::ONE) / (e2x + Self::ONE)
91    }
92
93    /// Return the sign of the value: -1, 0, or 1.
94    fn signum(self) -> Self;
95
96    /// Generate a random value in the range [-1, 1].
97    fn random() -> Self;
98}
99
100// -----------------------------------------------------------------------------
101// Scalar — f32
102// -----------------------------------------------------------------------------
103
104impl Scalar for f32 {
105    const ZERO: f32 = 0.0;
106    const ONE: f32 = 1.0;
107    const MIN: f32 = -1.0;
108    const MAX: f32 = 1.0;
109
110    #[inline(always)]
111    fn abs(self) -> f32 {
112        self.abs()
113    }
114
115    #[inline(always)]
116    fn min(self, other: f32) -> f32 {
117        self.min(other)
118    }
119
120    #[inline(always)]
121    fn max(self, other: f32) -> f32 {
122        self.max(other)
123    }
124
125    #[inline(always)]
126    fn clamp(self, min: f32, max: f32) -> f32 {
127        self.clamp(min, max)
128    }
129
130    #[inline(always)]
131    fn from_usize(n: usize) -> f32 {
132        n as f32
133    }
134}
135
136impl Transcendental for f32 {
137    const PI: f32 = std::f32::consts::PI;
138
139    #[inline(always)]
140    fn to_f32(self) -> f32 {
141        self
142    }
143
144    #[inline(always)]
145    fn from_f32(value: f32) -> f32 {
146        value
147    }
148
149    #[inline(always)]
150    fn from_f64(value: f64) -> f32 {
151        value as f32
152    }
153
154    #[inline(always)]
155    fn sqrt(self) -> f32 {
156        self.sqrt()
157    }
158
159    #[inline(always)]
160    fn exp(self) -> f32 {
161        self.exp()
162    }
163
164    #[inline(always)]
165    fn ln(self) -> f32 {
166        self.ln()
167    }
168
169    #[inline(always)]
170    fn sin(self) -> f32 {
171        self.sin()
172    }
173
174    #[inline(always)]
175    fn cos(self) -> f32 {
176        self.cos()
177    }
178
179    #[inline(always)]
180    fn tan(self) -> f32 {
181        f32::tan(self)
182    }
183
184    #[inline(always)]
185    fn tanh(self) -> f32 {
186        f32::tanh(self)
187    }
188
189    #[inline(always)]
190    fn signum(self) -> f32 {
191        f32::signum(self)
192    }
193
194    #[inline(always)]
195    fn random() -> f32 {
196        rand::random::<f32>() * 2.0 - 1.0
197    }
198}
199
200// -----------------------------------------------------------------------------
201// Scalar + Transcendental — f64
202// -----------------------------------------------------------------------------
203
204impl Scalar for f64 {
205    const ZERO: f64 = 0.0;
206    const ONE: f64 = 1.0;
207    const MIN: f64 = -1.0;
208    const MAX: f64 = 1.0;
209
210    #[inline(always)]
211    fn abs(self) -> f64 {
212        self.abs()
213    }
214
215    #[inline(always)]
216    fn min(self, other: f64) -> f64 {
217        self.min(other)
218    }
219
220    #[inline(always)]
221    fn max(self, other: f64) -> f64 {
222        self.max(other)
223    }
224
225    #[inline(always)]
226    fn clamp(self, min: f64, max: f64) -> f64 {
227        self.clamp(min, max)
228    }
229
230    #[inline(always)]
231    fn from_usize(n: usize) -> f64 {
232        n as f64
233    }
234}
235
236impl Transcendental for f64 {
237    const PI: f64 = std::f64::consts::PI;
238
239    #[inline(always)]
240    fn to_f32(self) -> f32 {
241        self as f32
242    }
243
244    #[inline(always)]
245    fn from_f32(value: f32) -> f64 {
246        value as f64
247    }
248
249    #[inline(always)]
250    fn from_f64(value: f64) -> f64 {
251        value
252    }
253
254    #[inline(always)]
255    fn sqrt(self) -> f64 {
256        self.sqrt()
257    }
258
259    #[inline(always)]
260    fn exp(self) -> f64 {
261        self.exp()
262    }
263
264    #[inline(always)]
265    fn ln(self) -> f64 {
266        self.ln()
267    }
268
269    #[inline(always)]
270    fn sin(self) -> f64 {
271        self.sin()
272    }
273
274    #[inline(always)]
275    fn cos(self) -> f64 {
276        self.cos()
277    }
278
279    #[inline(always)]
280    fn tan(self) -> f64 {
281        self.tan()
282    }
283
284    #[inline(always)]
285    fn tanh(self) -> f64 {
286        self.tanh()
287    }
288
289    #[inline(always)]
290    fn signum(self) -> f64 {
291        self.signum()
292    }
293
294    #[inline(always)]
295    fn random() -> f64 {
296        rand::random::<f64>() * 2.0 - 1.0
297    }
298}
299
300// -----------------------------------------------------------------------------
301// Scalar — integer types
302// -----------------------------------------------------------------------------
303
304macro_rules! impl_scalar_int {
305    ($ty:ty, $zero:expr, $one:expr, $min:expr, $max:expr) => {
306        impl Scalar for $ty {
307            const ZERO: $ty = $zero;
308            const ONE: $ty = $one;
309            const MIN: $ty = $min;
310            const MAX: $ty = $max;
311
312            #[inline(always)]
313            fn abs(self) -> $ty {
314                if self >= 0 {
315                    self
316                } else {
317                    -self
318                }
319            }
320
321            #[inline(always)]
322            fn min(self, other: $ty) -> $ty {
323                core::cmp::Ord::min(self, other)
324            }
325
326            #[inline(always)]
327            fn max(self, other: $ty) -> $ty {
328                core::cmp::Ord::max(self, other)
329            }
330
331            #[inline(always)]
332            fn clamp(self, lo: $ty, hi: $ty) -> $ty {
333                core::cmp::Ord::clamp(self, lo, hi)
334            }
335
336            #[inline(always)]
337            fn from_usize(n: usize) -> $ty {
338                n as $ty
339            }
340        }
341    };
342}
343
344impl_scalar_int!(i8, 0, 1, i8::MIN, i8::MAX);
345impl_scalar_int!(i16, 0, 1, i16::MIN, i16::MAX);
346impl_scalar_int!(i32, 0, 1, i32::MIN, i32::MAX);
347impl_scalar_int!(i64, 0, 1, i64::MIN, i64::MAX);