use core::cmp::Ordering;
use core::fmt;
use crate::int::Int;
use crate::rational::Rational;
#[derive(Clone)]
pub enum InfRational {
Finite(Rational),
PosInf,
NegInf,
Nan,
}
impl InfRational {
#[inline]
pub fn finite(r: Rational) -> InfRational {
InfRational::Finite(r)
}
pub fn ratio(num: Int, den: Int) -> InfRational {
if den.is_zero() {
match num.signum() {
1 => InfRational::PosInf,
-1 => InfRational::NegInf,
_ => InfRational::Nan,
}
} else {
InfRational::Finite(Rational::new(num, den))
}
}
#[inline]
pub fn is_nan(&self) -> bool {
matches!(self, InfRational::Nan)
}
#[inline]
pub fn is_infinite(&self) -> bool {
matches!(self, InfRational::PosInf | InfRational::NegInf)
}
#[inline]
pub fn is_finite(&self) -> bool {
matches!(self, InfRational::Finite(_))
}
#[inline]
pub fn is_zero(&self) -> bool {
matches!(self, InfRational::Finite(r) if r.is_zero())
}
fn ext_sign(&self) -> i32 {
match self {
InfRational::PosInf => 1,
InfRational::NegInf => -1,
InfRational::Nan => 0,
InfRational::Finite(r) => r.signum(),
}
}
pub fn to_rational(&self) -> Option<&Rational> {
match self {
InfRational::Finite(r) => Some(r),
_ => None,
}
}
pub fn neg(&self) -> InfRational {
match self {
InfRational::Finite(r) => InfRational::Finite(r.neg()),
InfRational::PosInf => InfRational::NegInf,
InfRational::NegInf => InfRational::PosInf,
InfRational::Nan => InfRational::Nan,
}
}
pub fn abs(&self) -> InfRational {
match self {
InfRational::Finite(r) => InfRational::Finite(r.abs()),
InfRational::PosInf | InfRational::NegInf => InfRational::PosInf,
InfRational::Nan => InfRational::Nan,
}
}
pub fn add(&self, rhs: &InfRational) -> InfRational {
use InfRational::*;
match (self, rhs) {
(Nan, _) | (_, Nan) => Nan,
(PosInf, NegInf) | (NegInf, PosInf) => Nan,
(PosInf, _) | (_, PosInf) => PosInf,
(NegInf, _) | (_, NegInf) => NegInf,
(Finite(a), Finite(b)) => Finite(a.add(b)),
}
}
pub fn sub(&self, rhs: &InfRational) -> InfRational {
self.add(&rhs.neg())
}
pub fn mul(&self, rhs: &InfRational) -> InfRational {
use InfRational::*;
if self.is_nan() || rhs.is_nan() {
return Nan;
}
if let (Finite(a), Finite(b)) = (self, rhs) {
return Finite(a.mul(b));
}
if self.is_zero() || rhs.is_zero() {
return Nan;
}
if self.ext_sign() * rhs.ext_sign() > 0 {
PosInf
} else {
NegInf
}
}
pub fn div(&self, rhs: &InfRational) -> InfRational {
use InfRational::*;
match (self, rhs) {
(Nan, _) | (_, Nan) => Nan,
(PosInf | NegInf, PosInf | NegInf) => Nan,
(PosInf | NegInf, Finite(b)) => {
if self.ext_sign() * finite_sign(b) >= 0 {
PosInf
} else {
NegInf
}
}
(Finite(_), PosInf | NegInf) => Finite(Rational::ZERO),
(Finite(a), Finite(b)) => {
if b.is_zero() {
match a.signum() {
1 => PosInf,
-1 => NegInf,
_ => Nan, }
} else {
Finite(a.div(b))
}
}
}
}
pub fn recip(&self) -> InfRational {
InfRational::Finite(Rational::ONE).div(self)
}
}
fn finite_sign(r: &Rational) -> i32 {
if r.is_negative() { -1 } else { 1 }
}
impl PartialEq for InfRational {
fn eq(&self, other: &Self) -> bool {
use InfRational::*;
match (self, other) {
(Finite(a), Finite(b)) => a == b,
(PosInf, PosInf) | (NegInf, NegInf) => true,
_ => false, }
}
}
impl PartialOrd for InfRational {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
use InfRational::*;
match (self, other) {
(Nan, _) | (_, Nan) => None,
(Finite(a), Finite(b)) => Some(a.cmp(b)),
(NegInf, NegInf) | (PosInf, PosInf) => Some(Ordering::Equal),
(NegInf, _) | (_, PosInf) => Some(Ordering::Less),
(PosInf, _) | (_, NegInf) => Some(Ordering::Greater),
}
}
}
impl From<Rational> for InfRational {
#[inline]
fn from(r: Rational) -> InfRational {
InfRational::Finite(r)
}
}
impl From<Int> for InfRational {
#[inline]
fn from(n: Int) -> InfRational {
InfRational::Finite(Rational::from_integer(n))
}
}
impl core::str::FromStr for InfRational {
type Err = crate::error::Error;
fn from_str(s: &str) -> crate::error::Result<InfRational> {
let t = s.trim();
if t.eq_ignore_ascii_case("nan") {
Ok(InfRational::Nan)
} else if t.eq_ignore_ascii_case("inf") || t.eq_ignore_ascii_case("+inf") {
Ok(InfRational::PosInf)
} else if t.eq_ignore_ascii_case("-inf") {
Ok(InfRational::NegInf)
} else {
Ok(InfRational::Finite(t.parse()?))
}
}
}
impl fmt::Display for InfRational {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
InfRational::Finite(r) => fmt::Display::fmt(r, f),
InfRational::PosInf => f.write_str("inf"),
InfRational::NegInf => f.write_str("-inf"),
InfRational::Nan => f.write_str("NaN"),
}
}
}
impl fmt::Debug for InfRational {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "InfRational({self})")
}
}
macro_rules! inf_binop {
($tr:ident, $m:ident, $atr:ident, $am:ident) => {
impl core::ops::$tr for InfRational {
type Output = InfRational;
#[inline]
fn $m(self, rhs: InfRational) -> InfRational {
InfRational::$m(&self, &rhs)
}
}
impl core::ops::$tr<&InfRational> for &InfRational {
type Output = InfRational;
#[inline]
fn $m(self, rhs: &InfRational) -> InfRational {
InfRational::$m(self, rhs)
}
}
impl core::ops::$atr for InfRational {
#[inline]
fn $am(&mut self, rhs: InfRational) {
*self = InfRational::$m(self, &rhs);
}
}
};
}
inf_binop!(Add, add, AddAssign, add_assign);
inf_binop!(Sub, sub, SubAssign, sub_assign);
inf_binop!(Mul, mul, MulAssign, mul_assign);
inf_binop!(Div, div, DivAssign, div_assign);
impl core::ops::Neg for InfRational {
type Output = InfRational;
#[inline]
fn neg(self) -> InfRational {
InfRational::neg(&self)
}
}