Skip to main content

stak_vm/
number.rs

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