stak_vm/
number.rs

1use crate::{Error, value::Value};
2use cfg_elif::{expr::feature, item};
3use core::{
4    fmt::{self, Display, Formatter},
5    ops::{Add, Div, Mul, Rem, Sub},
6};
7
8item::feature!(if ("float") {
9    /// A number representation.
10    pub type NumberRepresentation = f64;
11} else {
12    /// A number representation.
13    pub type NumberRepresentation = i64;
14});
15
16/// A number.
17///
18/// It represents a signed 63-bit integer by default. If the `float` feature is
19/// enabled, it represents a 64-bit floating-point number.
20#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
21#[cfg_attr(not(feature = "float"), derive(Eq, Ord))]
22pub struct Number(NumberRepresentation);
23
24impl Number {
25    /// Creates a number.
26    #[inline]
27    pub const fn new(number: NumberRepresentation) -> Self {
28        Self(feature!(if ("float") { number } else { (number << 1) | 1 }))
29    }
30
31    /// Converts a number to a number representation.
32    #[inline]
33    pub const fn to_representation(self) -> NumberRepresentation {
34        feature!(if ("float") { self.0 } else { self.0 >> 1 })
35    }
36
37    /// Converts `i64` into a number.
38    #[inline]
39    pub const fn from_i64(number: i64) -> Self {
40        Self::new(number as _)
41    }
42
43    /// Converts a number to `i64`.
44    #[inline]
45    pub const fn to_i64(self) -> i64 {
46        self.to_representation() as _
47    }
48
49    /// Converts `f64` to a number.
50    #[inline]
51    pub const fn from_f64(number: f64) -> Self {
52        Self::new(number as _)
53    }
54
55    /// Converts a number to `f64`.
56    #[inline]
57    pub const fn to_f64(self) -> f64 {
58        self.to_representation() as _
59    }
60
61    #[inline]
62    pub(crate) const fn from_raw(raw: u64) -> Self {
63        Self(feature!(if ("float") {
64            f64::from_bits(raw)
65        } else {
66            raw as _
67        }))
68    }
69
70    #[inline]
71    pub(crate) const fn to_raw(self) -> u64 {
72        feature!(if ("float") {
73            self.0.to_bits()
74        } else {
75            self.0 as _
76        })
77    }
78}
79
80impl Default for Number {
81    #[inline]
82    fn default() -> Self {
83        Self::new(0 as _)
84    }
85}
86
87impl Add for Number {
88    type Output = Self;
89
90    #[inline]
91    fn add(self, other: Self) -> Self::Output {
92        Self::new(self.to_representation() + other.to_representation())
93    }
94}
95
96impl Sub for Number {
97    type Output = Self;
98
99    #[inline]
100    fn sub(self, other: Self) -> Self::Output {
101        Self::new(self.to_representation() - other.to_representation())
102    }
103}
104
105impl Mul for Number {
106    type Output = Self;
107
108    #[inline]
109    fn mul(self, other: Self) -> Self::Output {
110        Self::new(self.to_representation() * other.to_representation())
111    }
112}
113
114impl Div for Number {
115    type Output = Self;
116
117    #[inline]
118    fn div(self, other: Self) -> Self::Output {
119        Self::new(self.to_representation() / other.to_representation())
120    }
121}
122
123impl Rem for Number {
124    type Output = Self;
125
126    #[inline]
127    fn rem(self, other: Self) -> Self::Output {
128        Self::new(self.to_representation() % other.to_representation())
129    }
130}
131
132impl TryFrom<Value> for Number {
133    type Error = Error;
134
135    #[inline]
136    fn try_from(value: Value) -> Result<Self, Self::Error> {
137        value.to_number().ok_or(Error::NumberExpected)
138    }
139}
140
141impl Display for Number {
142    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
143        write!(formatter, "n{}", self.to_representation())
144    }
145}
146
147#[cfg(test)]
148mod tests {
149    use super::*;
150    use alloc::format;
151
152    #[test]
153    fn default() {
154        assert_eq!(Number::default(), Number::from_i64(0));
155    }
156
157    #[test]
158    fn to_i64() {
159        assert_eq!(Number::default().to_i64(), 0);
160        assert_eq!(Number::from_i64(42).to_i64(), 42);
161        assert_eq!(Number::from_i64(-1).to_i64(), -1);
162    }
163
164    #[test]
165    fn format() {
166        assert_eq!(format!("{}", Number::from_i64(42)), "n42");
167        assert_eq!(format!("{}", Number::from_i64(-1)), "n-1");
168    }
169
170    #[test]
171    fn add() {
172        assert_eq!(Number::default() + Number::from_i64(1), Number::from_i64(1));
173        assert_eq!(
174            Number::from_i64(1) + Number::from_i64(2),
175            Number::from_i64(3)
176        );
177    }
178
179    #[test]
180    fn subtract() {
181        assert_eq!(
182            Number::default() - Number::from_i64(1),
183            Number::from_i64(-1)
184        );
185        assert_eq!(Number::from_i64(1) - Number::default(), Number::from_i64(1));
186        assert_eq!(
187            Number::from_i64(3) - Number::from_i64(1),
188            Number::from_i64(2)
189        );
190    }
191
192    #[test]
193    fn multiply() {
194        assert_eq!(Number::default() * Number::from_i64(1), Number::default());
195        assert_eq!(
196            Number::from_i64(1) * Number::from_i64(2),
197            Number::from_i64(2)
198        );
199        assert_eq!(
200            Number::from_i64(2) * Number::from_i64(3),
201            Number::from_i64(6)
202        );
203    }
204
205    #[test]
206    fn divide() {
207        assert_eq!(Number::default() / Number::from_i64(1), Number::default());
208        assert_eq!(
209            Number::from_i64(2) / Number::from_i64(1),
210            Number::from_i64(2)
211        );
212        assert_eq!(
213            Number::from_i64(6) / Number::from_i64(2),
214            Number::from_i64(3)
215        );
216    }
217
218    #[test]
219    fn remainder() {
220        assert_eq!(Number::default() % Number::from_i64(1), Number::default());
221        assert_eq!(
222            Number::from_i64(3) % Number::from_i64(2),
223            Number::from_i64(1)
224        );
225    }
226}