use anchor_lang::{AnchorDeserialize, AnchorSerialize};
use spl_math::{
precise_number::{self, *},
uint::U256,
};
use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign};
#[derive(Default, Clone, Debug, Copy, PartialEq, Eq, AnchorSerialize, AnchorDeserialize)]
pub struct Number([u64; 4]);
impl core::fmt::Display for Number {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let n = U256(self.0);
write!(f, "{}", n)
}
}
impl From<u64> for Number {
fn from(value: u64) -> Self {
let pn = PreciseNumber::new(value.into()).unwrap();
Number(pn.value.0)
}
}
impl From<u128> for Number {
fn from(value: u128) -> Self {
let pn = PreciseNumber::new(value).unwrap();
Number(pn.value.0)
}
}
impl Number {
pub const SIZEOF: usize = 32;
pub const ZERO: Self = Self(U256::zero().0);
pub const ONE: Self = Self([precise_number::ONE as u64, 0, 0, 0]);
pub const DENOM: u128 = precise_number::ONE;
pub fn from_bytes_le(slice: &[u8]) -> Self {
Self(U256::from_little_endian(slice).0)
}
pub fn from_natural_u64(value: u64) -> Self {
value.into()
}
pub fn from_ratio(num: u128, den: u128) -> Self {
let num = PreciseNumber::new(num).unwrap();
let den = PreciseNumber::new(den).unwrap();
PreciseNumber::checked_div(&num, &den)
.unwrap_or(PreciseNumber::new(0).unwrap())
.into()
}
pub fn from_bps(bps: u16) -> Self {
Self::from_natural_u64(bps as u64) / Self::from_natural_u64(10_000)
}
pub fn checked_add(&self, x: &Self) -> Option<Self> {
self.to_pn()
.checked_add(&x.to_pn())
.map(|pn| Self(pn.value.0))
}
pub fn checked_sub(&self, x: &Self) -> Option<Self> {
self.to_pn()
.checked_sub(&x.to_pn())
.map(|pn| Self(pn.value.0))
}
pub fn checked_mul(&self, x: &Self) -> Option<Self> {
self.to_pn()
.checked_mul(&x.to_pn())
.map(|pn| Self(pn.value.0))
}
pub fn checked_div(&self, x: &Self) -> Option<Self> {
self.to_pn()
.checked_div(&x.to_pn())
.map(|pn| Self(pn.value.0))
}
pub fn to_pn(&self) -> PreciseNumber {
PreciseNumber {
value: U256(self.0),
}
}
pub fn min(numbers: &[Self]) -> Self {
*numbers.iter().min().unwrap()
}
pub fn floor_u64(&self) -> u64 {
self.to_pn()
.floor()
.unwrap()
.to_imprecise()
.unwrap()
.try_into()
.unwrap()
}
pub fn ceil(&self) -> u128 {
self.to_pn().ceiling().unwrap().to_imprecise().unwrap()
}
pub fn ceil_u64(&self) -> u64 {
self.ceil().try_into().unwrap()
}
pub fn floor_u128(&self) -> u128 {
self.to_pn().floor().unwrap().to_imprecise().unwrap()
}
pub fn to_f64(&self) -> Option<f64> {
let n = self
.checked_mul(&Number::from(Self::DENOM))
.unwrap()
.to_pn()
.to_imprecise()
.unwrap();
let d = precise_number::ONE;
u128_to_f64_checked(n, d)
}
}
fn u128_to_f64_checked(numerator: u128, denominator: u128) -> Option<f64> {
const MAX_EXACT_U64: u128 = (1u128 << 53) - 1;
const U64_MAX_PLUS_ONE: f64 = 18446744073709551616.0;
if denominator == 0 {
return None; }
let num_high = (numerator >> 64) as u64;
let num_low = numerator as u64;
let denom_high = (denominator >> 64) as u64;
let denom_low = denominator as u64;
if numerator > MAX_EXACT_U64 || denominator > MAX_EXACT_U64 {
return None; }
let num_f64 = (num_high as f64) * U64_MAX_PLUS_ONE + num_low as f64;
let denom_f64 = (denom_high as f64) * U64_MAX_PLUS_ONE + denom_low as f64;
Some(num_f64 / denom_f64)
}
impl From<PreciseNumber> for Number {
fn from(pn: PreciseNumber) -> Self {
Self(pn.value.0)
}
}
impl Add<Number> for Number {
type Output = Self;
fn add(self, rhs: Number) -> Self::Output {
Self(self.to_pn().checked_add(&rhs.to_pn()).unwrap().value.0)
}
}
impl Mul<Number> for Number {
type Output = Self;
fn mul(self, x: Number) -> Self::Output {
Self(self.checked_mul(&x).unwrap().0)
}
}
impl Div<Number> for Number {
type Output = Self;
fn div(self, x: Number) -> Self {
Self(self.checked_div(&x).unwrap().0)
}
}
impl AddAssign<Number> for Number {
fn add_assign(&mut self, rhs: Number) {
self.0 = self.checked_add(&rhs).unwrap().0;
}
}
impl SubAssign<Number> for Number {
fn sub_assign(&mut self, rhs: Number) {
self.0 = self.checked_sub(&rhs).unwrap().0;
}
}
impl Sub<Number> for Number {
type Output = Self;
fn sub(self, x: Number) -> Self {
self.checked_sub(&x).unwrap()
}
}
impl PartialOrd for Number {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Number {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
U256(self.0).cmp(&U256(other.0))
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_to_f64() {
let n = Number::from_ratio(1, 2);
assert_eq!(n.to_f64(), Some(0.5));
let n = Number::from_ratio(123456789, 1_000_000_000);
assert_eq!(n.to_f64(), Some(0.123456789));
let n = Number::from_ratio(0, 1_000_000_000);
assert_eq!(n.to_f64(), Some(0.0));
let n = Number::from_ratio(1_000_000_000, 1_000_000_000);
assert_eq!(n.to_f64(), Some(1.0));
let n = Number::from_ratio(1_000_000_000_000, 1_000_000_000);
assert_eq!(n.to_f64(), Some(1000.0));
let n = Number::from_ratio(123456789123, 100_000_000_000);
assert_eq!(n.to_f64(), Some(1.23456789123));
}
#[test]
fn test_serialization() {
let n = Number([ONE as u64, 0, 0, 0]);
assert_eq!(n.floor_u64(), 1);
let n = Number::from_natural_u64(1_000_000_000);
let w0 = n.0[0] as u128;
let w1 = n.0[1] as u128;
let target: u128 = 1_000_000_000 * ONE;
assert_eq!(w0 + (w1 << 64), target);
}
#[test]
fn test_add() {
let num1 = Number::from(100u64);
let num2 = Number::from(200u64);
let ans = Number::from(300u64);
let num3 = num1 + num2;
assert_eq!(num3, ans);
}
#[test]
fn test_sub() {
let num1 = Number::from(100u64);
let num2 = Number::from(200u64);
let ans = Number::from(100u64);
let num3 = num2 - num1;
assert_eq!(num3, ans);
}
#[test]
fn test_mul() {
let num1 = Number::from(100u64);
let num2 = Number::from(200u64);
let ans = Number::from(20000u64);
let num3 = num1 * num2;
assert_eq!(num3, ans);
}
#[test]
fn test_div() {
let num1 = Number::from(100u64);
let num2 = Number::from(20u64);
let ans = Number::from(5u64);
let num3 = num1 / num2;
assert_eq!(num3, ans);
}
#[test]
fn test_add_assign() {
let mut num1 = Number::from(100u64);
let num2 = Number::from(200u64);
let ans = Number::from(300u64);
num1 += num2;
assert_eq!(num1, ans);
}
#[test]
fn test_sub_assign() {
let mut num1 = Number::from(300u64);
let num2 = Number::from(200u64);
let ans = Number::from(100u64);
num1 -= num2;
assert_eq!(num1, ans);
}
#[test]
fn test_min() {
let numbers = [
Number::from(300u64),
Number::from(100u64),
Number::from(200u64),
Number::from(400u64),
];
let min_value = Number::min(&numbers);
assert_eq!(min_value, Number::from(100u64));
}
#[test]
fn test_from_ratio() {
let num1 = Number::from_ratio(1, 2);
assert!(num1 > Number::from(0u64));
assert!(num1 < Number::from(1u64));
}
#[test]
fn test_mul_div() {
let e = Number::from_ratio(22346643, 234216);
let a = Number::from_natural_u64(100);
let e_a = e * a;
let e_i = Number::ONE / e;
assert_eq!(a / e_a, e_i);
}
#[test]
fn test_floor_u64() {
let e = Number::from_ratio(10, 11);
assert_eq!(e.floor_u64(), 0);
let e = Number::from_ratio(11, 10);
assert_eq!(e.floor_u64(), 1);
}
#[test]
fn test_floor_u128() {
assert_eq!(Number::from_ratio(19, 10).floor_u128(), 1);
assert_eq!(Number::from_ratio(21, 10).floor_u128(), 2);
assert_eq!(Number::from_ratio(29, 10).floor_u128(), 2);
assert_eq!(Number::from_ratio(20, 10).floor_u128(), 2);
assert_eq!(Number::from_ratio(300, 100).floor_u128(), 3);
assert_eq!(Number::from_ratio(299, 10).floor_u128(), 29);
assert_eq!(Number::from_ratio(9, 10).floor_u128(), 0);
}
}