use super::consts::*;
use super::unsigned::UnsignedNumeric;
use super::signed::SignedNumeric;
impl SignedNumeric {
pub fn exp(&self) -> Option<UnsignedNumeric> {
let hi: Self;
let lo: Self;
let k: Self;
let x: Self;
if self.value.greater_than(&HALFLN2) {
if self.value.greater_than_or_equal(&THREEHALFLN2) {
k = INVLN2
.signed()
.checked_mul(self)?
.checked_add(&Self {
value: HALF,
is_negative: self.is_negative,
})?
.floor()?;
} else {
k = Self {
value: UnsignedNumeric::one(),
is_negative: self.is_negative,
}
}
hi = self.checked_sub(
&k.checked_mul(&LN2HI.signed())?
.checked_div(&LN2HI_SCALE.signed())?,
)?;
lo = k
.checked_mul(&LN2LO.signed())?
.checked_div(&LN2LO_SCALE.signed())?;
x = hi.checked_sub(&lo)?
} else {
x = self.clone();
k = UnsignedNumeric::zero().signed();
hi = self.clone();
lo = UnsignedNumeric::zero().signed()
}
let xx = x.checked_mul(&x)?;
let p4p5 = P4.checked_add(&xx.checked_mul(&P5)?)?;
let p3p4p5 = P3.checked_add(&xx.checked_mul(&p4p5)?)?;
let p2p3p4p5 = P2.checked_add(&xx.checked_mul(&p3p4p5)?)?;
let p1p2p3p4p5 = P1.checked_add(&xx.checked_mul(&p2p3p4p5)?)?;
let c = x.checked_sub(&p1p2p3p4p5.checked_mul(&xx)?)?;
let y = ONE_PREC.signed().checked_add(
&x.checked_mul(&c)?
.checked_div(&TWO_PREC.signed().checked_sub(&c)?)?
.checked_sub(&lo)?
.checked_add(&hi)?,
)?;
if k.value.eq(&UnsignedNumeric::zero()) {
Some(y.value)
} else {
let bits = k.value.to_imprecise()?;
if k.is_negative {
Some(UnsignedNumeric {
value: y.value.value >> bits,
})
} else {
Some(UnsignedNumeric {
value: y.value.value << bits,
})
}
}
}
pub fn sqrt(&self) -> Option<Self> {
if self.is_negative {
return None;
}
self.value.sqrt().map(|v| Self { value: v, is_negative: false })
}
}
impl UnsignedNumeric {
pub fn frexp(&self) -> Option<(Self, i64)> {
if self.eq(&ZERO_PREC) {
Some((ZERO_PREC.clone(), 0))
} else if self.less_than(&ONE_PREC) {
let first_leading = self.value.0[0].leading_zeros();
let one_leading = ONE_PREC.value.0[0].leading_zeros();
let bits = i64::from(first_leading.checked_sub(one_leading).unwrap());
let frac = UnsignedNumeric {
value: self.value << bits,
};
if frac.less_than(&HALF) {
Some((frac.checked_mul(&TWO_PREC).unwrap(), -bits - 1))
} else {
Some((frac, -bits))
}
} else {
let bits = 128_i64.checked_sub(i64::from(self.to_imprecise()?.leading_zeros()))?;
let frac = UnsignedNumeric {
value: self.value >> bits,
};
if frac.less_than(&HALF) {
Some((frac.checked_mul(&TWO_PREC).unwrap(), bits - 1))
} else {
Some((frac, bits))
}
}
}
pub fn pow(&self, exp: &Self) -> Option<Self> {
if self.eq(&ZERO_PREC) {
return Some(ZERO_PREC.clone());
}
let lg = self.log()?;
let x = exp.signed().checked_mul(&lg)?;
x.exp()
}
pub fn sqrt(&self) -> Option<Self> {
self.pow(&HALF)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::InnerUint;
fn frexp_recombine(frac: UnsignedNumeric, exp: i64) -> UnsignedNumeric {
let shifted = if exp >= 0 {
UnsignedNumeric {
value: frac.value << (exp as usize),
}
} else {
UnsignedNumeric {
value: frac.value >> ((-exp) as usize),
}
};
shifted
}
#[test]
fn test_signed_exp() {
let precision = InnerUint::from(1_000_000_000_u128);
let half = UnsignedNumeric { value: half() }.signed();
assert!(half.exp().unwrap().almost_eq(
&UnsignedNumeric::new(16487212707001282)
.checked_div(&UnsignedNumeric::new(10000000000000000))
.unwrap(),
precision
));
let three_half = UnsignedNumeric::new(15)
.checked_div(&UnsignedNumeric::new(10))
.unwrap()
.signed();
assert!(three_half.exp().unwrap().almost_eq(
&UnsignedNumeric::new(44816890703380645)
.checked_div(&UnsignedNumeric::new(10000000000000000))
.unwrap(),
precision
));
let point_one = UnsignedNumeric::new(1)
.checked_div(&UnsignedNumeric::new(10))
.unwrap()
.signed();
assert!(point_one.exp().unwrap().almost_eq(
&UnsignedNumeric::new(11051709180756477)
.checked_div(&UnsignedNumeric::new(10000000000000000))
.unwrap(),
precision
));
let negative = UnsignedNumeric::new(55)
.checked_div(&UnsignedNumeric::new(100))
.unwrap()
.signed()
.negate();
assert!(negative.exp().unwrap().almost_eq(
&UnsignedNumeric::new(5769498103804866)
.checked_div(&UnsignedNumeric::new(10000000000000000))
.unwrap(),
precision
));
let test = UnsignedNumeric::new(19).signed();
assert!(test.exp().unwrap().almost_eq(
&UnsignedNumeric::new(178482300963187260)
.checked_div(&UnsignedNumeric::new(1000000000))
.unwrap(),
precision
));
}
#[test]
fn test_pow() {
let precision = InnerUint::from(5_000_000_000_000_u128); let test = UnsignedNumeric::new(8);
let sqrt = test.pow(&HALF).unwrap();
let expected = UnsignedNumeric::new(28284271247461903)
.checked_div(&UnsignedNumeric::new(10000000000000000))
.unwrap();
assert!(sqrt.almost_eq(&expected, precision));
let test2 = UnsignedNumeric::new(55)
.checked_div(&UnsignedNumeric::new(100))
.unwrap();
let squared = test2.pow(&TWO_PREC).unwrap();
let expected = UnsignedNumeric::new(3025)
.checked_div(&UnsignedNumeric::new(10000))
.unwrap();
assert!(squared.almost_eq(&expected, precision));
}
#[test]
fn test_sqrt() {
let precision = InnerUint::from(5_000_000_000_000_u128); let test = UnsignedNumeric::new(12);
let sqrt = test.sqrt().unwrap();
let expected = UnsignedNumeric::new(34641016151377544)
.checked_div(&UnsignedNumeric::new(10000000000000000))
.unwrap();
assert!(sqrt.almost_eq(&expected, precision));
}
#[test]
pub fn test_signed_sqrt() {
let precision = InnerUint::from(5_000_000_000_000_u128); let test = SignedNumeric {
value: UnsignedNumeric::new(8),
is_negative: false,
};
let sqrt = test.sqrt().unwrap();
let expected = UnsignedNumeric::new(28284271247461903)
.checked_div(&UnsignedNumeric::new(10000000000000000))
.unwrap();
assert!(sqrt.value.almost_eq(&expected, precision));
let neg_test = SignedNumeric {
value: UnsignedNumeric::new(8),
is_negative: true,
};
assert!(neg_test.sqrt().is_none());
}
#[test]
fn test_exp_zero() {
let zero = UnsignedNumeric::zero().signed();
let result = zero.exp().unwrap();
assert_eq!(result, UnsignedNumeric::one());
}
#[test]
fn test_exp_small_negative() {
let x = UnsignedNumeric::new(1)
.checked_div(&UnsignedNumeric::new(1000))
.unwrap()
.signed()
.negate();
let result = x.exp().unwrap();
let expected = UnsignedNumeric::from_scaled_u128(999_000_499_833_375_000);
assert!(
result.almost_eq(&expected, InnerUint::from(10_000_000)),
"got: {} expected: {}",
result.to_string(),
expected.to_string()
);
}
#[test]
fn test_exp_large_positive() {
let x = UnsignedNumeric::new(10).signed(); let result = x.exp().unwrap();
let expected = UnsignedNumeric::from_scaled_u128(22_026_465_794_806_700_000_000);
assert!(
result.almost_eq(&expected, InnerUint::from(1_000_000_000_000_u64)),
"got: {} expected: {}",
result.to_string(),
expected.to_string()
);
}
#[test]
fn test_exp_large_negative() {
let x = UnsignedNumeric::new(10).signed().negate(); let result = x.exp().unwrap();
let expected = UnsignedNumeric::from_scaled_u128(45_399_920_000_000); assert!(
result.almost_eq(&expected, InnerUint::from(1_000_000_000)),
"got: {} expected: {}",
result.to_string(),
expected.to_string()
);
}
#[test]
fn test_frexp_zero() {
let zero = UnsignedNumeric::zero();
let (frac, exp) = zero.frexp().unwrap();
assert_eq!(frac, zero);
assert_eq!(exp, 0);
}
#[test]
fn test_frexp_one() {
let one = UnsignedNumeric::one();
let (frac, exp) = one.frexp().unwrap();
let recombined = frexp_recombine(frac.clone(), exp);
assert!(
recombined.almost_eq(&one, InnerUint::from(1_000_000_000)),
"Expected: {}, Got: {} × 2^{} = {}",
one.to_string(),
frac.to_string(),
exp,
recombined.to_string()
);
assert!(frac.greater_than_or_equal(&HALF));
assert!(frac.less_than(&ONE_PREC));
}
#[test]
fn test_frexp_two() {
let two = UnsignedNumeric::new(2);
let (frac, exp) = two.frexp().unwrap();
let recombined = frexp_recombine(frac.clone(), exp);
assert!(
recombined.almost_eq(&two, InnerUint::from(1_000_000_000)),
"Expected: {}, Got: {} × 2^{} = {}",
two.to_string(),
frac.to_string(),
exp,
recombined.to_string()
);
assert!(frac.greater_than_or_equal(&HALF));
assert!(frac.less_than(&ONE_PREC));
}
#[test]
fn test_frexp_fractional() {
let val = UnsignedNumeric::new(3)
.checked_div(&UnsignedNumeric::new(4)) .unwrap();
let (frac, exp) = val.frexp().unwrap();
let recombined = frexp_recombine(frac.clone(), exp);
assert!(
recombined.almost_eq(&val, InnerUint::from(1_000_000_000)),
"Expected: {}, Got: {} × 2^{} = {}",
val.to_string(),
frac.to_string(),
exp,
recombined.to_string()
);
assert!(frac.greater_than_or_equal(&HALF));
assert!(frac.less_than(&ONE_PREC));
}
#[test]
fn test_frexp_large_value() {
let val = UnsignedNumeric {
value: InnerUint([0, 0, 1]), };
let (frac, exp) = val.frexp().unwrap();
let recombined = frexp_recombine(frac.clone(), exp);
assert!(
recombined.almost_eq(&val, InnerUint::from(10_000_000_000_u64)),
"Expected: {}, Got: {} × 2^{} = {}",
val.to_string(),
frac.to_string(),
exp,
recombined.to_string()
);
}
}