use crate::{
ext::xmpq,
ops::{AddFrom, DivFrom, MulFrom, NegAssign, Pow, PowAssign, SubFrom},
Assign, Rational,
};
use std::{
iter::{Product, Sum},
ops::{
Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Shl, ShlAssign, Shr, ShrAssign, Sub,
SubAssign,
},
};
arith_unary! {
Rational;
xmpq::neg;
Neg { neg }
NegAssign { neg_assign }
NegIncomplete;
fn from_incomplete(src) {
let mut dst = Rational::from(src.op);
dst.neg_assign();
dst
}
}
arith_binary! {
Rational;
xmpq::add;
Add { add }
AddAssign { add_assign }
AddFrom { add_from }
AddIncomplete;
rhs_has_more_alloc
}
arith_binary! {
Rational;
xmpq::sub;
Sub { sub }
SubAssign { sub_assign }
SubFrom { sub_from }
SubIncomplete;
rhs_has_more_alloc
}
arith_binary! {
Rational;
xmpq::mul;
Mul { mul }
MulAssign { mul_assign }
MulFrom { mul_from }
MulIncomplete;
rhs_has_more_alloc
}
arith_binary! {
Rational;
xmpq::div;
Div { div }
DivAssign { div_assign }
DivFrom { div_from }
DivIncomplete;
rhs_has_more_alloc
}
arith_prim! {
Rational;
xmpq::lshift_i32;
Shl { shl }
ShlAssign { shl_assign }
i32, ShlI32Incomplete;
}
arith_prim! {
Rational;
xmpq::rshift_i32;
Shr { shr }
ShrAssign { shr_assign }
i32, ShrI32Incomplete;
}
arith_prim! {
Rational;
xmpq::pow_i32;
Pow { pow }
PowAssign { pow_assign }
i32, PowI32Incomplete;
}
arith_prim! {
Rational;
xmpq::mul_2exp;
Shl { shl }
ShlAssign { shl_assign }
u32, ShlU32Incomplete;
}
arith_prim! {
Rational;
xmpq::div_2exp;
Shr { shr }
ShrAssign { shr_assign }
u32, ShrU32Incomplete;
}
arith_prim! {
Rational;
xmpq::pow_u32;
Pow { pow }
PowAssign { pow_assign }
u32, PowU32Incomplete;
}
impl<T> Sum<T> for Rational
where
Rational: AddAssign<T>,
{
fn sum<I>(iter: I) -> Rational
where
I: Iterator<Item = T>,
{
let mut ret = Rational::new();
for i in iter {
ret.add_assign(i);
}
ret
}
}
impl<T> Product<T> for Rational
where
Rational: MulAssign<T>,
{
fn product<I>(iter: I) -> Rational
where
I: Iterator<Item = T>,
{
let mut ret = Rational::from(1);
for i in iter {
ret.mul_assign(i);
}
ret
}
}
#[inline]
fn rhs_has_more_alloc(lhs: &Rational, rhs: &Rational) -> bool {
lhs.numer().inner().alloc - rhs.denom().inner().alloc
< rhs.numer().inner().alloc - lhs.denom().inner().alloc
}
#[cfg(test)]
mod tests {
use crate::{ops::Pow, Rational};
macro_rules! test_ref_op {
($first:expr, $second:expr) => {
assert_eq!(
Rational::from($first),
$second,
"({}) != ({})",
stringify!($first),
stringify!($second)
);
};
}
#[test]
fn check_ref_op() {
let lhs = Rational::from((-13, 27));
let rhs = Rational::from((15, 101));
let pu = 30_u32;
let pi = -15_i32;
test_ref_op!(-&lhs, -lhs.clone());
test_ref_op!(&lhs + &rhs, lhs.clone() + &rhs);
test_ref_op!(&lhs - &rhs, lhs.clone() - &rhs);
test_ref_op!(&lhs * &rhs, lhs.clone() * &rhs);
test_ref_op!(&lhs / &rhs, lhs.clone() / &rhs);
test_ref_op!(&lhs << pu, lhs.clone() << pu);
test_ref_op!(&lhs >> pu, lhs.clone() >> pu);
test_ref_op!((&lhs).pow(pu), lhs.clone().pow(pu));
test_ref_op!(&lhs << pi, lhs.clone() << pi);
test_ref_op!(&lhs >> pi, lhs.clone() >> pi);
test_ref_op!((&lhs).pow(pi), lhs.clone().pow(pi));
}
#[test]
fn check_neg_pow() {
let a = Rational::from((-12, 7));
let pow_pos = a.clone().pow(3i32);
assert_eq!(pow_pos, ((-12i32).pow(3), 7i32.pow(3u32)));
let pow_neg = a.clone().pow(-3i32);
assert_eq!(pow_neg, ((-7i32).pow(3), 12i32.pow(3)));
}
}