typst_utils/
scalar.rs

1use std::cmp::Ordering;
2use std::fmt::{self, Debug, Formatter};
3use std::hash::{Hash, Hasher};
4use std::iter::Sum;
5use std::ops::{
6    Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign,
7};
8
9use crate::Numeric;
10
11/// A 64-bit float that implements `Eq`, `Ord` and `Hash`.
12///
13/// Panics if it's `NaN` during any of those operations.
14#[derive(Default, Copy, Clone)]
15pub struct Scalar(f64);
16
17impl Scalar {
18    /// The scalar containing `0.0`.
19    pub const ZERO: Self = Self(0.0);
20
21    /// The scalar containing `1.0`.
22    pub const ONE: Self = Self(1.0);
23
24    /// The scalar containing `f64::INFINITY`.
25    pub const INFINITY: Self = Self(f64::INFINITY);
26
27    /// Creates a [`Scalar`] with the given value.
28    ///
29    /// If the value is NaN, then it is set to `0.0` in the result.
30    pub const fn new(x: f64) -> Self {
31        Self(if is_nan(x) { 0.0 } else { x })
32    }
33
34    /// Gets the value of this [`Scalar`].
35    pub const fn get(self) -> f64 {
36        self.0
37    }
38}
39
40// We have to detect NaNs this way since `f64::is_nan` isn’t const
41// on stable yet:
42// ([tracking issue](https://github.com/rust-lang/rust/issues/57241))
43#[allow(clippy::unusual_byte_groupings)]
44const fn is_nan(x: f64) -> bool {
45    // Safety: all bit patterns are valid for u64, and f64 has no padding bits.
46    // We cannot use `f64::to_bits` because it is not const.
47    let x_bits = unsafe { std::mem::transmute::<f64, u64>(x) };
48    (x_bits << 1 >> (64 - 12 + 1)) == 0b0_111_1111_1111 && (x_bits << 12) != 0
49}
50
51impl Numeric for Scalar {
52    fn zero() -> Self {
53        Self(0.0)
54    }
55
56    fn is_finite(self) -> bool {
57        self.0.is_finite()
58    }
59}
60
61impl Debug for Scalar {
62    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
63        self.0.fmt(f)
64    }
65}
66
67impl Eq for Scalar {}
68
69impl PartialEq for Scalar {
70    fn eq(&self, other: &Self) -> bool {
71        assert!(!self.0.is_nan() && !other.0.is_nan(), "float is NaN");
72        self.0 == other.0
73    }
74}
75
76impl PartialEq<f64> for Scalar {
77    fn eq(&self, other: &f64) -> bool {
78        self == &Self(*other)
79    }
80}
81
82impl Ord for Scalar {
83    fn cmp(&self, other: &Self) -> Ordering {
84        self.0.partial_cmp(&other.0).expect("float is NaN")
85    }
86}
87
88impl PartialOrd for Scalar {
89    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
90        Some(self.cmp(other))
91    }
92}
93
94impl Hash for Scalar {
95    fn hash<H: Hasher>(&self, state: &mut H) {
96        debug_assert!(!self.0.is_nan(), "float is NaN");
97        self.0.to_bits().hash(state);
98    }
99}
100
101impl From<f64> for Scalar {
102    fn from(float: f64) -> Self {
103        Self::new(float)
104    }
105}
106
107impl From<Scalar> for f64 {
108    fn from(scalar: Scalar) -> Self {
109        scalar.0
110    }
111}
112
113impl Neg for Scalar {
114    type Output = Self;
115
116    fn neg(self) -> Self::Output {
117        Self::new(-self.0)
118    }
119}
120
121impl<T: Into<Self>> Add<T> for Scalar {
122    type Output = Self;
123
124    fn add(self, rhs: T) -> Self::Output {
125        Self::new(self.0 + rhs.into().0)
126    }
127}
128
129impl<T: Into<Self>> AddAssign<T> for Scalar {
130    fn add_assign(&mut self, rhs: T) {
131        *self = *self + rhs.into();
132    }
133}
134
135impl<T: Into<Self>> Sub<T> for Scalar {
136    type Output = Self;
137
138    fn sub(self, rhs: T) -> Self::Output {
139        Self::new(self.0 - rhs.into().0)
140    }
141}
142
143impl<T: Into<Self>> SubAssign<T> for Scalar {
144    fn sub_assign(&mut self, rhs: T) {
145        *self = *self - rhs.into();
146    }
147}
148
149impl<T: Into<Self>> Mul<T> for Scalar {
150    type Output = Self;
151
152    fn mul(self, rhs: T) -> Self::Output {
153        Self::new(self.0 * rhs.into().0)
154    }
155}
156
157impl<T: Into<Self>> MulAssign<T> for Scalar {
158    fn mul_assign(&mut self, rhs: T) {
159        *self = *self * rhs.into();
160    }
161}
162
163impl<T: Into<Self>> Div<T> for Scalar {
164    type Output = Self;
165
166    fn div(self, rhs: T) -> Self::Output {
167        Self::new(self.0 / rhs.into().0)
168    }
169}
170
171impl<T: Into<Self>> DivAssign<T> for Scalar {
172    fn div_assign(&mut self, rhs: T) {
173        *self = *self / rhs.into();
174    }
175}
176
177impl<T: Into<Self>> Rem<T> for Scalar {
178    type Output = Self;
179
180    fn rem(self, rhs: T) -> Self::Output {
181        Self::new(self.0 % rhs.into().0)
182    }
183}
184
185impl<T: Into<Self>> RemAssign<T> for Scalar {
186    fn rem_assign(&mut self, rhs: T) {
187        *self = *self % rhs.into();
188    }
189}
190
191impl Sum for Scalar {
192    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
193        Self::new(iter.map(|s| s.0).sum())
194    }
195}
196
197impl<'a> Sum<&'a Self> for Scalar {
198    fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
199        Self::new(iter.map(|s| s.0).sum())
200    }
201}