diff --git a/Cargo.lock b/Cargo.lock index ae15f94..4ebe6f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,13 +2,36 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + [[package]] name = "const-decimal" version = "0.1.0" dependencies = [ + "num-traits", + "paste", "serde", ] +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "proc-macro2" version = "1.0.86" diff --git a/Cargo.toml b/Cargo.toml index 9fdd59a..cb97b29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,9 +16,11 @@ disallowed_methods = 'warn' serde = ["dep:serde"] [dependencies] +num-traits = "0.2.19" serde = { version = "1.0.210", features = ["derive"], optional = true } -[build-dependencies] +[dev-dependencies] +paste = "1.0.15" [profile.release] opt-level = 3 diff --git a/src/const_traits.rs b/src/const_traits.rs new file mode 100644 index 0000000..7511a06 --- /dev/null +++ b/src/const_traits.rs @@ -0,0 +1,19 @@ +pub trait PrecisionFactor { + const PRECISION_FACTOR: Self; +} + +impl PrecisionFactor for u64 { + const PRECISION_FACTOR: Self = 10u64.pow(D as u32); +} + +impl PrecisionFactor for i64 { + const PRECISION_FACTOR: Self = 10i64.pow(D as u32); +} + +impl PrecisionFactor for u128 { + const PRECISION_FACTOR: Self = 10u128.pow(D as u32); +} + +impl PrecisionFactor for i128 { + const PRECISION_FACTOR: Self = 10i128.pow(D as u32); +} diff --git a/src/decimal.rs b/src/decimal.rs new file mode 100644 index 0000000..137e23f --- /dev/null +++ b/src/decimal.rs @@ -0,0 +1,137 @@ +use std::ops::{Add, Div, Mul, Sub}; + +use num_traits::{ConstOne, One}; + +use crate::const_traits::PrecisionFactor; + +pub type Uint64 = Decimal; +pub type Uint128 = Decimal; +pub type Int64 = Decimal; +pub type Int128 = Decimal; + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Decimal(pub I); + +pub trait Integer: + PrecisionFactor + + ConstOne + + One + + Add + + Sub + + Mul + + Div + + Clone + + Copy +{ +} + +impl Integer for I where + I: PrecisionFactor + + ConstOne + + One + + Add + + Sub + + Mul + + Div + + Clone + + Copy +{ +} + +impl Decimal +where + I: Integer, +{ + pub const ONE: Decimal = Decimal(I::PRECISION_FACTOR); + pub const PRECISION_FACTOR: I = I::PRECISION_FACTOR; +} + +impl Add for Decimal +where + I: Integer, +{ + type Output = Self; + + #[inline] + fn add(self, rhs: Self) -> Self::Output { + Decimal(self.0 + rhs.0) + } +} + +impl Sub for Decimal +where + I: Integer, +{ + type Output = Self; + + #[inline] + fn sub(self, rhs: Self) -> Self::Output { + Decimal(self.0 - rhs.0) + } +} + +impl Mul for Decimal +where + I: Integer, +{ + type Output = Self; + + #[inline] + fn mul(self, rhs: Self) -> Self::Output { + Decimal(self.0 * rhs.0 / I::PRECISION_FACTOR) + } +} + +impl Div for Decimal +where + I: Integer, +{ + type Output = Self; + + #[inline] + fn div(self, rhs: Self) -> Self::Output { + Decimal(self.0 * I::PRECISION_FACTOR / rhs.0) + } +} + +#[cfg(test)] +mod tests { + use paste::paste; + + use super::*; + + macro_rules! test_basic_ops { + ($variant:ty) => { + paste! { + #[test] + fn [<$variant:lower _add>]() { + assert_eq!( + $variant::ONE + $variant::ONE, + Decimal($variant::PRECISION_FACTOR * 2), + ); + } + + #[test] + fn [<$variant:lower _sub>]() { + assert_eq!($variant::ONE - $variant::ONE, Decimal(0)); + } + + #[test] + fn [<$variant:lower _mul>]() { + assert_eq!($variant::ONE * $variant::ONE, $variant::ONE); + } + + #[test] + fn [<$variant:lower _div>]() { + assert_eq!($variant::ONE / $variant::ONE, $variant::ONE); + } + } + }; + } + + test_basic_ops!(Uint64); + test_basic_ops!(Uint128); + test_basic_ops!(Int64); + test_basic_ops!(Int128); +} diff --git a/src/lib.rs b/src/lib.rs index ae9cd0d..e225daf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,72 +1,4 @@ -use std::ops::{Add, Div, Mul, Sub}; +mod const_traits; +mod decimal; -pub const PRECISION_FACTOR: u64 = 10u64.pow(9); - -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Decimal64(pub u64); - -impl Decimal64 { - pub const ONE: Decimal64 = Decimal64(PRECISION_FACTOR); -} - -impl Add for Decimal64 { - type Output = Self; - - #[inline] - fn add(self, rhs: Self) -> Self::Output { - Decimal64(self.0 + rhs.0) - } -} - -impl Sub for Decimal64 { - type Output = Self; - - #[inline] - fn sub(self, rhs: Self) -> Self::Output { - Decimal64(self.0 - rhs.0) - } -} - -impl Mul for Decimal64 { - type Output = Self; - - #[inline] - fn mul(self, rhs: Self) -> Self::Output { - Decimal64(self.0 * rhs.0 / PRECISION_FACTOR) - } -} - -impl Div for Decimal64 { - type Output = Self; - - #[inline] - fn div(self, rhs: Self) -> Self::Output { - Decimal64(self.0 * PRECISION_FACTOR / rhs.0) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn add() { - assert_eq!(Decimal64::ONE + Decimal64::ONE, Decimal64(PRECISION_FACTOR * 2)); - } - - #[test] - fn sub() { - assert_eq!(Decimal64::ONE - Decimal64::ONE, Decimal64(0)); - } - - #[test] - fn mul() { - assert_eq!(Decimal64::ONE * Decimal64::ONE, Decimal64::ONE); - } - - #[test] - fn div() { - assert_eq!(Decimal64::ONE / Decimal64::ONE, Decimal64::ONE); - } -} +pub use decimal::*;