use std::fmt;
use std::iter::Sum;
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use std::str::FromStr;
use num_bigint::BigInt;
use num_rational::BigRational;
use num_traits::{One, Signed, ToPrimitive, Zero};
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Rational(BigRational);
impl Rational {
pub fn new(numer: i64, denom: i64) -> Self {
assert!(denom != 0, "denominator is zero");
Rational(BigRational::new(BigInt::from(numer), BigInt::from(denom)))
}
pub fn zero() -> Self {
Rational(BigRational::zero())
}
pub fn one() -> Self {
Rational(BigRational::one())
}
pub fn is_zero(&self) -> bool {
self.0.is_zero()
}
pub fn is_positive(&self) -> bool {
self.0.is_positive()
}
pub fn is_negative(&self) -> bool {
self.0.is_negative()
}
pub fn is_integer(&self) -> bool {
self.0.is_integer()
}
pub fn abs(&self) -> Self {
Rational(self.0.abs())
}
pub fn to_i64_parts(&self) -> Option<(i64, i64)> {
Some((self.0.numer().to_i64()?, self.0.denom().to_i64()?))
}
pub fn to_f64(&self) -> f64 {
self.0.to_f64().unwrap_or(f64::NAN)
}
}
impl Default for Rational {
fn default() -> Self {
Rational::zero()
}
}
impl From<i32> for Rational {
fn from(n: i32) -> Self {
Rational(BigRational::from_integer(BigInt::from(n)))
}
}
impl From<i64> for Rational {
fn from(n: i64) -> Self {
Rational(BigRational::from_integer(BigInt::from(n)))
}
}
impl fmt::Display for Rational {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl fmt::Debug for Rational {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ParseRationalError {
reason: String,
}
impl fmt::Display for ParseRationalError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.reason)
}
}
impl std::error::Error for ParseRationalError {}
impl FromStr for Rational {
type Err = ParseRationalError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse::<BigRational>()
.map(Rational)
.map_err(|e| ParseRationalError {
reason: e.to_string(),
})
}
}
macro_rules! binary_op {
($trait:ident, $method:ident, $assign_trait:ident, $assign_method:ident) => {
impl $trait<Rational> for Rational {
type Output = Rational;
fn $method(self, rhs: Rational) -> Rational {
Rational(self.0.$method(rhs.0))
}
}
impl $trait<&Rational> for Rational {
type Output = Rational;
fn $method(self, rhs: &Rational) -> Rational {
Rational(self.0.$method(&rhs.0))
}
}
impl $trait<Rational> for &Rational {
type Output = Rational;
fn $method(self, rhs: Rational) -> Rational {
Rational((&self.0).$method(rhs.0))
}
}
impl $trait<&Rational> for &Rational {
type Output = Rational;
fn $method(self, rhs: &Rational) -> Rational {
Rational((&self.0).$method(&rhs.0))
}
}
impl $assign_trait<Rational> for Rational {
fn $assign_method(&mut self, rhs: Rational) {
self.0.$assign_method(rhs.0);
}
}
impl $assign_trait<&Rational> for Rational {
fn $assign_method(&mut self, rhs: &Rational) {
self.0.$assign_method(&rhs.0);
}
}
};
}
binary_op!(Add, add, AddAssign, add_assign);
binary_op!(Sub, sub, SubAssign, sub_assign);
binary_op!(Mul, mul, MulAssign, mul_assign);
binary_op!(Div, div, DivAssign, div_assign);
impl Neg for Rational {
type Output = Rational;
fn neg(self) -> Rational {
Rational(-self.0)
}
}
impl Neg for &Rational {
type Output = Rational;
fn neg(self) -> Rational {
Rational(-&self.0)
}
}
impl Sum for Rational {
fn sum<I: Iterator<Item = Rational>>(iter: I) -> Rational {
iter.fold(Rational::zero(), |acc, x| acc + x)
}
}
impl<'a> Sum<&'a Rational> for Rational {
fn sum<I: Iterator<Item = &'a Rational>>(iter: I) -> Rational {
iter.fold(Rational::zero(), |acc, x| acc + x)
}
}
#[cfg(feature = "num-interop")]
impl From<BigRational> for Rational {
fn from(r: BigRational) -> Self {
Rational(r)
}
}
#[cfg(feature = "num-interop")]
impl From<Rational> for BigRational {
fn from(r: Rational) -> Self {
r.0
}
}
#[cfg(feature = "num-interop")]
impl Rational {
pub fn as_big_rational(&self) -> &BigRational {
&self.0
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Rational {
fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
s.collect_str(self)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Rational {
fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
let s = String::deserialize(d)?;
s.trim()
.parse()
.map_err(|e| serde::de::Error::custom(format!("invalid rational {s:?}: {e}")))
}
}