use std::cmp::Ordering;
use std::ops::{Add, Shl};
use traiter::numbers::{
CheckedDivRemEuclid, One, Parity, Round, Signed, TieBreaking,
};
use crate::big_int::BigInt;
use super::types::Fraction;
impl<'a, Digit, const DIGIT_BITNESS: usize> Round
for &'a Fraction<BigInt<Digit, DIGIT_BITNESS>>
where
&'a BigInt<Digit, DIGIT_BITNESS>: CheckedDivRemEuclid<
Output = Option<(
BigInt<Digit, DIGIT_BITNESS>,
BigInt<Digit, DIGIT_BITNESS>,
)>,
>,
BigInt<Digit, DIGIT_BITNESS>: Add<Output = BigInt<Digit, DIGIT_BITNESS>>
+ One
+ Ord
+ Shl<usize, Output = BigInt<Digit, DIGIT_BITNESS>>,
for<'b> &'b BigInt<Digit, DIGIT_BITNESS>: Parity + Signed,
{
type Output = BigInt<Digit, DIGIT_BITNESS>;
fn round(self, tie_breaking: TieBreaking) -> Self::Output {
let (quotient, remainder) = unsafe {
self.numerator
.checked_div_rem_euclid(&self.denominator)
.unwrap_unchecked()
};
match (remainder << 1).cmp(&self.denominator) {
Ordering::Equal => {
if match tie_breaking {
TieBreaking::AwayFromZero => !("ient).is_negative(),
TieBreaking::ToEven => quotient.is_odd(),
TieBreaking::ToOdd => quotient.is_even(),
TieBreaking::TowardZero => ("ient).is_negative(),
} {
quotient + BigInt::<Digit, DIGIT_BITNESS>::one()
} else {
quotient
}
}
Ordering::Greater => {
quotient + BigInt::<Digit, DIGIT_BITNESS>::one()
}
Ordering::Less => quotient,
}
}
}
impl<Digit, const DIGIT_BITNESS: usize> Round
for Fraction<BigInt<Digit, DIGIT_BITNESS>>
where
for<'a> &'a BigInt<Digit, DIGIT_BITNESS>: Parity + Signed,
for<'a> BigInt<Digit, DIGIT_BITNESS>: Add<Output = BigInt<Digit, DIGIT_BITNESS>>
+ CheckedDivRemEuclid<
&'a BigInt<Digit, DIGIT_BITNESS>,
Output = Option<(
BigInt<Digit, DIGIT_BITNESS>,
BigInt<Digit, DIGIT_BITNESS>,
)>,
> + One
+ Ord
+ Shl<usize, Output = BigInt<Digit, DIGIT_BITNESS>>,
{
type Output = BigInt<Digit, DIGIT_BITNESS>;
fn round(self, tie_breaking: TieBreaking) -> Self::Output {
let (quotient, remainder) = unsafe {
self.numerator
.checked_div_rem_euclid(&self.denominator)
.unwrap_unchecked()
};
match (remainder << 1).cmp(&self.denominator) {
Ordering::Equal => {
if match tie_breaking {
TieBreaking::AwayFromZero => !("ient).is_negative(),
TieBreaking::ToEven => quotient.is_odd(),
TieBreaking::ToOdd => quotient.is_even(),
TieBreaking::TowardZero => ("ient).is_negative(),
} {
quotient + BigInt::<Digit, DIGIT_BITNESS>::one()
} else {
quotient
}
}
Ordering::Greater => {
quotient + BigInt::<Digit, DIGIT_BITNESS>::one()
}
Ordering::Less => quotient,
}
}
}
macro_rules! signed_integer_fraction_round_impl {
($($integer:ty)*) => ($(
impl Round for &Fraction<$integer> {
type Output = $integer;
fn round(self, tie_breaking: TieBreaking) -> Self::Output {
let (quotient, remainder) = unsafe {
self.numerator
.checked_div_rem_euclid(self.denominator)
.unwrap_unchecked()
};
match (remainder << 1).cmp(&self.denominator) {
Ordering::Equal => {
if match tie_breaking {
TieBreaking::AwayFromZero => {
!quotient.is_negative()
}
TieBreaking::ToEven => quotient.is_odd(),
TieBreaking::ToOdd => quotient.is_even(),
TieBreaking::TowardZero => quotient.is_negative(),
} {
quotient + 1
} else {
quotient
}
}
Ordering::Greater => quotient + 1,
Ordering::Less => quotient,
}
}
}
impl Round for Fraction<$integer> {
type Output = $integer;
fn round(self, tie_breaking: TieBreaking) -> Self::Output {
let (quotient, remainder) = unsafe {
self.numerator
.checked_div_rem_euclid(self.denominator)
.unwrap_unchecked()
};
match (remainder << 1).cmp(&self.denominator) {
Ordering::Equal => {
if match tie_breaking {
TieBreaking::AwayFromZero => {
!quotient.is_negative()
}
TieBreaking::ToEven => quotient.is_odd(),
TieBreaking::ToOdd => quotient.is_even(),
TieBreaking::TowardZero => quotient.is_negative(),
} {
quotient + 1
} else {
quotient
}
}
Ordering::Greater => quotient + 1,
Ordering::Less => quotient,
}
}
}
)*)
}
signed_integer_fraction_round_impl!(i8 i16 i32 i64 i128 isize);
macro_rules! unsigned_integer_fraction_round_impl {
($($integer:ty)*) => ($(
impl Round for &Fraction<$integer> {
type Output = $integer;
fn round(self, tie_breaking: TieBreaking) -> Self::Output {
let (quotient, remainder) = unsafe {
self.numerator
.checked_div_rem_euclid(self.denominator)
.unwrap_unchecked()
};
match (remainder << 1).cmp(&self.denominator) {
Ordering::Equal => {
if match tie_breaking {
TieBreaking::AwayFromZero => true,
TieBreaking::ToEven => quotient.is_odd(),
TieBreaking::ToOdd => quotient.is_even(),
TieBreaking::TowardZero => false,
} {
quotient + 1
} else {
quotient
}
}
Ordering::Greater => quotient + 1,
Ordering::Less => quotient,
}
}
}
impl Round for Fraction<$integer> {
type Output = $integer;
fn round(self, tie_breaking: TieBreaking) -> Self::Output {
let (quotient, remainder) = unsafe {
self.numerator
.checked_div_rem_euclid(self.denominator)
.unwrap_unchecked()
};
match (remainder << 1).cmp(&self.denominator) {
Ordering::Equal => {
if match tie_breaking {
TieBreaking::AwayFromZero => true,
TieBreaking::ToEven => quotient.is_odd(),
TieBreaking::ToOdd => quotient.is_even(),
TieBreaking::TowardZero => false,
} {
quotient + 1
} else {
quotient
}
}
Ordering::Greater => quotient + 1,
Ordering::Less => quotient,
}
}
}
)*)
}
unsigned_integer_fraction_round_impl!(u8 u16 u32 u64 u128 usize);