use std::{
cmp::Ordering,
error::Error,
fmt::{self, Display, Formatter}
};
use crate::Weight;
#[derive(Debug, Clone)]
pub struct Rational
{
negative: bool,
numerator: Weight,
denominator: Weight
}
impl Rational
{
pub const ZERO: Self = Self {
negative: false,
numerator: Weight::ZERO,
denominator: Weight::ONE
};
pub const ONE: Self = Self {
negative: false,
numerator: Weight::ONE,
denominator: Weight::ONE
};
pub fn new(
negative: bool,
numerator: Weight,
denominator: Weight
) -> Result<Self, ZeroDenominatorError>
{
if denominator.is_zero()
{
return Err(ZeroDenominatorError)
}
let negative = negative && !numerator.is_zero();
Ok(Self::new_unchecked(negative, numerator, denominator))
}
pub(crate) fn new_unchecked(
negative: bool,
numerator: Weight,
denominator: Weight
) -> Self
{
debug_assert!(!denominator.is_zero());
debug_assert!(!(negative && numerator.is_zero()));
Self {
negative,
numerator,
denominator
}
}
#[inline]
pub fn is_negative(&self) -> bool { self.negative }
#[inline]
pub fn numerator(&self) -> &Weight { &self.numerator }
#[inline]
pub fn denominator(&self) -> &Weight { &self.denominator }
pub fn to_f64(&self) -> f64
{
let magnitude = self.numerator.ratio_to_f64(&self.denominator);
match self.negative
{
true => -magnitude,
false => magnitude
}
}
}
impl PartialEq for Rational
{
fn eq(&self, other: &Self) -> bool { self.cmp(other) == Ordering::Equal }
}
impl Eq for Rational {}
impl PartialOrd for Rational
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering>
{
Some(self.cmp(other))
}
}
impl Ord for Rational
{
fn cmp(&self, other: &Self) -> Ordering
{
match (self.negative, other.negative)
{
(false, true) => Ordering::Greater,
(true, false) => Ordering::Less,
(false, false) => self.cmp_magnitude(other),
(true, true) => other.cmp_magnitude(self)
}
}
}
impl Rational
{
fn cmp_magnitude(&self, other: &Self) -> Ordering
{
if self.denominator == other.denominator
{
return self.numerator.cmp(&other.numerator)
}
(&self.numerator * &other.denominator)
.cmp(&(&other.numerator * &self.denominator))
}
}
impl Display for Rational
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result
{
let sign = if self.negative { "-" } else { "" };
write!(f, "{sign}{}/{}", self.numerator, self.denominator)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ZeroDenominatorError;
impl Display for ZeroDenominatorError
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result
{
write!(f, "rational has a zero denominator")
}
}
impl Error for ZeroDenominatorError {}