use crate::{value::Value, Error};
use cfg_exif::feature;
use core::{
fmt::{self, Display, Formatter},
ops::{Add, Div, Mul, Rem, Sub},
};
#[cfg(feature = "float")]
pub type NumberRepresentation = f64;
#[cfg(not(feature = "float"))]
pub type NumberRepresentation = i64;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
#[cfg_attr(not(feature = "float"), derive(Eq, Ord))]
pub struct Number(NumberRepresentation);
impl Number {
pub const fn new(number: NumberRepresentation) -> Self {
Self(feature!(if ("float") { number } else { number << 1 | 1 }))
}
pub const fn to_representation(self) -> NumberRepresentation {
feature!(if ("float") { self.0 } else { self.0 >> 1 })
}
pub const fn from_i64(number: i64) -> Self {
Self::new(number as _)
}
pub const fn to_i64(self) -> i64 {
self.to_representation() as _
}
pub const fn from_f64(number: f64) -> Self {
Self::new(number as _)
}
pub const fn to_f64(self) -> f64 {
self.to_representation() as _
}
pub(crate) const fn from_raw(raw: u64) -> Self {
Self(feature!(if ("float") {
f64::from_bits(raw)
} else {
raw as _
}))
}
pub(crate) const fn to_raw(self) -> u64 {
feature!(if ("float") {
self.0.to_bits()
} else {
self.0 as _
})
}
}
impl Default for Number {
fn default() -> Self {
Self::new(0 as _)
}
}
impl Add for Number {
type Output = Self;
fn add(self, other: Self) -> Self::Output {
Self::new(self.to_representation() + other.to_representation())
}
}
impl Sub for Number {
type Output = Self;
fn sub(self, other: Self) -> Self::Output {
Self::new(self.to_representation() - other.to_representation())
}
}
impl Mul for Number {
type Output = Self;
fn mul(self, other: Self) -> Self::Output {
Self::new(self.to_representation() * other.to_representation())
}
}
impl Div for Number {
type Output = Self;
fn div(self, other: Self) -> Self::Output {
Self::new(self.to_representation() / other.to_representation())
}
}
impl Rem for Number {
type Output = Self;
fn rem(self, other: Self) -> Self::Output {
Self::new(self.to_representation() % other.to_representation())
}
}
impl TryFrom<Value> for Number {
type Error = Error;
fn try_from(value: Value) -> Result<Self, Self::Error> {
value.to_number().ok_or(Error::NumberExpected)
}
}
impl Display for Number {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
write!(formatter, "n{}", self.to_representation())
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::format;
#[test]
fn default() {
assert_eq!(Number::default(), Number::from_i64(0));
}
#[test]
fn to_i64() {
assert_eq!(Number::default().to_i64(), 0);
assert_eq!(Number::from_i64(42).to_i64(), 42);
assert_eq!(Number::from_i64(-1).to_i64(), -1);
}
#[test]
fn format() {
assert_eq!(format!("{}", Number::from_i64(42)), "n42");
assert_eq!(format!("{}", Number::from_i64(-1)), "n-1");
}
#[test]
fn add() {
assert_eq!(Number::default() + Number::from_i64(1), Number::from_i64(1));
assert_eq!(
Number::from_i64(1) + Number::from_i64(2),
Number::from_i64(3)
);
}
#[test]
fn subtract() {
assert_eq!(
Number::default() - Number::from_i64(1),
Number::from_i64(-1)
);
assert_eq!(Number::from_i64(1) - Number::default(), Number::from_i64(1));
assert_eq!(
Number::from_i64(3) - Number::from_i64(1),
Number::from_i64(2)
);
}
#[test]
fn multiply() {
assert_eq!(Number::default() * Number::from_i64(1), Number::default());
assert_eq!(
Number::from_i64(1) * Number::from_i64(2),
Number::from_i64(2)
);
assert_eq!(
Number::from_i64(2) * Number::from_i64(3),
Number::from_i64(6)
);
}
#[test]
fn divide() {
assert_eq!(Number::default() / Number::from_i64(1), Number::default());
assert_eq!(
Number::from_i64(2) / Number::from_i64(1),
Number::from_i64(2)
);
assert_eq!(
Number::from_i64(6) / Number::from_i64(2),
Number::from_i64(3)
);
}
#[test]
fn remainder() {
assert_eq!(Number::default() % Number::from_i64(1), Number::default());
assert_eq!(
Number::from_i64(3) % Number::from_i64(2),
Number::from_i64(1)
);
}
}