use super::FixedPoint;
impl<const D: u32> FixedPoint<u64, D> {
pub const fn checked_add(self, rhs: Self) -> Option<Self> {
match self.raw.checked_add(rhs.raw) {
Some(r) => Some(Self { raw: r }),
None => None,
}
}
pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
match self.raw.checked_sub(rhs.raw) {
Some(r) => Some(Self { raw: r }),
None => None,
}
}
pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
let wide = (self.raw as u128).checked_mul(rhs.raw as u128);
match wide {
Some(w) => {
let scale = Self::scale_u64() as u128;
let result = w / scale;
if result > u64::MAX as u128 {
None
} else {
Some(Self { raw: result as u64 })
}
}
None => None,
}
}
pub const fn checked_div(self, rhs: Self) -> Option<Self> {
if rhs.raw == 0 {
return None;
}
let scale = Self::scale_u64() as u128;
let wide = (self.raw as u128).checked_mul(scale);
match wide {
Some(w) => {
let result = w / (rhs.raw as u128);
if result > u64::MAX as u128 {
None
} else {
Some(Self { raw: result as u64 })
}
}
None => None,
}
}
pub const fn saturating_add(self, rhs: Self) -> Self {
Self { raw: self.raw.saturating_add(rhs.raw) }
}
pub const fn saturating_sub(self, rhs: Self) -> Self {
Self { raw: self.raw.saturating_sub(rhs.raw) }
}
pub const fn saturating_mul(self, rhs: Self) -> Self {
match self.checked_mul(rhs) {
Some(v) => v,
None => Self::MAX,
}
}
pub const fn wrapping_add(self, rhs: Self) -> Self {
Self { raw: self.raw.wrapping_add(rhs.raw) }
}
pub const fn wrapping_sub(self, rhs: Self) -> Self {
Self { raw: self.raw.wrapping_sub(rhs.raw) }
}
pub const fn wrapping_mul(self, rhs: Self) -> Self {
let wide = (self.raw as u128).wrapping_mul(rhs.raw as u128);
let scale = Self::scale_u64() as u128;
Self { raw: (wide / scale) as u64 }
}
}
#[derive(Clone, Copy)]
struct U256 {
lo: u128,
hi: u128,
}
impl U256 {
const fn widening_mul(a: u128, b: u128) -> Self {
let a_lo = a as u64 as u128;
let a_hi = (a >> 64) as u64 as u128;
let b_lo = b as u64 as u128;
let b_hi = (b >> 64) as u64 as u128;
let ll = a_lo * b_lo;
let lh = a_lo * b_hi;
let hl = a_hi * b_lo;
let hh = a_hi * b_hi;
let mid = lh + (ll >> 64);
let mid_lo = mid as u64 as u128;
let mid_hi = mid >> 64;
let mid2 = mid_lo + hl;
let mid2_carry = mid2 >> 64;
let lo = ((mid2 as u64 as u128) << 64) | (ll as u64 as u128);
let hi = hh + mid_hi + mid2_carry;
Self { lo, hi }
}
const fn checked_div_u128(self, divisor: u128) -> Option<u128> {
if divisor == 0 {
return None;
}
if self.hi == 0 {
return Some(self.lo / divisor);
}
if self.hi >= divisor {
return None;
}
let mut remainder = self.hi;
let lo_hi = self.lo >> 64;
let dividend_hi = (remainder << 64) | lo_hi;
let q_hi = dividend_hi / divisor;
remainder = dividend_hi % divisor;
let lo_lo = self.lo as u64 as u128;
let dividend_lo = (remainder << 64) | lo_lo;
let q_lo = dividend_lo / divisor;
Some((q_hi << 64) | q_lo)
}
}
impl<const D: u32> FixedPoint<u128, D> {
pub const fn checked_add(self, rhs: Self) -> Option<Self> {
match self.raw.checked_add(rhs.raw) {
Some(r) => Some(Self { raw: r }),
None => None,
}
}
pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
match self.raw.checked_sub(rhs.raw) {
Some(r) => Some(Self { raw: r }),
None => None,
}
}
pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
let wide = U256::widening_mul(self.raw, rhs.raw);
let scale = Self::scale_u128();
match wide.checked_div_u128(scale) {
Some(result) => Some(Self { raw: result }),
None => None,
}
}
pub const fn checked_div(self, rhs: Self) -> Option<Self> {
if rhs.raw == 0 {
return None;
}
let scale = Self::scale_u128();
let wide = U256::widening_mul(self.raw, scale);
match wide.checked_div_u128(rhs.raw) {
Some(result) => Some(Self { raw: result }),
None => None,
}
}
pub const fn saturating_add(self, rhs: Self) -> Self {
Self { raw: self.raw.saturating_add(rhs.raw) }
}
pub const fn saturating_sub(self, rhs: Self) -> Self {
Self { raw: self.raw.saturating_sub(rhs.raw) }
}
pub const fn saturating_mul(self, rhs: Self) -> Self {
match self.checked_mul(rhs) {
Some(v) => v,
None => Self::MAX,
}
}
pub const fn wrapping_add(self, rhs: Self) -> Self {
Self { raw: self.raw.wrapping_add(rhs.raw) }
}
pub const fn wrapping_sub(self, rhs: Self) -> Self {
Self { raw: self.raw.wrapping_sub(rhs.raw) }
}
pub const fn wrapping_mul(self, rhs: Self) -> Self {
let wide = U256::widening_mul(self.raw, rhs.raw);
let scale = Self::scale_u128();
match wide.checked_div_u128(scale) {
Some(r) => Self { raw: r },
None => {
let lo_result = wide.lo.wrapping_div(scale);
Self { raw: lo_result }
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
type Fp6 = FixedPoint<u64, 6>;
type Fp18 = FixedPoint<u128, 18>;
#[test]
fn u64_checked_add() {
let a = Fp6::from_raw(1_000_000); let b = Fp6::from_raw(2_000_000); let c = a.checked_add(b).unwrap();
assert_eq!(*c.raw(), 3_000_000);
}
#[test]
fn u64_checked_add_overflow() {
let a = Fp6::MAX;
let b = Fp6::from_raw(1);
assert!(a.checked_add(b).is_none());
}
#[test]
fn u64_checked_sub() {
let a = Fp6::from_raw(3_000_000);
let b = Fp6::from_raw(1_000_000);
let c = a.checked_sub(b).unwrap();
assert_eq!(*c.raw(), 2_000_000);
}
#[test]
fn u64_checked_sub_underflow() {
let a = Fp6::ZERO;
let b = Fp6::from_raw(1);
assert!(a.checked_sub(b).is_none());
}
#[test]
fn u64_checked_mul() {
let a = Fp6::from_raw(2_000_000); let b = Fp6::from_raw(3_000_000); let c = a.checked_mul(b).unwrap();
assert_eq!(*c.raw(), 6_000_000); }
#[test]
fn u64_checked_mul_fractional() {
let a = Fp6::from_raw(1_500_000); let b = Fp6::from_raw(2_000_000); let c = a.checked_mul(b).unwrap();
assert_eq!(*c.raw(), 3_000_000); }
#[test]
fn u64_checked_div() {
let a = Fp6::from_raw(6_000_000); let b = Fp6::from_raw(2_000_000); let c = a.checked_div(b).unwrap();
assert_eq!(*c.raw(), 3_000_000); }
#[test]
fn u64_checked_div_by_zero() {
let a = Fp6::from_raw(1_000_000);
assert!(a.checked_div(Fp6::ZERO).is_none());
}
#[test]
fn u64_saturating_add() {
let a = Fp6::MAX;
let b = Fp6::from_raw(1);
assert_eq!(a.saturating_add(b), Fp6::MAX);
}
#[test]
fn u64_saturating_sub() {
let a = Fp6::ZERO;
let b = Fp6::from_raw(1);
assert_eq!(a.saturating_sub(b), Fp6::ZERO);
}
#[test]
fn u64_wrapping_add() {
let a = Fp6::MAX;
let b = Fp6::from_raw(1);
assert_eq!(a.wrapping_add(b), Fp6::ZERO);
}
#[test]
fn u64_mul_by_zero() {
let a = Fp6::from_raw(1_000_000);
let c = a.checked_mul(Fp6::ZERO).unwrap();
assert_eq!(*c.raw(), 0);
}
#[test]
fn u64_mul_by_one() {
let a = Fp6::from_raw(12_345_678);
let c = a.checked_mul(Fp6::ONE).unwrap();
assert_eq!(*c.raw(), 12_345_678);
}
#[test]
fn u128_checked_add() {
let a = Fp18::from_raw(1_000_000_000_000_000_000); let b = Fp18::from_raw(2_000_000_000_000_000_000); let c = a.checked_add(b).unwrap();
assert_eq!(*c.raw(), 3_000_000_000_000_000_000);
}
#[test]
fn u128_checked_mul() {
let a = Fp18::from_raw(2_000_000_000_000_000_000); let b = Fp18::from_raw(3_000_000_000_000_000_000); let c = a.checked_mul(b).unwrap();
assert_eq!(*c.raw(), 6_000_000_000_000_000_000); }
#[test]
fn u128_checked_mul_fractional() {
let scale = Fp18::scale();
let a = Fp18::from_raw(scale / 2); let b = Fp18::from_raw(scale / 4); let c = a.checked_mul(b).unwrap();
assert_eq!(*c.raw(), scale / 8); }
#[test]
fn u128_checked_div() {
let a = Fp18::from_raw(6_000_000_000_000_000_000); let b = Fp18::from_raw(2_000_000_000_000_000_000); let c = a.checked_div(b).unwrap();
assert_eq!(*c.raw(), 3_000_000_000_000_000_000); }
#[test]
fn u128_checked_div_by_zero() {
let a = Fp18::from_raw(1_000_000_000_000_000_000);
assert!(a.checked_div(Fp18::ZERO).is_none());
}
#[test]
fn u128_mul_by_one() {
let a = Fp18::from_raw(123_456_789_000_000_000);
let c = a.checked_mul(Fp18::ONE).unwrap();
assert_eq!(*c.raw(), 123_456_789_000_000_000);
}
#[test]
fn u128_large_mul() {
let a = Fp18::from_raw(100_000_000_000_000_000_000u128); let b = Fp18::from_raw(200_000_000_000_000_000_000u128); let c = a.checked_mul(b).unwrap();
assert_eq!(*c.raw(), 20_000_000_000_000_000_000_000u128); }
#[test]
fn u256_widening_mul_basic() {
let r = U256::widening_mul(10, 20);
assert_eq!(r.lo, 200);
assert_eq!(r.hi, 0);
}
#[test]
fn u256_widening_mul_large() {
let a: u128 = 1 << 127;
let r = U256::widening_mul(a, 2);
assert_eq!(r.lo, 0);
assert_eq!(r.hi, 1);
}
#[test]
fn u256_div_basic() {
let v = U256 { lo: 200, hi: 0 };
assert_eq!(v.checked_div_u128(10), Some(20));
}
#[test]
fn u256_div_with_hi() {
let v = U256 { lo: 0, hi: 1 };
assert_eq!(v.checked_div_u128(2), Some(1u128 << 127));
}
#[test]
fn u256_div_by_zero() {
let v = U256 { lo: 100, hi: 0 };
assert!(v.checked_div_u128(0).is_none());
}
}