#![no_std]
#![cfg_attr(feature = "bench", feature(test))]
#[cfg(feature = "std")]
extern crate std;
use core::fmt;
use core::num::ParseIntError;
mod r32_t;
mod r64_t;
pub use r32_t::r32;
pub use r64_t::r64;
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ParseRatioErr {
Empty,
Overflow,
Numerator(ParseIntError),
Denominator(ParseIntError),
}
impl fmt::Display for ParseRatioErr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ParseRatioErr::Empty =>
f.write_str("cannot parse rational from empty string"),
ParseRatioErr::Overflow =>
f.write_str("numbers are too large to fit in fraction"),
ParseRatioErr::Numerator(pie) =>
write!(f, "numerator error: {}", pie),
ParseRatioErr::Denominator(pie) =>
write!(f, "denominator error: {}", pie),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseRatioErr {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
ParseRatioErr::Numerator(pie) => Some(pie),
ParseRatioErr::Denominator(pie) => Some(pie),
_ => None
}
}
}
#[macro_export]
macro_rules! r32 {
($numer:literal) => { r32!($numer / 1) };
($numer:literal / $denom:literal) => {
r32::new($numer, $denom).expect("literal out of range for `r32`")
};
}
#[macro_export]
macro_rules! r64 {
($numer:literal) => { r64!($numer / 1) };
($numer:literal / $denom:literal) => {
r64::new($numer, $denom).expect("literal out of range for `r64`")
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! impl_ratio_type {
($name:ident $uint:ident $int:ident $nzuint:ident) => {
impl $name {
pub const MAX: $name = $name(1 << (FRACTION_SIZE - 1));
pub const MIN: $name = $name(1 << FRACTION_SIZE);
pub const MIN_POSITIVE: $name = $name(FRACTION_SIZE << FRACTION_SIZE | FRACTION_FIELD);
pub const NAN: $name = $name($uint::MAX);
#[inline]
fn denom_size(self) -> $uint {
self.0 >> FRACTION_SIZE
}
#[inline]
fn denom_mask(self) -> $uint {
(1 << self.denom_size()) - 1
}
#[inline]
fn numer_mask(self) -> $uint {
FRACTION_FIELD & !self.denom_mask()
}
#[inline]
pub(crate) fn numer(self) -> $int {
(self.0 as $int)
.wrapping_shl(DSIZE_SIZE)
.wrapping_shr(DSIZE_SIZE + (self.denom_size() as u32))
}
#[inline]
pub(crate) fn denom(self) -> $uint {
1 << self.denom_size() | (self.0 & self.denom_mask())
}
#[inline]
pub fn is_nan(self) -> bool {
self.denom_size() >= FRACTION_SIZE
}
#[inline]
pub fn is_positive(self) -> bool {
!self.is_nan() && self.numer().is_positive()
}
#[inline]
pub fn is_negative(self) -> bool {
!self.is_nan() && self.numer().is_negative()
}
#[inline]
pub fn trunc(self) -> $name {
if self.is_nan() { return self }
let numer = self.numer() / (self.denom() as $int);
$name((numer as $uint) & FRACTION_FIELD)
}
#[inline]
pub fn fract(self) -> $name {
if self.is_nan() { return self }
let numer = (self.numer() % (self.denom() as $int)) as $uint;
$name(
self.0 & !self.numer_mask()
| (numer << self.denom_size()) & FRACTION_FIELD
)
}
pub fn floor(self) -> $name {
if self.is_negative() {
if self.numer() % (self.denom() as $int) == 0 {
self
} else {
self.trunc() - $name(1)
}
} else {
self.trunc()
}
}
pub fn ceil(self) -> $name {
if self.is_positive() {
if self.numer() % (self.denom() as $int) == 0 {
self
} else {
self.trunc() + $name(1)
}
} else {
self.trunc()
}
}
pub fn round(self) -> $name {
if self.is_negative() {
unsafe { self - $name::new_unchecked(1, 2) }
} else if self.is_positive() {
unsafe { self + $name::new_unchecked(1, 2) }
} else {
self
}
.trunc()
}
#[inline]
pub fn abs(self) -> $name {
if self.is_negative() {
-self
} else {
self
}
}
#[inline]
pub fn signum(self) -> $name {
if self.is_nan() {
self
} else if self.is_negative() {
unsafe { $name::new_unchecked(-1, 1) }
} else if self.is_positive() {
$name(1)
} else {
$name(0)
}
}
#[inline]
pub fn recip(self) -> $name {
self.checked_recip().expect("attempt to divide by zero")
}
pub fn normalize(self) -> $name {
if self.is_nan() { return self }
if self.numer() == 0 {
return $name(0);
}
let n = self.numer();
let d = self.denom();
let gcd = n.unsigned_abs().gcd(d);
unsafe { $name::new_unchecked(n / (gcd as $int), d / gcd) }
}
pub fn pow(self, exp: i32) -> $name {
if exp == 0 { return $name(1) }
if self.is_nan() { return self }
let exp_is_neg = exp < 0;
let exp = exp.unsigned_abs();
let num = self.numer().pow(exp);
let den = self.denom().pow(exp);
if exp_is_neg {
$name::new(num, den).map($name::recip)
} else {
$name::new(num, den)
}
.expect("attempt to multiply with overflow")
}
pub fn max(self, other: $name) -> $name {
match (self.is_nan(), other.is_nan()) {
(true, true) => $name::NAN,
(true, false) => other,
(false, true) => self,
(false, false) => match self.partial_cmp(&other).unwrap() {
Ordering::Less => other,
_ => self
}
}
}
pub fn min(self, other: $name) -> $name {
match (self.is_nan(), other.is_nan()) {
(true, true) => $name::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<$name> {
if self.is_nan() { return Some(self) }
$name::new(-self.numer(), self.denom())
}
#[inline]
pub fn checked_abs(self) -> Option<$name> {
if self.is_negative() {
self.checked_neg()
} else {
Some(self)
}
}
pub fn checked_recip(self) -> Option<$name> {
if self.is_nan() {
Some(self)
} else if self.numer() == 0 {
None
} else {
let mut denom = self.denom() as $int;
if self.is_negative() { denom = -denom }
$name::new(denom, self.numer().unsigned_abs())
}
}
pub fn checked_pow(self, exp: i32) -> Option<$name> {
if exp == 0 { return Some($name(1)) }
if self.is_nan() { return Some($name::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 {
$name::new(num, den)?.checked_recip()
} else {
$name::new(num, den)
}
}
pub fn checked_sub(self, rhs: $name) -> Option<$name> {
self.checked_add(rhs.checked_neg()?)
}
#[inline]
pub fn checked_div(self, rhs: $name) -> Option<$name> {
self.checked_mul(rhs.checked_recip()?)
}
#[inline]
pub fn checked_rem(self, rhs: $name) -> Option<$name> {
let div = self.checked_div(rhs)?;
div.checked_sub(div.floor())?.checked_mul(rhs)
}
#[inline]
pub fn to_bits(self) -> $uint { self.0 }
#[inline]
pub fn from_bits(bits: $uint) -> $name { $name(bits) }
}
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.is_nan() {
return f.write_str("NaN");
}
let norm = self.normalize();
norm.numer().fmt(f)?;
if norm.denom_size() > 0 {
write!(f, "/{}", norm.denom())?;
}
Ok(())
}
}
impl fmt::Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.is_nan() {
f.write_str("NaN")
} else {
write!(f, "{}/{}", self.numer(), self.denom())
}
}
}
impl FromStr for $name {
type Err = ParseRatioErr;
fn from_str(src: &str) -> Result<Self, Self::Err> {
use core::num::$nzuint;
if src.is_empty() {
return Err(ParseRatioErr::Empty);
}
if src == "NaN" {
return Ok($name::NAN);
}
let bar_pos = src.find('/');
let numer_end = bar_pos.unwrap_or(src.len());
let numerator = src[..numer_end]
.parse::<$int>()
.map_err(ParseRatioErr::Numerator)?;
let denominator = bar_pos
.map(|pos|
src[pos+1..]
.parse::<$nzuint>()
.map_err(ParseRatioErr::Denominator)
) .transpose()?
.map($nzuint::get)
.unwrap_or(1);
$name::new(numerator, denominator)
.ok_or(ParseRatioErr::Overflow)
}
}
impl From<u8> for $name {
#[inline]
fn from(v: u8) -> Self { $name(v as $uint) }
}
impl From<i8> for $name {
fn from(v: i8) -> Self {
unsafe { $name::new_unchecked(v as $int, 1) }
}
}
impl PartialEq for $name {
fn eq(&self, other: &$name) -> bool {
self.is_nan() && other.is_nan()
|| self.normalize().0 == other.normalize().0
}
}
impl Neg for $name {
type Output = $name;
fn neg(self) -> Self::Output {
self.checked_neg().expect("attempt to negate with overflow")
}
}
impl Add for $name {
type Output = $name;
fn add(self, other: $name) -> Self::Output {
self.checked_add(other).expect("attempt to add with overflow")
}
}
impl AddAssign for $name {
fn add_assign(&mut self, other: $name) {
*self = *self + other
}
}
impl Sub for $name {
type Output = $name;
fn sub(self, other: $name) -> Self::Output {
self.checked_sub(other).expect("attempt to subtract with overflow")
}
}
impl SubAssign for $name {
fn sub_assign(&mut self, other: $name) {
*self = *self - other
}
}
impl Mul for $name {
type Output = $name;
fn mul(self, other: $name) -> Self::Output {
self.checked_mul(other).expect("attempt to multiply with overflow")
}
}
impl MulAssign for $name {
fn mul_assign(&mut self, other: $name) {
*self = *self * other
}
}
impl Div for $name {
type Output = $name;
fn div(self, other: $name) -> Self::Output {
self.checked_div(other).expect("attempt to divide with overflow")
}
}
impl DivAssign for $name {
fn div_assign(&mut self, other: $name) {
*self = *self / other
}
}
impl Rem for $name {
type Output = $name;
fn rem(self, other: $name) -> Self::Output {
self.checked_rem(other).expect("attempt to divide with overflow")
}
}
impl RemAssign for $name {
fn rem_assign(&mut self, other: $name) {
*self = *self % other
}
}
} }