use super::consts::*;
use super::signed::SignedNumeric;
use super::InnerUint;
use core::convert::*;
use core::ops::{Add, Sub, Mul, Div};
#[cfg(not(feature = "std"))]
use alloc::{format, string::String};
#[derive(Clone, Debug, PartialEq)]
pub struct UnsignedNumeric {
pub value: InnerUint,
}
impl UnsignedNumeric {
pub fn zero() -> Self {
Self { value: zero() }
}
pub fn one() -> Self {
Self { value: one() }
}
pub fn new(value: u128) -> Self {
let value = InnerUint::from(value).checked_mul(one()).unwrap();
Self { value }
}
pub fn from_scaled_u128(value: u128) -> Self {
Self {
value: InnerUint::from(value),
}
}
pub fn from_values(lo: u64, mid: u64, hi: u64) -> Self {
Self {
value: InnerUint([lo, mid, hi]),
}
}
pub fn to_bytes(&self) -> [u8; 24] {
let InnerUint([lo, mid, hi]) = self.value;
let mut bytes = [0u8; 24];
bytes[0..8].copy_from_slice(&lo.to_le_bytes());
bytes[8..16].copy_from_slice(&mid.to_le_bytes());
bytes[16..24].copy_from_slice(&hi.to_le_bytes());
bytes
}
pub fn from_bytes(bytes: &[u8; 24]) -> Self {
let lo = u64::from_le_bytes(bytes[0..8].try_into().unwrap());
let mid = u64::from_le_bytes(bytes[8..16].try_into().unwrap());
let hi = u64::from_le_bytes(bytes[16..24].try_into().unwrap());
Self {
value: InnerUint([lo, mid, hi]),
}
}
pub fn to_imprecise(&self) -> Option<u128> {
self.value
.checked_add(Self::rounding_correction())?
.checked_div(one())
.map(|v| v.as_u128())
}
pub fn signed(&self) -> SignedNumeric {
SignedNumeric {
value: self.clone(),
is_negative: false,
}
}
fn rounding_correction() -> InnerUint {
InnerUint::from(ONE / 2)
}
pub fn almost_eq(&self, rhs: &Self, precision: InnerUint) -> bool {
let (difference, _) = self.unsigned_sub(rhs);
difference.value < precision
}
pub fn less_than(&self, rhs: &Self) -> bool {
self.value < rhs.value
}
pub fn greater_than(&self, rhs: &Self) -> bool {
self.value > rhs.value
}
pub fn less_than_or_equal(&self, rhs: &Self) -> bool {
self.value <= rhs.value
}
pub fn greater_than_or_equal(&self, rhs: &Self) -> bool {
self.value >= rhs.value
}
pub fn floor(&self) -> Option<Self> {
let value = self.value.checked_div(one())?.checked_mul(one())?;
Some(Self { value })
}
pub fn ceiling(&self) -> Option<Self> {
let value = self
.value
.checked_add(one().checked_sub(InnerUint::from(1))?)?
.checked_div(one())?
.checked_mul(one())?;
Some(Self { value })
}
pub fn checked_div(&self, rhs: &Self) -> Option<Self> {
if *rhs == Self::zero() {
return None;
}
match self.value.checked_mul(one()) {
Some(v) => {
let value = v
.checked_add(Self::rounding_correction())?
.checked_div(rhs.value)?;
Some(Self { value })
}
None => {
let value = self
.value
.checked_add(Self::rounding_correction())?
.checked_div(rhs.value)?
.checked_mul(one())?;
Some(Self { value })
}
}
}
pub fn checked_mul(&self, rhs: &Self) -> Option<Self> {
match self.value.checked_mul(rhs.value) {
Some(v) => {
let value = v
.checked_add(Self::rounding_correction())?
.checked_div(one())?;
Some(Self { value })
}
None => {
let value = if self.value >= rhs.value {
self.value.checked_div(one())?.checked_mul(rhs.value)?
} else {
rhs.value.checked_div(one())?.checked_mul(self.value)?
};
Some(Self { value })
}
}
}
pub fn checked_add(&self, rhs: &Self) -> Option<Self> {
let value = self.value.checked_add(rhs.value)?;
Some(Self { value })
}
pub fn checked_sub(&self, rhs: &Self) -> Option<Self> {
let value = self.value.checked_sub(rhs.value)?;
Some(Self { value })
}
pub fn unsigned_sub(&self, rhs: &Self) -> (Self, bool) {
match self.value.checked_sub(rhs.value) {
None => {
let value = rhs.value.checked_sub(self.value).unwrap();
(Self { value }, true)
}
Some(value) => (Self { value }, false),
}
}
pub fn to_string(&self) -> String {
let whole = self.floor().unwrap().to_imprecise().unwrap();
let decimals = self
.checked_sub(&UnsignedNumeric::new(whole))
.unwrap()
.checked_mul(&UnsignedNumeric::new(ONE))
.unwrap()
.to_imprecise()
.unwrap();
format!("{}.{:0>width$}", whole, decimals, width = 18)
}
}
impl Add for UnsignedNumeric {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
self.checked_add(&rhs).unwrap()
}
}
impl Add<&UnsignedNumeric> for UnsignedNumeric {
type Output = Self;
fn add(self, rhs: &Self) -> Self::Output {
self.checked_add(rhs).unwrap()
}
}
impl Add<UnsignedNumeric> for &UnsignedNumeric {
type Output = UnsignedNumeric;
fn add(self, rhs: UnsignedNumeric) -> Self::Output {
self.checked_add(&rhs).unwrap()
}
}
impl Add<&UnsignedNumeric> for &UnsignedNumeric {
type Output = UnsignedNumeric;
fn add(self, rhs: &UnsignedNumeric) -> Self::Output {
self.checked_add(rhs).unwrap()
}
}
impl Sub for UnsignedNumeric {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
self.checked_sub(&rhs).unwrap()
}
}
impl Sub<&UnsignedNumeric> for UnsignedNumeric {
type Output = Self;
fn sub(self, rhs: &Self) -> Self::Output {
self.checked_sub(rhs).unwrap()
}
}
impl Sub<UnsignedNumeric> for &UnsignedNumeric {
type Output = UnsignedNumeric;
fn sub(self, rhs: UnsignedNumeric) -> Self::Output {
self.checked_sub(&rhs).unwrap()
}
}
impl Sub<&UnsignedNumeric> for &UnsignedNumeric {
type Output = UnsignedNumeric;
fn sub(self, rhs: &UnsignedNumeric) -> Self::Output {
self.checked_sub(rhs).unwrap()
}
}
impl Mul for UnsignedNumeric {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
self.checked_mul(&rhs).unwrap()
}
}
impl Mul<&UnsignedNumeric> for UnsignedNumeric {
type Output = Self;
fn mul(self, rhs: &Self) -> Self::Output {
self.checked_mul(rhs).unwrap()
}
}
impl Mul<UnsignedNumeric> for &UnsignedNumeric {
type Output = UnsignedNumeric;
fn mul(self, rhs: UnsignedNumeric) -> Self::Output {
self.checked_mul(&rhs).unwrap()
}
}
impl Mul<&UnsignedNumeric> for &UnsignedNumeric {
type Output = UnsignedNumeric;
fn mul(self, rhs: &UnsignedNumeric) -> Self::Output {
self.checked_mul(rhs).unwrap()
}
}
impl Div for UnsignedNumeric {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
self.checked_div(&rhs).unwrap()
}
}
impl Div<&UnsignedNumeric> for UnsignedNumeric {
type Output = Self;
fn div(self, rhs: &Self) -> Self::Output {
self.checked_div(rhs).unwrap()
}
}
impl Div<UnsignedNumeric> for &UnsignedNumeric {
type Output = UnsignedNumeric;
fn div(self, rhs: UnsignedNumeric) -> Self::Output {
self.checked_div(&rhs).unwrap()
}
}
impl Div<&UnsignedNumeric> for &UnsignedNumeric {
type Output = UnsignedNumeric;
fn div(self, rhs: &UnsignedNumeric) -> Self::Output {
self.checked_div(rhs).unwrap()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn max_numeric() -> UnsignedNumeric {
UnsignedNumeric {
value: InnerUint::MAX,
}
}
#[test]
fn test_zero_and_one() {
assert_eq!(UnsignedNumeric::zero().value, InnerUint::from(0));
assert_eq!(UnsignedNumeric::one().value, InnerUint::from(ONE));
}
#[test]
fn test_from_scaled_u128() {
let n = UnsignedNumeric::from_scaled_u128(42 * ONE);
assert_eq!(n.to_imprecise().unwrap(), 42);
}
#[test]
fn test_to_string_exact() {
let n = UnsignedNumeric::new(3);
assert_eq!(n.to_string(), "3.000000000000000000");
}
#[test]
fn test_to_string_fractional() {
let mut n = UnsignedNumeric::new(3);
n.value += InnerUint::from(250_000_000_000_000_000u128); assert_eq!(n.to_string(), "3.250000000000000000");
}
#[test]
fn test_checked_add() {
let a = UnsignedNumeric::new(1);
let b = UnsignedNumeric::new(2);
let sum = a.checked_add(&b).unwrap();
assert_eq!(sum.to_imprecise().unwrap(), 3);
}
#[test]
fn test_checked_sub_underflow() {
let a = UnsignedNumeric::new(1);
let b = UnsignedNumeric::new(2);
assert!(a.checked_sub(&b).is_none());
}
#[test]
fn test_checked_sub_valid() {
let a = UnsignedNumeric::new(3);
let b = UnsignedNumeric::new(1);
let result = a.checked_sub(&b).unwrap();
assert_eq!(result.to_imprecise().unwrap(), 2);
}
#[test]
fn test_checked_mul_simple() {
let a = UnsignedNumeric::new(2);
let b = UnsignedNumeric::new(3);
let product = a.checked_mul(&b).unwrap();
assert_eq!(product.to_imprecise().unwrap(), 6);
}
#[test]
fn test_checked_div_exact() {
let a = UnsignedNumeric::new(6);
let b = UnsignedNumeric::new(2);
let result = a.checked_div(&b).unwrap();
assert_eq!(result.to_imprecise().unwrap(), 3);
}
#[test]
fn test_checked_div_rounded() {
let a = UnsignedNumeric::new(1);
let b = UnsignedNumeric::new(3);
let result = a.checked_div(&b).unwrap();
let expected = UnsignedNumeric::from_scaled_u128(333_333_333_333_333_333);
assert!(result.almost_eq(&expected, InnerUint::from(1_000_000)));
}
#[test]
fn test_unsigned_sub_positive() {
let a = UnsignedNumeric::new(7);
let b = UnsignedNumeric::new(3);
let (result, is_negative) = a.unsigned_sub(&b);
assert_eq!(result.to_imprecise().unwrap(), 4);
assert!(!is_negative);
}
#[test]
fn test_unsigned_sub_negative() {
let a = UnsignedNumeric::new(3);
let b = UnsignedNumeric::new(7);
let (result, is_negative) = a.unsigned_sub(&b);
assert_eq!(result.to_imprecise().unwrap(), 4);
assert!(is_negative);
}
#[test]
fn test_almost_eq_within_precision() {
let a = UnsignedNumeric::new(5);
let mut b = a.clone();
b.value += InnerUint::from(10);
assert!(a.almost_eq(&b, InnerUint::from(20)));
assert!(!a.almost_eq(&b, InnerUint::from(5)));
}
#[test]
fn test_comparisons() {
let a = UnsignedNumeric::new(1);
let b = UnsignedNumeric::new(2);
assert!(a.less_than(&b));
assert!(b.greater_than(&a));
assert!(a.less_than_or_equal(&b));
assert!(b.greater_than_or_equal(&a));
assert!(a.less_than_or_equal(&a));
assert!(a.greater_than_or_equal(&a));
}
#[test]
fn test_signed_conversion() {
let u = UnsignedNumeric::new(42);
let s = u.signed();
assert_eq!(s.value, u);
assert!(!s.is_negative);
}
#[test]
fn test_serialization() {
let original = UnsignedNumeric::from_values(123, 456, 789);
let bytes = original.to_bytes();
let recovered = UnsignedNumeric::from_bytes(&bytes);
assert_eq!(original, recovered);
}
#[test]
fn test_floor() {
let whole_number = UnsignedNumeric::new(2);
let mut decimal_number = UnsignedNumeric::new(2);
decimal_number.value += InnerUint::from(1);
let floor = decimal_number.floor().unwrap();
let floor_again = floor.floor().unwrap();
assert_eq!(whole_number.value, floor.value);
assert_eq!(whole_number.value, floor_again.value);
}
#[test]
fn test_ceiling() {
let whole_number = UnsignedNumeric::new(2);
let mut decimal_number = UnsignedNumeric::new(2);
decimal_number.value -= InnerUint::from(1);
let ceiling = decimal_number.ceiling().unwrap();
let ceiling_again = ceiling.ceiling().unwrap();
assert_eq!(whole_number.value, ceiling.value);
assert_eq!(whole_number.value, ceiling_again.value);
}
#[test]
fn test_add_overflow() {
let a = max_numeric();
let b = UnsignedNumeric::one();
assert!(a.checked_add(&b).is_none(), "Expected overflow on add");
}
#[test]
fn test_mul_overflow() {
let a = max_numeric();
let b = UnsignedNumeric::new(2);
let result = a.checked_mul(&b);
assert!(result.is_none(), "Expected overflow on multiply");
}
#[test]
fn test_mul_fallback_path() {
let a = UnsignedNumeric::new(1_000_000_000_000u128); let b = UnsignedNumeric::new(2);
let result = a.checked_mul(&b).unwrap();
assert_eq!(result.to_imprecise().unwrap(), 2_000_000_000_000);
}
#[test]
fn test_mul_large_by_large_overflow() {
let a = UnsignedNumeric {
value: InnerUint([0, 0, 1]), };
let b = a.clone();
let result = a.checked_mul(&b);
assert!(result.is_none(), "Expected overflow on large * large");
}
#[test]
fn test_div_by_zero() {
let a = UnsignedNumeric::new(42);
let zero = UnsignedNumeric::zero();
assert!(a.checked_div(&zero).is_none(), "Expected div by zero to fail");
}
#[test]
fn test_floor_overflow_fallback() {
let a = max_numeric();
let result = a.floor();
assert!(result.is_some(), "Should handle overflow in floor safely");
}
#[test]
fn test_ceiling_overflow() {
let a = max_numeric();
let result = a.ceiling();
assert!(result.is_none(), "Expected overflow in ceiling()");
}
#[test]
fn test_rounding_correction_addition_overflow() {
let a = max_numeric();
let corrected = a.value.checked_add(UnsignedNumeric::rounding_correction());
assert!(corrected.is_none(), "Expected overflow in rounding correction");
}
#[test]
fn test_arithmetic_operators() {
let a = UnsignedNumeric::new(5);
let b = UnsignedNumeric::new(3);
let sum = a.clone() + b.clone();
assert_eq!(sum.to_imprecise().unwrap(), 8);
let diff = a.clone() - b.clone();
assert_eq!(diff.to_imprecise().unwrap(), 2);
let product = a.clone() * b.clone();
assert_eq!(product.to_imprecise().unwrap(), 15);
let quotient = a / b;
assert!(quotient.almost_eq(&UnsignedNumeric::from_scaled_u128(1_666_666_666_666_666_666), InnerUint::from(1_000_000)));
}
#[test]
fn test_arithmetic_operators_with_references() {
let a = UnsignedNumeric::new(10);
let b = UnsignedNumeric::new(4);
let sum = &a + &b;
assert_eq!(sum.to_imprecise().unwrap(), 14);
let diff = &a - &b;
assert_eq!(diff.to_imprecise().unwrap(), 6);
let product = &a * &b;
assert_eq!(product.to_imprecise().unwrap(), 40);
let quotient = &a / &b;
assert_eq!(quotient.to_imprecise().unwrap(), 3);
}
#[test]
fn test_arithmetic_operators_mixed_references() {
let a = UnsignedNumeric::new(6);
let b = UnsignedNumeric::new(2);
let sum1 = a.clone() + &b;
let sum2 = &a + b.clone();
assert_eq!(sum1.to_imprecise().unwrap(), 8);
assert_eq!(sum2.to_imprecise().unwrap(), 8);
let product1 = a.clone() * &b;
let product2 = &a * b.clone();
assert_eq!(product1.to_imprecise().unwrap(), 12);
assert_eq!(product2.to_imprecise().unwrap(), 12);
}
}