#[cfg(feature = "try_from")]
use crate::number::rational::Rationals;
use crate::{
error::{NumeraErrors, NumeraResult, RationalErrors},
number::{
integer::*,
macros::impl_larger_smaller,
traits::{
Bound, ConstLowerBounded, ConstNegOne, ConstOne, ConstUpperBounded, ConstZero, Count,
Countable, Ident, LowerBounded, NegOne, Negative, Number, One, Positive, Sign,
UpperBounded, Zero,
},
},
};
use core::{
cmp::Ordering,
fmt,
hash::{Hash, Hasher},
num::{NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8},
};
use devela::paste;
macro_rules! define_rational_sized {
(multi $name:ident, $abbr:ident,
$numi:ident, $numim:ident, $deni:ident, $denim:ident,
$doc_num:literal, $doc_type:literal, // $doc_new:literal,
$doc_sign:literal, $doc_lower:expr, $doc_upper:expr,
$num:ident, $den:ident,
$(
(
$doc_det:literal, $b:expr,
larger: $larger:literal, $larger_b:literal,
smaller: $smaller:literal, $smaller_b:literal
)
),+
) => {
$(
define_rational_sized![single $name, $abbr,
$numi, $numim, $deni, $denim,
$doc_num, $doc_type, // $doc_new,
$doc_sign, $doc_lower, $doc_upper,
$num, $den,
($doc_det, $b,
larger: $larger, $larger_b,
smaller: $smaller, $smaller_b
)];
)+
};
(single $name:ident, $abbr:ident,
$numi:ident, $numim:ident, $deni:ident, $denim:ident,
$doc_num:literal, $doc_type:literal, $doc_sign:literal, $doc_lower:expr, $doc_upper:expr,
$num:ident, $den:ident,
(
$doc_det:literal, $b:expr,
larger: $larger:literal, $larger_b:literal,
smaller: $smaller:literal, $smaller_b:literal
)
) => { paste! {
#[doc = $doc_det " "$b "-bit " $doc_num $doc_type ","]
#[doc = "also known as [`" [<$abbr$b>] "`][super::" [<$abbr$b>] "]."]
#[doc = "\n\nThe range of valid numeric values is $\\lbrack"
$doc_sign "$[`" $numim$b "::" $doc_lower "`] $\\dots$ [`"
$denim$b "::" $doc_upper "`]$\\rbrack$."]
#[derive(Clone, Copy)]
pub struct [<$name$b>] {
pub num: [<$num$b>],
pub den: [<$den$b>],
}
impl Default for [<$name$b>] {
fn default() -> Self {
Self {
num: [<$num$b>]::ZERO,
den: [<$den$b>]::ONE,
}
}
}
impl Hash for [<$name$b>] {
fn hash<H: Hasher>(&self, state: &mut H) {
self.num.hash(state);
self.den.hash(state);
}
}
impl PartialEq for [<$name$b>] {
fn eq(&self, other: &Self) -> bool {
let uself = self.as_larger_or_same();
let uother = other.as_larger_or_same();
uself.num * uother.den.into() == uother.num * uself.den.into()
}
}
impl Eq for [<$name$b>] {}
impl PartialOrd for [<$name$b>] {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
let uself = self.as_larger_or_same();
let uother = other.as_larger_or_same();
let lhs = uself.num * uother.den.into();
let rhs = uother.num * uself.den.into();
lhs.partial_cmp(&rhs)
}
}
impl Ord for [<$name$b>] {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(&other).unwrap()
}
}
impl fmt::Display for [<$name$b>] {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}/{}", self.num, self.den)
}
}
impl fmt::Debug for [<$name$b>] {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}({}/{})", stringify!([<$abbr$b>]), self.num, self.den)
}
}
impl [<$name$b>] {
#[doc = "Returns a new `" [<$name$b>] "`."]
#[inline]
pub const fn new(numerator: [<i$b>], denominator: [<i$b>])
-> NumeraResult<Self> {
if let Ok(den) = [<$den$b>]::new(denominator) {
let num = [<$num$b>]::new(numerator);
Ok(Self{ num, den })
} else {
Err(NumeraErrors::Rational(RationalErrors::ZeroDenominator))
}
}
#[doc = "Returns a new `" [<$name$b>] "`."]
#[inline]
#[cfg(not(feature = "safe"))]
#[cfg_attr(feature = "nightly", doc(cfg(feature = "not(safe)")))]
pub const unsafe fn new_unchecked(numerator: [<i$b>], denominator: [<i$b>])
-> Self {
debug_assert![denominator != 0];
Self {
num: [<$num$b>]::new(numerator),
den: [<$den$b>]::new_unchecked(denominator),
}
}
}
impl_larger_smaller![$name, $b, Rationals,
larger: $larger, $larger_b, smaller: $smaller, $smaller_b
];
impl Sign for [<$name$b>] {
#[inline]
fn can_negative(&self) -> bool { true }
#[inline]
fn can_positive(&self) -> bool { true }
#[inline]
fn is_negative(&self) -> bool {
self.num.is_negative() && self.den.is_positive() ||
self.num.is_positive() && self.den.is_negative()
}
#[inline]
fn is_positive(&self) -> bool {
self.num.is_negative() && self.den.is_negative() ||
self.num.is_positive() && self.den.is_positive()
}
}
impl Positive for [<$name$b>] {}
impl Negative for [<$name$b>] {}
impl Bound for [<$name$b>] {
#[inline]
fn is_lower_bounded(&self) -> bool { true }
#[inline]
fn is_upper_bounded(&self) -> bool { true }
#[inline]
fn lower_bound(&self) -> Option<Self> {
Some(<Self as ConstLowerBounded>::MIN)
}
#[inline]
fn upper_bound(&self) -> Option<Self> {
Some(<Self as ConstUpperBounded>::MAX)
}
}
impl LowerBounded for [<$name$b>] {
#[inline]
fn new_min() -> Self { <Self as ConstLowerBounded>::MIN }
}
impl UpperBounded for [<$name$b>] {
#[inline]
fn new_max() -> Self { <Self as ConstUpperBounded>::MAX }
}
impl ConstLowerBounded for [<$name$b>] {
const MIN: Self = Self {
num: [<$num$b>]::MIN,
den: [<$den$b>]::ONE,
};
}
impl ConstUpperBounded for [<$name$b>] {
const MAX: Self = Self {
num: [<$num$b>]::MAX,
den: [<$den$b>]::ONE,
};
}
impl Count for [<$name$b>] {
#[inline]
fn is_countable(&self) -> bool { true }
}
impl Countable for [<$name$b>] {
#[inline]
fn next(&self) -> NumeraResult<Self> {
Ok(Self {
num: self.num.0.checked_add(1)
.ok_or(RationalErrors::NumeratorOverflow)?.into(),
den: self.den,
})
}
#[inline]
fn previous(&self) -> NumeraResult<Self> {
Ok(Self {
num: self.num.0.checked_sub(1)
.ok_or(RationalErrors::NumeratorUnderflow)?.into(),
den: self.den,
})
}
}
impl Ident for [<$name$b>] {
#[inline]
fn can_zero(&self) -> bool { true }
#[inline]
fn can_one(&self) -> bool { true }
#[inline]
fn can_neg_one(&self) -> bool { true }
#[inline]
fn is_zero(&self) -> bool { self.num.is_zero() }
#[inline]
fn is_one(&self) -> bool { self.num == self.den.into() }
#[inline]
fn is_neg_one(&self) -> bool { self.num.neg() == self.den.into() }
}
impl ConstZero for [<$name$b>] {
const ZERO: Self = Self {
num: [<$num$b>]::ZERO,
den: [<$den$b>]::ONE,
};
}
impl Zero for [<$name$b>] {
#[inline]
fn new_zero() -> Self { <Self as ConstZero>::ZERO }
}
impl ConstOne for [<$name$b>] {
const ONE: Self = Self {
num: [<$num$b>]::ONE,
den: [<$den$b>]::ONE,
};
}
impl One for [<$name$b>] {
#[inline]
fn new_one() -> Self { <Self as ConstOne>::ONE }
}
impl ConstNegOne for [<$name$b>] {
const NEG_ONE: Self = Self {
num: [<$num$b>]::NEG_ONE,
den: [<$den$b>]::ONE,
};
}
impl NegOne for [<$name$b>] {
#[inline]
fn new_neg_one() -> Self { <Self as ConstNegOne>::NEG_ONE }
}
impl Number for [<$name$b>] {
type InnerRepr = ([<$numi$b>], [<$deni$b>]);
type InnermostRepr = ([<$numim$b>], [<$denim$b>]);
#[inline]
fn from_inner_repr(value: Self::InnerRepr) -> NumeraResult<Self> {
Ok(
Self {
num: [<$num$b>]::from_inner_repr(value.0)?,
den: [<$den$b>]::from_inner_repr(value.1)
.map_err(|_| RationalErrors::ZeroDenominator)?,
}
)
}
#[inline]
#[cfg(not(feature = "safe"))]
#[cfg_attr(feature = "nightly", doc(cfg(feature = "not(safe)")))]
unsafe fn from_inner_repr_unchecked(value: Self::InnerRepr) -> Self {
Self {
num: [<$num$b>]::from_inner_repr_unchecked(value.0),
den: [<$den$b>]::from_inner_repr_unchecked(value.1),
}
}
#[inline]
fn from_innermost_repr(value: Self::InnermostRepr) -> NumeraResult<Self> {
Ok(
Self {
num: [<$num$b>]::from_innermost_repr(value.0)?,
den: [<$den$b>]::from_innermost_repr(value.1)
.map_err(|_| RationalErrors::ZeroDenominator)?,
}
)
}
#[inline]
#[cfg(not(feature = "safe"))]
#[cfg_attr(feature = "nightly", doc(cfg(feature = "not(safe)")))]
unsafe fn from_innermost_repr_unchecked(value: Self::InnermostRepr) -> Self {
debug_assert![value.1 != [<$numim$b>]::ZERO];
Self {
num: [<$num$b>]::from_innermost_repr_unchecked(value.0),
den: [<$den$b>]::from_innermost_repr_unchecked(value.1),
}
}
#[inline]
fn into_inner_repr(self) -> Self::InnerRepr { (self.num.into(), self.den.into()) }
#[inline]
fn into_innermost_repr(self) -> Self::InnermostRepr { (self.num.into(), self.den.into()) }
}
}};
}
define_rational_sized![multi Rational, Q,
i, i, NonZeroI, i,
"rational number", ", from the set $\\Bbb{Q}$",
"", MIN, MAX,
Integer, NonZeroInteger,
("A 2 ×", 8, larger: true, 16, smaller: false, 4),
("A 2 ×", 16, larger: true, 32, smaller: true, 8),
("A 2 ×", 32, larger: true, 64, smaller: true, 16),
("A 2 ×", 64, larger: true, 128, smaller: true, 32),
("A 2 ×", 128, larger: false, 256, smaller: true, 64)
];
#[cfg(test)]
mod tests {
use crate::all::*;
#[test]
fn q_define_sized() -> NumeraResult<()> {
assert_eq![
Rational8::from_innermost_repr((5, 0)),
Err(RationalErrors::ZeroDenominator.into())
];
let _q5 = Rational8::from_innermost_repr((5, 1))?;
#[cfg(feature = "std")]
assert_eq![_q5.to_string(), "5/1"];
assert![Q8::new(4, 2)? == Q8::new(4, 2)?]; assert![Q8::new(4, 2)? == Q8::new(2, 1)?]; assert![Q8::new(4, 2)? != Q8::new(3, 1)?];
assert![Q8::new(3, 2)? < Q8::new(4, 2)?];
assert![Q8::new(3, 2)? > Q8::new(3, 5)?];
Ok(())
}
}