use num_traits::{Num, One, Signed, Zero};
use oxinum_core::Sign;
use oxinum_int::native::{BigInt, BigUint};
use super::BigRational;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseBigRationalError(String);
impl std::fmt::Display for ParseBigRationalError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ParseBigRationalError: {}", self.0)
}
}
impl std::error::Error for ParseBigRationalError {}
impl Zero for BigRational {
#[inline]
fn zero() -> Self {
BigRational::zero()
}
#[inline]
fn is_zero(&self) -> bool {
BigRational::is_zero(self)
}
}
impl One for BigRational {
#[inline]
fn one() -> Self {
BigRational::one()
}
#[inline]
fn is_one(&self) -> bool {
BigRational::is_one(self)
}
}
impl Num for BigRational {
type FromStrRadixErr = ParseBigRationalError;
fn from_str_radix(s: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
if radix != 10 {
return Err(ParseBigRationalError(format!(
"BigRational::from_str_radix only supports radix 10, got {radix}"
)));
}
if let Some(slash_pos) = s.find('/') {
let num_str = &s[..slash_pos];
let den_str = &s[slash_pos + 1..];
let num = parse_bigint(num_str)?;
let den = parse_biguint(den_str)?;
BigRational::from_parts(num, den).map_err(|e| ParseBigRationalError(e.to_string()))
} else {
let num = parse_bigint(s)?;
Ok(BigRational::from_integer(num))
}
}
}
fn parse_bigint(s: &str) -> Result<BigInt, ParseBigRationalError> {
let (neg, rest) = if let Some(r) = s.strip_prefix('-') {
(true, r)
} else if let Some(r) = s.strip_prefix('+') {
(false, r)
} else {
(false, s)
};
parse_biguint(rest).map(|mag| {
if neg && !mag.is_zero() {
BigInt::from_parts(Sign::Negative, mag)
} else {
BigInt::from_parts(Sign::Positive, mag)
}
})
}
fn parse_biguint(s: &str) -> Result<BigUint, ParseBigRationalError> {
BigUint::from_str_radix(s, 10).map_err(|e| ParseBigRationalError(e.to_string()))
}
impl Signed for BigRational {
#[inline]
fn abs(&self) -> Self {
BigRational::abs(self)
}
fn abs_sub(&self, other: &Self) -> Self {
if self > other {
self.clone() - other.clone()
} else {
BigRational::zero()
}
}
fn signum(&self) -> Self {
let s = BigRational::signum(self);
BigRational::from_integer(BigInt::from(s as i64))
}
fn is_positive(&self) -> bool {
BigRational::signum(self) > 0
}
fn is_negative(&self) -> bool {
BigRational::signum(self) < 0
}
}