fj_math/
scalar.rs

1use std::{
2    cmp,
3    f64::consts::{PI, TAU},
4    fmt,
5    hash::Hash,
6    ops,
7};
8
9use decorum::R64;
10
11/// A rational, finite scalar value
12///
13/// This is a wrapper around `f64`. On construction, it checks that the `f64`
14/// value is not NaN. This allows `Scalar` to provide implementations of [`Eq`],
15/// [`Ord`], and [`Hash`], enabling `Scalar` (and types built on top of it), to
16/// be used as keys in hash maps, hash sets, and similar types.
17#[derive(Clone, Copy, Default)]
18#[repr(C)]
19pub struct Scalar(f64);
20
21impl Scalar {
22    /// The `Scalar` instance that represents zero
23    pub const ZERO: Self = Self(0.);
24
25    /// The `Scalar` instance that represents one
26    pub const ONE: Self = Self(1.);
27
28    /// The `Scalar` instance that represents two
29    pub const TWO: Self = Self(2.);
30
31    /// The largest `Scalar` value
32    pub const MAX: Self = Self(f64::MAX);
33
34    /// The `Scalar` instance that represents pi
35    pub const PI: Self = Self(PI);
36
37    /// The `Scalar` instance that represents tau
38    pub const TAU: Self = Self(TAU);
39
40    /// Construct a `Scalar` from an `f64`
41    ///
42    /// # Panics
43    ///
44    /// Panics, if `scalar` is NaN.
45    pub fn from_f64(scalar: f64) -> Self {
46        if scalar.is_nan() {
47            panic!("Invalid scalar value: {scalar}");
48        } else {
49            Self(scalar)
50        }
51    }
52
53    /// Construct a `Scalar` from a `u64`
54    pub fn from_u64(scalar: u64) -> Self {
55        Self::from_f64(scalar as f64)
56    }
57
58    /// Convert the scalar into an `f32`
59    pub fn into_f32(self) -> f32 {
60        self.0 as f32
61    }
62
63    /// Convert the scalar into an `f64`
64    pub fn into_f64(self) -> f64 {
65        self.0
66    }
67
68    /// Convert the scalar into a `u64`
69    pub fn into_u64(self) -> u64 {
70        self.0 as u64
71    }
72
73    /// Indicate whether the scalar is negative
74    pub fn is_negative(self) -> bool {
75        self < Self::ZERO
76    }
77
78    /// Indicate whether the scalar is positive
79    pub fn is_positive(self) -> bool {
80        self > Self::ZERO
81    }
82
83    /// Indicate whether the scalar is zero
84    pub fn is_zero(self) -> bool {
85        self == Self::ZERO
86    }
87
88    /// The sign of the scalar
89    ///
90    /// Return `Scalar::ZERO`, if the scalar is zero, `Scalar::ONE`, if it is
91    /// positive, `-Scalar::ONE`, if it is negative.
92    pub fn sign(self) -> Sign {
93        if self.is_negative() {
94            return Sign::Negative;
95        }
96        if self.is_positive() {
97            return Sign::Positive;
98        }
99        if self.is_zero() {
100            return Sign::Zero;
101        }
102
103        unreachable!("Sign is neither negative, nor positive, nor zero.")
104    }
105
106    /// Compute the absolute value of the scalar
107    pub fn abs(self) -> Self {
108        self.0.abs().into()
109    }
110
111    /// Compute the maximum of this and another scalar
112    pub fn max(self, other: impl Into<Self>) -> Self {
113        self.0.max(other.into().0).into()
114    }
115
116    /// Compute the largest integer smaller than or equal to this scalar
117    pub fn floor(self) -> Self {
118        self.0.floor().into()
119    }
120
121    /// Compute the smallest integer larger than or equal to this scalar
122    pub fn ceil(self) -> Self {
123        self.0.ceil().into()
124    }
125
126    /// Round the scalar
127    pub fn round(self) -> Self {
128        self.0.round().into()
129    }
130
131    /// Compute the cosine
132    pub fn cos(self) -> Self {
133        self.0.cos().into()
134    }
135
136    /// Compute sine and cosine
137    pub fn sin_cos(self) -> (Self, Self) {
138        let (sin, cos) = self.0.sin_cos();
139        (sin.into(), cos.into())
140    }
141
142    /// Compute the arccosine
143    pub fn acos(self) -> Self {
144        self.0.acos().into()
145    }
146
147    /// Compute the four-quadrant arctangent
148    pub fn atan2(self, other: Self) -> Self {
149        self.0.atan2(other.0).into()
150    }
151}
152
153impl PartialEq for Scalar {
154    fn eq(&self, other: &Self) -> bool {
155        self.0 == other.0
156    }
157}
158
159impl Eq for Scalar {}
160
161impl PartialOrd for Scalar {
162    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
163        Some(self.cmp(other))
164    }
165}
166
167impl Ord for Scalar {
168    fn cmp(&self, other: &Self) -> cmp::Ordering {
169        // Should never panic, as `from_f64` checks that the wrapped value is
170        // finite.
171        self.0.partial_cmp(&other.0).expect("Invalid `Scalar`")
172    }
173}
174
175impl Hash for Scalar {
176    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
177        // To the best of my knowledge, this matches the `PartialEq`
178        // implementation.
179        R64::from_inner(self.0).hash(state);
180    }
181}
182
183impl From<f32> for Scalar {
184    fn from(scalar: f32) -> Self {
185        Self::from_f64(scalar.into())
186    }
187}
188
189impl From<f64> for Scalar {
190    fn from(scalar: f64) -> Self {
191        Self::from_f64(scalar)
192    }
193}
194
195impl From<Scalar> for f64 {
196    fn from(scalar: Scalar) -> Self {
197        scalar.into_f64()
198    }
199}
200
201impl ops::Neg for Scalar {
202    type Output = Self;
203
204    fn neg(self) -> Self::Output {
205        self.0.neg().into()
206    }
207}
208
209impl<T: Into<Self>> ops::Add<T> for Scalar {
210    type Output = Self;
211
212    fn add(self, rhs: T) -> Self::Output {
213        self.0.add(rhs.into().0).into()
214    }
215}
216
217impl<T: Into<Self>> ops::Sub<T> for Scalar {
218    type Output = Self;
219
220    fn sub(self, rhs: T) -> Self::Output {
221        self.0.sub(rhs.into().0).into()
222    }
223}
224
225impl<T: Into<Self>> ops::Mul<T> for Scalar {
226    type Output = Self;
227
228    fn mul(self, rhs: T) -> Self::Output {
229        self.0.mul(rhs.into().0).into()
230    }
231}
232
233impl<T: Into<Self>> ops::Div<T> for Scalar {
234    type Output = Self;
235
236    fn div(self, rhs: T) -> Self::Output {
237        self.0.div(rhs.into().0).into()
238    }
239}
240
241impl<T: Into<Self>> ops::Rem<T> for Scalar {
242    type Output = Self;
243
244    fn rem(self, rhs: T) -> Self::Output {
245        self.0.rem(rhs.into().0).into()
246    }
247}
248
249impl<T: Into<Self>> ops::AddAssign<T> for Scalar {
250    fn add_assign(&mut self, rhs: T) {
251        self.0.add_assign(rhs.into().0);
252        *self = self.0.into();
253    }
254}
255
256impl<T: Into<Self>> ops::SubAssign<T> for Scalar {
257    fn sub_assign(&mut self, rhs: T) {
258        self.0.sub_assign(rhs.into().0);
259        *self = self.0.into();
260    }
261}
262
263impl<T: Into<Self>> ops::MulAssign<T> for Scalar {
264    fn mul_assign(&mut self, rhs: T) {
265        self.0.mul_assign(rhs.into().0);
266        *self = self.0.into();
267    }
268}
269
270impl<T: Into<Self>> ops::DivAssign<T> for Scalar {
271    fn div_assign(&mut self, rhs: T) {
272        self.0.div_assign(rhs.into().0);
273        *self = self.0.into();
274    }
275}
276
277impl<T: Into<Self>> ops::RemAssign<T> for Scalar {
278    fn rem_assign(&mut self, rhs: T) {
279        self.0.rem_assign(rhs.into().0);
280        *self = self.0.into();
281    }
282}
283
284impl num_traits::Zero for Scalar {
285    fn zero() -> Self {
286        Self::ZERO
287    }
288
289    fn is_zero(&self) -> bool {
290        self.0.is_zero()
291    }
292}
293
294impl num_traits::One for Scalar {
295    fn one() -> Self {
296        Self::ONE
297    }
298}
299
300impl num_traits::Num for Scalar {
301    type FromStrRadixErr = <f64 as num_traits::Num>::FromStrRadixErr;
302
303    fn from_str_radix(
304        str: &str,
305        radix: u32,
306    ) -> Result<Self, Self::FromStrRadixErr> {
307        f64::from_str_radix(str, radix).map(Self::from_f64)
308    }
309}
310
311impl num_traits::NumCast for Scalar {
312    fn from<T: num_traits::ToPrimitive>(n: T) -> Option<Self> {
313        Some(Self::from_f64(<f64 as num_traits::NumCast>::from(n)?))
314    }
315}
316
317impl num_traits::Signed for Scalar {
318    fn abs(&self) -> Self {
319        self.0.abs().into()
320    }
321
322    fn abs_sub(&self, other: &Self) -> Self {
323        <f64 as num_traits::Signed>::abs_sub(&self.0, &other.0).into()
324    }
325
326    fn signum(&self) -> Self {
327        <f64 as num_traits::Signed>::signum(&self.0).into()
328    }
329
330    fn is_positive(&self) -> bool {
331        <f64 as num_traits::Signed>::is_positive(&self.0)
332    }
333
334    fn is_negative(&self) -> bool {
335        <f64 as num_traits::Signed>::is_negative(&self.0)
336    }
337}
338
339impl num_traits::ToPrimitive for Scalar {
340    fn to_i64(&self) -> Option<i64> {
341        self.0.to_i64()
342    }
343
344    fn to_u64(&self) -> Option<u64> {
345        self.0.to_u64()
346    }
347}
348
349impl num_traits::Float for Scalar {
350    fn nan() -> Self {
351        panic!("`Scalar` can not represent NaN")
352    }
353
354    fn infinity() -> Self {
355        Self::from_f64(f64::infinity())
356    }
357
358    fn neg_infinity() -> Self {
359        Self::from_f64(f64::neg_infinity())
360    }
361
362    fn neg_zero() -> Self {
363        Self::from_f64(f64::neg_zero())
364    }
365
366    fn min_value() -> Self {
367        Self::from_f64(f64::min_value())
368    }
369
370    fn min_positive_value() -> Self {
371        Self::from_f64(f64::min_positive_value())
372    }
373
374    fn max_value() -> Self {
375        Self::from_f64(f64::max_value())
376    }
377
378    fn is_nan(self) -> bool {
379        self.0.is_nan()
380    }
381
382    fn is_infinite(self) -> bool {
383        self.0.is_infinite()
384    }
385
386    fn is_finite(self) -> bool {
387        self.0.is_finite()
388    }
389
390    fn is_normal(self) -> bool {
391        self.0.is_normal()
392    }
393
394    fn classify(self) -> std::num::FpCategory {
395        self.0.classify()
396    }
397
398    fn floor(self) -> Self {
399        Self::from_f64(self.0.floor())
400    }
401
402    fn ceil(self) -> Self {
403        Self::from_f64(self.0.ceil())
404    }
405
406    fn round(self) -> Self {
407        Self::from_f64(self.0.round())
408    }
409
410    fn trunc(self) -> Self {
411        Self::from_f64(self.0.trunc())
412    }
413
414    fn fract(self) -> Self {
415        Self::from_f64(self.0.fract())
416    }
417
418    fn abs(self) -> Self {
419        Self::from_f64(self.0.abs())
420    }
421
422    fn signum(self) -> Self {
423        Self::from_f64(self.0.signum())
424    }
425
426    fn is_sign_positive(self) -> bool {
427        self.0.is_sign_positive()
428    }
429
430    fn is_sign_negative(self) -> bool {
431        self.0.is_sign_negative()
432    }
433
434    fn mul_add(self, a: Self, b: Self) -> Self {
435        Self::from_f64(self.0.mul_add(a.0, b.0))
436    }
437
438    fn recip(self) -> Self {
439        Self::from_f64(self.0.recip())
440    }
441
442    fn powi(self, n: i32) -> Self {
443        Self::from_f64(self.0.powi(n))
444    }
445
446    fn powf(self, n: Self) -> Self {
447        Self::from_f64(self.0.powf(n.0))
448    }
449
450    fn sqrt(self) -> Self {
451        Self::from_f64(self.0.sqrt())
452    }
453
454    fn exp(self) -> Self {
455        Self::from_f64(self.0.exp())
456    }
457
458    fn exp2(self) -> Self {
459        Self::from_f64(self.0.exp2())
460    }
461
462    fn ln(self) -> Self {
463        Self::from_f64(self.0.ln())
464    }
465
466    fn log(self, base: Self) -> Self {
467        Self::from_f64(self.0.log(base.0))
468    }
469
470    fn log2(self) -> Self {
471        Self::from_f64(self.0.log2())
472    }
473
474    fn log10(self) -> Self {
475        Self::from_f64(self.0.log10())
476    }
477
478    fn max(self, other: Self) -> Self {
479        Self::from_f64(self.0.max(other.0))
480    }
481
482    fn min(self, other: Self) -> Self {
483        Self::from_f64(self.0.min(other.0))
484    }
485
486    fn abs_sub(self, other: Self) -> Self {
487        (self - other).abs()
488    }
489
490    fn cbrt(self) -> Self {
491        Self::from_f64(self.0.cbrt())
492    }
493
494    fn hypot(self, other: Self) -> Self {
495        Self::from_f64(self.0.hypot(other.0))
496    }
497
498    fn sin(self) -> Self {
499        Self::from_f64(self.0.sin())
500    }
501
502    fn cos(self) -> Self {
503        Self::from_f64(self.0.cos())
504    }
505
506    fn tan(self) -> Self {
507        Self::from_f64(self.0.tan())
508    }
509
510    fn asin(self) -> Self {
511        Self::from_f64(self.0.asin())
512    }
513
514    fn acos(self) -> Self {
515        Self::from_f64(self.0.acos())
516    }
517
518    fn atan(self) -> Self {
519        Self::from_f64(self.0.atan())
520    }
521
522    fn atan2(self, other: Self) -> Self {
523        Self::from_f64(self.0.atan2(other.0))
524    }
525
526    fn sin_cos(self) -> (Self, Self) {
527        let (sin, cos) = self.0.sin_cos();
528        (Self::from_f64(sin), Self::from_f64(cos))
529    }
530
531    fn exp_m1(self) -> Self {
532        Self::from_f64(self.0.exp_m1())
533    }
534
535    fn ln_1p(self) -> Self {
536        Self::from_f64(self.0.ln_1p())
537    }
538
539    fn sinh(self) -> Self {
540        Self::from_f64(self.0.sinh())
541    }
542
543    fn cosh(self) -> Self {
544        Self::from_f64(self.0.cosh())
545    }
546
547    fn tanh(self) -> Self {
548        Self::from_f64(self.0.tanh())
549    }
550
551    fn asinh(self) -> Self {
552        Self::from_f64(self.0.asinh())
553    }
554
555    fn acosh(self) -> Self {
556        Self::from_f64(self.0.acosh())
557    }
558
559    fn atanh(self) -> Self {
560        Self::from_f64(self.0.atanh())
561    }
562
563    fn integer_decode(self) -> (u64, i16, i8) {
564        self.0.integer_decode()
565    }
566}
567
568impl fmt::Debug for Scalar {
569    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
570        self.0.fmt(f)
571    }
572}
573
574impl fmt::Display for Scalar {
575    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
576        self.0.fmt(f)
577    }
578}
579
580impl approx::AbsDiffEq for Scalar {
581    type Epsilon = Self;
582
583    fn default_epsilon() -> Self::Epsilon {
584        f64::default_epsilon().into()
585    }
586
587    fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
588        self.0.abs_diff_eq(&other.0, epsilon.0)
589    }
590}
591
592/// The sign of a [`Scalar`]
593///
594/// See [`Scalar::sign`]
595#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)]
596pub enum Sign {
597    /// The scalar is negative
598    Negative,
599
600    /// The scalar is positive
601    Positive,
602
603    /// The scalar is zero
604    Zero,
605}
606
607impl Sign {
608    /// Convert this sign back to a scalar
609    pub fn to_scalar(self) -> Scalar {
610        match self {
611            Self::Negative => -Scalar::ONE,
612            Self::Positive => Scalar::ONE,
613            Self::Zero => Scalar::ZERO,
614        }
615    }
616}