use core::fmt;
use core::cmp::Ordering;
use core::ops::*;
use core::str::FromStr;
use gcd::Gcd;
use super::{ParseRatioErr, r32};
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Eq, Default)]
pub struct r64(u64);
const DSIZE_SIZE: u32 = 6;
const FRACTION_SIZE: u64 = 58;
const FRACTION_FIELD: u64 = (1 << FRACTION_SIZE) - 1;
impl r64 {
#[inline]
fn get_frac_size(n: i128, d: u128) -> u64 {
let dsize = 128 - d.leading_zeros() - 1;
let nsize = if n >= 0 {
128 - n.leading_zeros() + 1
} else {
128 - n.leading_ones() + 1
};
(nsize + dsize) as u64
}
#[inline]
pub const unsafe fn new_unchecked(numer: i64, denom: u64) -> r64 {
let denom_size = 64 - denom.leading_zeros() - 1;
let denom_mask = (1 << denom_size as u64) - 1;
let numer_mask = FRACTION_FIELD & !denom_mask;
r64(
(denom_size as u64) << FRACTION_SIZE |
((numer << denom_size) as u64) & numer_mask |
denom & denom_mask
)
}
pub fn new(mut numer: i64, mut denom: u64) -> Option<r64> {
let gcd = numer.unsigned_abs().gcd(denom);
numer /= gcd as i64;
denom /= gcd;
let denom_size = 64 - denom.leading_zeros() - 1;
let numer_size = if numer >= 0 {
64 - numer.leading_zeros() + 1
} else {
64 - numer.leading_ones() + 1
};
if numer_size + denom_size > FRACTION_SIZE as u32 {
return None;
}
unsafe {
Some(r64::new_unchecked(numer, denom))
}
}
pub fn sqrt(self) -> r64 {
let f: f64 = self.into();
r64::from(f.sqrt())
}
pub fn cbrt(self) -> r64 {
let f: f64 = self.into();
r64::from(f.cbrt())
}
pub fn checked_add(self, rhs: r64) -> Option<r64> {
if self.is_nan() || rhs.is_nan() { return Some(r64::NAN) }
let mut num =
self.numer() as i128 * rhs.denom() as i128
+ self.denom() as i128 * rhs.numer() as i128;
let mut den = self.denom() as u128 * rhs.denom() as u128;
let mut size = r64::get_frac_size(num, den);
if size > FRACTION_SIZE {
let gcd = num.unsigned_abs().gcd(den);
num /= gcd as i128;
den /= gcd;
size = r64::get_frac_size(num, den);
}
if size <= FRACTION_SIZE {
unsafe { Some(r64::new_unchecked(num as i64, den as u64)) }
} else {
None
}
}
pub fn checked_mul(self, rhs: r64) -> Option<r64> {
match (self.is_nan(), rhs.is_nan()) {
(true, false) if rhs.numer() == 0 => return Some(r64(0)),
(false, true) if self.numer() == 0 => return Some(r64(0)),
(false, false) => {}
_ => return Some(r64::NAN),
}
let mut n = self.numer() as i128 * rhs.numer() as i128;
let mut d = self.denom() as u128 * rhs.denom() as u128;
let mut size = r64::get_frac_size(n, d);
if size > FRACTION_SIZE {
let gcd = n.unsigned_abs().gcd(d);
n /= gcd as i128;
d /= gcd;
size = r64::get_frac_size(n, d);
}
if size <= FRACTION_SIZE {
unsafe { Some(r64::new_unchecked(n as i64, d as u64)) }
} else {
None
}
}
#[inline]
pub fn to_i64(self) -> Option<i64> {
let norm = self.normalize();
if norm.denom_size() == 0 {
Some(norm.numer())
} else {
None
}
}
}
crate::impl_ratio_type! { r64 u64 i64 NonZeroU64 }
impl From<u16> for r64 {
#[inline]
fn from(v: u16) -> Self { r64(v as u64) }
}
impl From<i16> for r64 {
fn from(v: i16) -> Self {
unsafe { r64::new_unchecked(v as i64, 1) }
}
}
impl From<u32> for r64 {
#[inline]
fn from(v: u32) -> Self { r64(v as u64) }
}
impl From<i32> for r64 {
fn from(v: i32) -> Self {
unsafe { r64::new_unchecked(v as i64, 1) }
}
}
impl From<f32> for r64 {
fn from(f: f32) -> Self { r64::from(f as f64) }
}
impl From<f64> for r64 {
fn from(mut f: f64) -> Self {
const N: u64 = (1 << 29) - 1; let is_neg = f < 0.0;
if f.is_nan() || f.is_infinite() {
return r64::NAN;
}
if is_neg { f = f.abs() }
let (mut a, mut b) = (0, 1); let (mut c, mut d) = (1, 0); let mut is_mediant = false;
while b <= N && d <= N {
let mediant = (a + c) as f64 / (b + d) as f64;
if f == mediant {
is_mediant = true;
break;
} else if f > mediant {
a += c;
b += d;
} else {
c += a;
d += b;
}
}
let result = if is_mediant {
if b + d <= N { (a + c, b + d) } else if d > b { (c, d) } else { (a, b) } } else {
if b > N { (c, d) } else { (a, b) } };
unsafe {
if is_neg {
r64::new_unchecked(-result.0, result.1)
} else {
r64::new_unchecked(result.0, result.1)
}
}
}
}
impl From<r32> for r64 {
fn from(v: r32) -> Self {
unsafe { r64::new_unchecked(v.numer() as i64, v.denom() as u64) }
}
}
impl From<r64> for f32 {
fn from(r: r64) -> f32 {
if r.is_nan() {
f32::NAN
} else {
(r.numer() as f32) / (r.denom() as f32)
}
}
}
impl From<r64> for f64 {
fn from(r: r64) -> f64 {
if r.is_nan() {
f64::NAN
} else {
(r.numer() as f64) / (r.denom() as f64)
}
}
}
impl PartialOrd for r64 {
fn partial_cmp(&self, other: &r64) -> Option<Ordering> {
if self.is_nan() && other.is_nan() {
return Some(Ordering::Equal);
}
if self.is_nan() || other.is_nan() {
return None;
}
self.is_positive()
.partial_cmp(&other.is_positive())
.map(|c| c.then(
(self.numer() as i128 * other.denom() as i128)
.cmp(&(self.denom() as i128 * other.numer() as i128))
))
}
}
#[cfg(test)]
mod tests {
#[cfg(feature = "bench")]
extern crate test;
use super::*;
use super::super::r64;
#[test]
fn normalize() {
assert_eq!(r64!(4/2).normalize(), r64!(2));
assert_eq!(r64!(-4/2).normalize(), r64!(-2));
}
#[test]
fn neg() {
assert_eq!(-r64(0), r64(0));
assert_eq!(-r64(1), r64!(-1));
assert_eq!(-r64!(-1), r64(1));
}
#[test]
fn signum() {
assert_eq!(r64::NAN.signum(), r64::NAN);
assert_eq!(r64(0).signum(), r64(0));
assert_eq!(r64(1).signum(), r64(1));
assert_eq!(r64(2).signum(), r64(1));
assert_eq!(r64!(-1).signum(), r64!(-1));
assert_eq!(r64!(-2).signum(), r64!(-1));
}
#[test]
fn pow() {
assert_eq!(r64::NAN.pow(0), r64(1));
assert_eq!(r64(0).pow(0), r64(1));
assert_eq!(r64(1).pow(1), r64(1));
assert_eq!(r64(3).pow(2), r64(9));
assert_eq!(r64(3).pow(-2), r64!(1/9));
assert_eq!(r64!(-3).pow(2), r64(9));
assert_eq!(r64!(-3).pow(-2), r64!(1/9));
assert_eq!(r64(2).pow(3), r64(8));
assert_eq!(r64(2).pow(-3), r64!(1/8));
assert_eq!(r64!(1/2).pow(3), r64!(1/8));
assert_eq!(r64!(1/2).pow(-3), r64(8));
assert_eq!(r64!(-2/1).pow(3), r64!(-8/1));
assert_eq!(r64!(-2/1).pow(-3), r64!(-1/8));
assert_eq!(r64!(-1/2).pow(3), r64!(-1/8));
assert_eq!(r64!(-1/2).pow(-3), r64!(-8/1));
}
#[test]
fn checked_pow() {
assert_eq!(r64::NAN.checked_pow(0), Some(r64(1)));
assert_eq!(r64(3).checked_pow(60), None);
}
#[test]
#[cfg(feature = "roots")]
fn checked_sqrt() {
assert_eq!(r64(0).checked_sqrt(), Some(r64(0)));
assert_eq!(r64(1).checked_sqrt(), Some(r64(1)));
assert_eq!(r64(2).checked_sqrt(), None);
assert_eq!(r64(4).checked_sqrt(), Some(r64(2)));
}
#[test]
fn fract() {
assert_eq!(r64(5).fract(), r64(0));
assert_eq!(r64!(3/2).fract(), r64!( 1/2));
assert_eq!(r64!(-3/2).fract(), r64!(-1/2));
}
#[test]
fn trunc() {
assert_eq!(r64(5).trunc(), r64(5));
assert_eq!(r64!( 1/2).trunc(), r64(0));
assert_eq!(r64!(-1/2).trunc(), r64(0));
assert_eq!(r64!( 3/2).trunc(), r64(1));
assert_eq!(r64!(-3/2).trunc(), r64!(-1));
}
#[test]
fn floor() {
assert_eq!(r64!( 3/2).floor(), r64(1));
assert_eq!(r64!( 2/1).floor(), r64(2));
assert_eq!(r64!(-3/2).floor(), r64!(-2));
assert_eq!(r64!(-2/1).floor(), r64!(-2));
}
#[test]
fn ceil() {
assert_eq!(r64!( 3/2).ceil(), r64(2));
assert_eq!(r64!( 2/1).ceil(), r64(2));
assert_eq!(r64!(-3/2).ceil(), r64!(-1));
assert_eq!(r64!(-2/1).ceil(), r64!(-2));
}
#[test]
fn round() {
assert_eq!(r64(1).round(), r64(1));
assert_eq!(r64!(-1).round(), r64!(-1));
assert_eq!(r64!( 3/2).round(), r64(2));
assert_eq!(r64!(-3/2).round(), r64!(-2));
}
#[test]
fn min() {
assert_eq!(r64::NAN.min(r64::NAN), r64::NAN);
assert_eq!(r64::NAN.min(r64(0)), r64(0));
assert_eq!(r64(0).min(r64::NAN), r64(0));
assert_eq!(r64(0).min(r64(1)), r64(0));
}
#[test]
fn max() {
assert_eq!(r64::NAN.max(r64::NAN), r64::NAN);
assert_eq!(r64::NAN.max(r64(0)), r64(0));
assert_eq!(r64(0).max(r64::NAN), r64(0));
assert_eq!(r64(0).max(r64(1)), r64(1));
}
#[test]
fn recip() {
assert_eq!(r64(5).recip(), r64!(1/5));
assert_eq!(r64!(5/2).recip(), r64!(2/5));
assert_eq!(r64(1).recip(), r64(1));
}
#[test]
fn cmp() {
assert!(r64(0) == r64(0));
assert!(r64(0) < r64(1));
assert!(r64(2) < r64(3));
assert!(r64(0) > -r64(1));
assert!(r64(2) > -r64(3));
}
#[test]
fn mul() {
assert_eq!(r64(0) * r64(0), r64(0));
assert_eq!(r64(0) * r64(1), r64(0));
assert_eq!(r64(1) * r64(0), r64(0));
assert_eq!(r64(1) * r64(1), r64(1));
assert_eq!(-r64(1) * r64(1), -r64(1));
assert_eq!( r64(1) * -r64(1), -r64(1));
assert_eq!(-r64(1) * -r64(1), r64(1));
assert_eq!(r64(1) * r64(2), r64(2));
assert_eq!(r64(2) * r64(2), r64(4));
assert_eq!(
r64!(1/2) * r64!(1/2), r64!(1/4)
);
assert_eq!(
r64!(-1/2) * r64!(1/2), r64!(-1/4)
);
assert_eq!(
r64!(2/3) * r64!(2/3), r64!(4/9)
);
assert_eq!(
r64!(3/2) * r64!(2/3), r64(1)
);
}
#[test] #[should_panic]
fn mul_invalid() {
let _ = r64(1 << FRACTION_SIZE - 1) * r64(1 << FRACTION_SIZE - 1);
}
#[test]
fn div() {
assert_eq!(r64(0) / r64(1), r64(0));
assert_eq!(r64(0) / r64(2), r64(0));
assert_eq!(r64(1) / r64(1), r64(1));
assert_eq!(-r64(1) / r64(1), -r64(1));
assert_eq!(r64(1) / -r64(1), -r64(1));
assert_eq!(-r64(1) / -r64(1), r64(1));
assert_eq!(r64(1) / r64(2), r64!(1/2));
assert_eq!(r64(2) / r64(1), r64(2));
assert_eq!(r64(2) / r64(2), r64(1));
}
#[test]
fn rem() {
assert_eq!(r64(5) % r64(2), r64(1));
assert_eq!(r64(6) % r64(2), r64(0));
assert_eq!(r64(8) % r64!(3/2), r64!(1/2));
assert_eq!(-r64(5) % r64(2), r64(1));
assert_eq!( r64(5) % -r64(2), -r64(1));
assert_eq!(-r64(5) % -r64(2), -r64(1));
}
#[test]
fn add() {
assert_eq!(r64(0) + r64(0), r64(0));
assert_eq!(-r64(0) + r64(0), r64(0));
assert_eq!( r64(1) + r64(1), r64(2));
assert_eq!( r64(1) + -r64(1), r64(0));
assert_eq!(-r64(1) + r64(1), r64(0));
assert_eq!(-r64(1) + -r64(1), -r64(2));
assert_eq!(r64(2) + r64(2), r64(4));
assert_eq!(
r64!(1/2) + r64!(3/4), r64!(5/4)
);
assert_eq!(
r64!(1/2) + r64!(-3/4), r64!(-1/4)
);
assert_eq!(
r64!(-1/2) + r64!(3/4), r64!(1/4)
);
}
#[test] #[should_panic]
fn add_invalid() {
let _ = r64(1 << FRACTION_SIZE - 1) + r64(1 << FRACTION_SIZE - 1);
}
#[test]
fn from_str() {
assert_eq!("0".parse::<r64>().unwrap(), r64(0));
assert_eq!("1".parse::<r64>().unwrap(), r64(1));
assert_eq!("+1".parse::<r64>().unwrap(), r64(1));
assert_eq!("-1".parse::<r64>().unwrap(), r64!(-1));
assert_eq!("1/1".parse::<r64>().unwrap(), r64(1));
}
#[test] #[should_panic]
fn from_str_fail() {
"1/-1".parse::<r64>().unwrap();
"/1".parse::<r64>().unwrap();
"1/".parse::<r64>().unwrap();
"1/0".parse::<r64>().unwrap();
}
#[test]
fn from_f32() {
}
}