use core::fmt;
use core::cmp::Ordering;
use core::convert::TryFrom;
use core::ops::*;
use core::str::FromStr;
use gcd::Gcd;
use super::{ParseRatioErr, TryFromRatioError};
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Eq, Default)]
pub struct r32(u32);
const DSIZE_SIZE: u32 = 5;
const FRACTION_SIZE: u32 = 27;
const FRACTION_FIELD: u32 = (1 << FRACTION_SIZE) - 1;
impl r32 {
pub const MAX: r32 = r32((1 << (FRACTION_SIZE - 1)) - 1);
pub const MIN: r32 = r32(1 << (FRACTION_SIZE - 1));
pub const MIN_POSITIVE: r32 = r32(FRACTION_SIZE << FRACTION_SIZE | FRACTION_FIELD);
pub const NAN: r32 = r32(u32::MAX);
#[inline]
const fn denom_size(self) -> u32 {
self.0 >> FRACTION_SIZE
}
#[inline]
const fn denom_mask(self) -> u32 {
(1 << self.denom_size()) - 1
}
#[inline]
const fn numer_mask(self) -> u32 {
FRACTION_FIELD & !self.denom_mask()
}
#[inline]
fn get_frac_size(n: i64, d: u64) -> u32 {
let dsize = 64 - d.leading_zeros() - 1;
let nsize = if n >= 0 {
64 - n.leading_zeros() + 1
} else {
64 - n.leading_ones() + 1
};
nsize + dsize
}
#[inline]
pub(crate) const fn numer(self) -> i32 {
(self.0 as i32)
.wrapping_shl(DSIZE_SIZE)
.wrapping_shr(DSIZE_SIZE + (self.denom_size() as u32))
}
#[inline]
pub(crate) const fn denom(self) -> u32 {
1 << self.denom_size() | (self.0 & self.denom_mask())
}
#[inline]
pub const unsafe fn new_unchecked(numer: i32, denom: u32) -> r32 {
let denom_size = 32 - denom.leading_zeros() - 1;
let denom_mask = (1 << denom_size as u32) - 1;
let numer_mask = FRACTION_FIELD & !denom_mask;
r32(
(denom_size as u32) << FRACTION_SIZE |
((numer << denom_size) as u32) & numer_mask |
denom & denom_mask
)
}
pub const fn new(numer: i32, denom: u32) -> Option<r32> {
if denom == 0 { return None }
let denom_size = 32 - denom.leading_zeros() - 1;
let numer_size = if numer >= 0 {
32 - numer.leading_zeros() + 1
} else {
32 - numer.leading_ones() + 1
};
if numer_size + denom_size > FRACTION_SIZE as u32 {
return None;
}
unsafe {
Some(r32::new_unchecked(numer, denom))
}
}
pub fn new_reduced(mut numer: i32, mut denom: u32) -> Option<r32> {
let gcd = numer.unsigned_abs().gcd(denom);
numer /= gcd as i32;
denom /= gcd;
r32::new(numer, denom)
}
#[inline]
pub const fn is_nan(self) -> bool {
self.denom_size() >= FRACTION_SIZE
}
#[inline]
pub const fn is_positive(self) -> bool {
!self.is_nan() && self.numer().is_positive()
}
#[inline]
pub const fn is_negative(self) -> bool {
!self.is_nan() && self.numer().is_negative()
}
#[inline]
pub fn trunc(self) -> r32 {
if self.is_nan() { return self }
let numer = self.numer() / (self.denom() as i32);
r32((numer as u32) & FRACTION_FIELD)
}
#[inline]
pub fn fract(self) -> r32 {
if self.is_nan() { return self }
let numer = (self.numer() % (self.denom() as i32)) as u32;
r32(
self.0 & !self.numer_mask()
| (numer << self.denom_size()) & FRACTION_FIELD
)
}
#[inline]
pub fn floor(self) -> r32 {
if self.is_negative() {
if self.numer() % (self.denom() as i32) == 0 {
self
} else {
self.trunc() - r32(1)
}
} else {
self.trunc()
}
}
#[inline]
pub fn ceil(self) -> r32 {
if self.is_positive() {
if self.numer() % (self.denom() as i32) == 0 {
self
} else {
self.trunc() + r32(1)
}
} else {
self.trunc()
}
}
#[inline]
pub fn round(self) -> r32 {
if self.is_negative() {
unsafe { self - r32::new_unchecked(1, 2) }
} else if self.is_positive() {
unsafe { self + r32::new_unchecked(1, 2) }
} else {
self
}
.trunc()
}
#[inline]
pub fn abs(self) -> r32 {
if self.is_negative() {
-self
} else {
self
}
}
#[inline]
pub fn signum(self) -> r32 {
if self.is_nan() {
self
} else if self.is_negative() {
unsafe { r32::new_unchecked(-1, 1) }
} else if self.is_positive() {
r32(1)
} else {
r32(0)
}
}
#[inline]
pub fn recip(self) -> r32 {
self.checked_recip().expect("attempt to divide by zero")
}
#[inline]
pub fn normalize(self) -> r32 {
if self.is_nan() { return self }
let n = self.numer();
let d = self.denom();
let gcd = n.unsigned_abs().gcd(d);
unsafe {
r32::new_unchecked(n / (gcd as i32), d / gcd)
}
}
#[inline]
pub fn pow(self, exp: i32) -> r32 {
self.checked_pow(exp).expect("attempt to multiply with overflow")
}
pub fn max(self, other: r32) -> r32 {
match (self.is_nan(), other.is_nan()) {
(true, true) => r32::NAN,
(true, false) => other,
(false, true) => self,
(false, false) => match self.partial_cmp(&other).unwrap() {
Ordering::Less => other,
_ => self
}
}
}
pub fn min(self, other: r32) -> r32 {
match (self.is_nan(), other.is_nan()) {
(true, true) => r32::NAN,
(true, false) => other,
(false, true) => self,
(false, false) => match self.partial_cmp(&other).unwrap() {
Ordering::Greater => other,
_ => self
}
}
}
#[inline]
pub fn checked_neg(self) -> Option<r32> {
if self.is_nan() { return Some(self) }
r32::new(-self.numer(), self.denom())
}
#[inline]
pub fn checked_abs(self) -> Option<r32> {
if self.is_negative() {
self.checked_neg()
} else {
Some(self)
}
}
#[inline]
pub fn checked_recip(self) -> Option<r32> {
if self.is_nan() {
Some(self)
} else if self.numer() == 0 {
None
} else {
let mut denom = self.denom() as i32;
if self.is_negative() { denom = -denom }
r32::new(denom, self.numer().unsigned_abs())
}
}
pub fn checked_add(self, rhs: r32) -> Option<r32> {
match (self.is_nan(), rhs.is_nan()) {
(true, true) => return Some(r32::NAN),
(true, false) => return Some(self),
(false, true) => return Some(rhs),
_ => {}
}
let mut num =
self.numer() as i64 * rhs.denom() as i64
+ self.denom() as i64 * rhs.numer() as i64;
let mut den = self.denom() as u64 * rhs.denom() as u64;
let mut size = r32::get_frac_size(num, den);
if size > FRACTION_SIZE {
let gcd = num.unsigned_abs().gcd(den);
num /= gcd as i64;
den /= gcd;
size = r32::get_frac_size(num, den);
}
if size <= FRACTION_SIZE {
unsafe { Some(r32::new_unchecked(num as i32, den as u32)) }
} else {
None
}
}
pub fn checked_mul(self, rhs: r32) -> Option<r32> {
match (self.is_nan(), rhs.is_nan()) {
(true, true) => return Some(r32::NAN),
(true, false) => return Some(self),
(false, true) => return Some(rhs),
_ => {}
}
let mut n = self.numer() as i64 * rhs.numer() as i64;
let mut d = self.denom() as u64 * rhs.denom() as u64;
let mut size = r32::get_frac_size(n, d);
if size > FRACTION_SIZE {
let gcd = n.unsigned_abs().gcd(d);
n /= gcd as i64;
d /= gcd;
size = r32::get_frac_size(n, d);
}
if size <= FRACTION_SIZE {
unsafe { Some(r32::new_unchecked(n as i32, d as u32)) }
} else {
None
}
}
#[inline]
pub fn checked_sub(self, rhs: r32) -> Option<r32> {
self.checked_add(rhs.checked_neg()?)
}
#[inline]
pub fn checked_div(self, rhs: r32) -> Option<r32> {
self.checked_mul(rhs.checked_recip()?)
}
#[inline]
pub fn checked_rem(self, rhs: r32) -> Option<r32> {
let div = self.checked_div(rhs)?;
div.checked_sub(div.floor())?.checked_mul(rhs)
}
#[inline]
pub fn checked_pow(self, exp: i32) -> Option<r32> {
if exp == 0 { return Some(r32(1)) }
if self.is_nan() { return Some(r32::NAN) }
let exp_is_neg = exp < 0;
let exp = exp.unsigned_abs();
let num = self.numer().checked_pow(exp)?;
let den = self.denom().checked_pow(exp)?;
if exp_is_neg {
r32::new(num, den)?.checked_recip()
} else {
r32::new(num, den)
}
}
#[inline]
pub fn to_bits(self) -> u32 { self.0 }
#[inline]
pub fn from_bits(bits: u32) -> r32 { r32(bits) }
}
crate::impl_ratio_traits! { r32 u32 i32 NonZeroU32 }
impl Add for r32 {
type Output = r32;
#[inline]
fn add(self, rhs: r32) -> Self::Output {
if self.is_nan() || rhs.is_nan() {
return r32::NAN;
}
let mut num =
self.numer() * rhs.denom() as i32
+ self.denom() as i32 * rhs.numer();
let mut den = self.denom() * rhs.denom();
let mut frac_size = r32::get_frac_size(num as _, den as _);
if frac_size > FRACTION_SIZE {
let gcd = num.unsigned_abs().gcd(den);
num /= gcd as i32;
den /= gcd;
frac_size = r32::get_frac_size(num as _, den as _);
}
debug_assert!(
frac_size <= FRACTION_SIZE,
"attempt to add with overflow"
);
unsafe {
r32::new_unchecked(num, den)
}
}
}
impl Mul for r32 {
type Output = r32;
#[inline]
fn mul(self, rhs: r32) -> Self::Output {
if self.is_nan() || rhs.is_nan() {
return r32::NAN;
}
let mut num = self.numer() as i64 * rhs.numer() as i64;
let mut den = self.denom() as u64 * rhs.denom() as u64;
let frac_size = r32::get_frac_size(num as _, den as _);
if frac_size > FRACTION_SIZE {
let shift = num.trailing_zeros().min(den.trailing_zeros());
num >>= shift;
den >>= shift;
}
debug_assert!(
frac_size <= FRACTION_SIZE,
"attempt to multiply with overflow"
);
unsafe {
r32::new_unchecked(num as i32, den as u32)
}
}
}
impl From<u16> for r32 {
#[inline]
fn from(v: u16) -> Self { r32(v as u32) }
}
impl From<i16> for r32 {
#[inline]
fn from(v: i16) -> Self {
unsafe { r32::new_unchecked(v as i32, 1) }
}
}
impl TryFrom<r32> for u32 {
type Error = TryFromRatioError;
#[inline]
fn try_from(value: r32) -> Result<Self, Self::Error> {
let norm = value.normalize();
if norm.denom_size() == 0 && norm.numer() >= 0 {
Ok(norm.numer() as u32)
} else {
Err(TryFromRatioError)
}
}
}
impl TryFrom<r32> for i32 {
type Error = TryFromRatioError;
#[inline]
fn try_from(value: r32) -> Result<Self, Self::Error> {
let norm = value.normalize();
if norm.denom_size() == 0 {
Ok(norm.numer())
} else {
Err(TryFromRatioError)
}
}
}
impl TryFrom<r32> for u64 {
type Error = TryFromRatioError;
#[inline]
fn try_from(value: r32) -> Result<Self, Self::Error> {
let norm = value.normalize();
if norm.denom_size() == 0 && norm.numer() >= 0 {
Ok(norm.numer() as u64)
} else {
Err(TryFromRatioError)
}
}
}
impl TryFrom<r32> for i64 {
type Error = TryFromRatioError;
#[inline]
fn try_from(value: r32) -> Result<Self, Self::Error> {
let norm = value.normalize();
if norm.denom_size() == 0 {
Ok(norm.numer() as i64)
} else {
Err(TryFromRatioError)
}
}
}
impl TryFrom<r32> for u128 {
type Error = TryFromRatioError;
#[inline]
fn try_from(value: r32) -> Result<Self, Self::Error> {
let norm = value.normalize();
if norm.denom_size() == 0 && norm.numer() >= 0 {
Ok(norm.numer() as u128)
} else {
Err(TryFromRatioError)
}
}
}
impl TryFrom<r32> for i128 {
type Error = TryFromRatioError;
#[inline]
fn try_from(value: r32) -> Result<Self, Self::Error> {
let norm = value.normalize();
if norm.denom_size() == 0 {
Ok(norm.numer() as i128)
} else {
Err(TryFromRatioError)
}
}
}
impl From<f32> for r32 {
fn from(mut f: f32) -> Self {
if f.is_nan() || f.is_infinite() {
return r32::NAN;
}
let is_neg = f < 0.0;
if is_neg { f = f.abs() }
let (mut a, mut b) = (0, 1); let (mut c, mut d) = (1, 0);
let result = loop {
let p: i32 = a + c;
let q: u32 = b + d;
let lower_residual = f.mul_add(b as f32, -(a as f32)).abs();
let upper_residual = f.mul_add(d as f32, -(c as f32)).abs();
if r32::get_frac_size(p as i64, q as u64) > FRACTION_SIZE {
let lower_error = d as f32 * lower_residual;
let upper_error = b as f32 * upper_residual;
if lower_error <= upper_error {
break (a, b)
} else {
break (c, d)
}
}
match f.mul_add(q as f32, -(p as f32)).total_cmp(&0.0_f32) {
std::cmp::Ordering::Greater => {
let mut step = (lower_residual / upper_residual).floor() as u32;
assert!(step > 0);
(a, b) = loop {
if step == 1 {
break (p, q);
}
let na = a as i64 + step as i64 * c as i64;
let nb = b as u64 + step as u64 * d as u64;
if r32::get_frac_size(na, nb) <= FRACTION_SIZE {
break (na as i32, nb as u32);
}
step >>= 1;
}
},
std::cmp::Ordering::Less => {
let mut step = (upper_residual / lower_residual).floor() as u32;
assert!(step > 0);
(c, d) = loop {
if step == 1 {
break (p, q);
}
let nc = c as i64 + step as i64 * a as i64;
let nd = d as u64 + step as u64 * b as u64;
if r32::get_frac_size(nc, nd) <= FRACTION_SIZE {
break (nc as i32, nd as u32);
}
step >>= 1;
}
},
std::cmp::Ordering::Equal => break (p, q), }
};
unsafe {
if is_neg {
r32::new_unchecked(-result.0, result.1)
} else {
r32::new_unchecked(result.0, result.1)
}
}
}
}
impl From<r32> for f32 {
fn from(r: r32) -> f32 {
if r.is_nan() {
f32::NAN
} else {
(r.numer() as f32) / (r.denom() as f32)
}
}
}
impl From<r32> for f64 {
fn from(r: r32) -> f64 {
if r.is_nan() {
f64::NAN
} else {
(r.numer() as f64) / (r.denom() as f64)
}
}
}
impl PartialOrd for r32 {
fn partial_cmp(&self, other: &r32) -> Option<Ordering> {
if self.is_nan() && other.is_nan() {
return Some(Ordering::Equal);
}
if self.is_nan() || other.is_nan() {
return None;
}
Some(
(self.numer() as i64 * other.denom() as i64)
.cmp(&(self.denom() as i64 * other.numer() as i64))
)
}
}
crate::impl_ratio_tests!(r32);
#[test]
fn from_f32_chooses_closest_representable_fraction() {
for x in 0..23 {
assert_eq!(r32::from(<f32>::from(r32::new(1,1u32<<x).unwrap())), r32::new(1,1u32<<x).unwrap());
}
for x in 0..24 {
assert_eq!(r32::from(<f32>::from(r32::new(1i32<<x,1).unwrap())), r32::new(1i32<<x,1).unwrap());
}
}