use std::{
cmp::Ordering,
fmt::Display,
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign},
str::FromStr,
};
use crate::{detail, Decimal};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Fraction {
num: i128,
den: i128,
}
impl Fraction {
pub fn new() -> Self {
Self { num: 0, den: 1 }
}
pub fn abs(&self) -> Self {
Self {
num: self.num.abs(),
den: self.den,
}
}
pub fn numerator(&self) -> i128 {
self.num
}
pub fn denominator(&self) -> i128 {
self.den
}
pub fn as_decimal(&self) -> Decimal {
(*self).into()
}
pub fn gcd(a: Self, b: Self) -> Self {
detail::gcd(a.abs(), b.abs())
}
pub fn lcm(a: Self, b: Self) -> Self {
if a == 0.into() || b == 0.into() {
return Self::new();
}
(a * b).abs() / Self::gcd(a, b) }
fn from_integer<T: Into<i128>>(value: T) -> Self {
Self { num: value.into(), den: 1 }
}
fn from_ratio<T: Into<i128>>(num: T, den: T) -> Self {
let (mut num, mut den) = (num.into(), den.into());
detail::check_zero(den);
if den < 0 {
num = -num;
den = -den;
}
let gcd = detail::gcd(num.abs(), den.abs());
num /= gcd;
den /= gcd;
Self { num, den }
}
pub const MAX: Fraction = Fraction { num: i128::MAX, den: 1 };
pub const MIN: Fraction = Fraction { num: i128::MIN, den: 1 };
pub const EPSILON: Fraction = Fraction { num: 1, den: i128::MAX };
}
macro_rules! impl_from_integer {
($($t:ty),+ $(,)?) => { $(
impl From<$t> for Fraction {
fn from(value: $t) -> Self {
Fraction::from_integer(value)
}
}
impl From<($t, $t)> for Fraction {
fn from(value: ($t, $t)) -> Self {
Fraction::from_ratio(value.0, value.1)
}
}
)+ };
}
impl_from_integer!(i8, i16, i32, i64, i128, u8, u16, u32, u64);
impl From<f64> for Fraction {
fn from(value: f64) -> Self {
if !value.is_finite() {
panic!("Error: Invalid floating-point number.");
}
let int_part = value.floor();
let dec_part = value - int_part;
let precision = i128::pow(10, f64::DIGITS);
let gcd = detail::gcd((dec_part * (precision as f64)).round() as i128, precision);
let mut num = (dec_part * precision as f64).round() as i128 / gcd;
let den = precision / gcd;
num += int_part as i128 * den;
Self { num, den }
}
}
impl From<f32> for Fraction {
fn from(value: f32) -> Self {
Fraction::from(value as f64)
}
}
impl From<Decimal> for Fraction {
fn from(value: Decimal) -> Self {
value.as_fraction()
}
}
impl From<&str> for Fraction {
fn from(value: &str) -> Self {
Self::from_str(value).unwrap_or_else(|_| panic!("expect format `numerator/denominator` but got `{}`", value))
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct ParseFractionError;
impl FromStr for Fraction {
type Err = ParseFractionError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
if let Ok(num) = s.parse() {
return Ok(Self { num, den: 1 });
}
let (num, den) = s.split_once('/').ok_or(ParseFractionError)?;
let num = num.parse::<i128>().map_err(|_| ParseFractionError)?;
let den = den.parse::<i128>().map_err(|_| ParseFractionError)?;
Ok(Self::from((num, den)))
}
}
impl Default for Fraction {
fn default() -> Self {
Self::new()
}
}
impl PartialOrd for Fraction {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Fraction {
fn cmp(&self, other: &Self) -> Ordering {
(self.num * other.den - self.den * other.num).cmp(&0)
}
}
impl Neg for Fraction {
type Output = Self;
fn neg(self) -> Self::Output {
Self { num: -self.num, den: self.den }
}
}
#[auto_impl_ops::auto_ops]
impl Add for Fraction {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self::from((self.num * rhs.den + self.den * rhs.num, self.den * rhs.den))
}
}
#[auto_impl_ops::auto_ops]
impl Sub for Fraction {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self::from((self.num * rhs.den - self.den * rhs.num, self.den * rhs.den))
}
}
#[auto_impl_ops::auto_ops]
impl Mul for Fraction {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self::from((self.num * rhs.num, self.den * rhs.den))
}
}
#[auto_impl_ops::auto_ops]
impl Div for Fraction {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
Self::from((self.num * rhs.den, self.den * rhs.num))
}
}
#[auto_impl_ops::auto_ops]
impl Rem for Fraction {
type Output = Self;
fn rem(self, rhs: Self) -> Self::Output {
detail::check_zero(rhs.num);
Self::from(((self.num * rhs.den) % (rhs.num * self.den), self.den * rhs.den))
}
}
impl Display for Fraction {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if self.den == 1 {
write!(f, "{}", self.num)
} else {
write!(f, "{}/{}", self.num, self.den)
}
}
}
impl From<Fraction> for f64 {
fn from(value: Fraction) -> Self {
value.num as f64 / value.den as f64
}
}
impl From<Fraction> for f32 {
fn from(value: Fraction) -> Self {
value.num as f32 / value.den as f32
}
}