// Copyright © 2016–2020 University of Malta // This program is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License // as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License and a copy of the GNU General Public License along with // this program. If not, see . #[cfg(feature = "integer")] use crate::Integer; #[cfg(feature = "rational")] use crate::Rational; use crate::{ ext::xmpfr::{self, OptFloat}, float::{Round, SmallFloat}, ops::{ AddAssignRound, AddFrom, AddFromRound, AssignRound, DivAssignRound, DivFrom, DivFromRound, MulAssignRound, MulFrom, MulFromRound, NegAssign, Pow, PowAssign, PowAssignRound, PowFrom, PowFromRound, RemAssignRound, RemFrom, RemFromRound, SubAssignRound, SubFrom, SubFromRound, }, Float, }; use az::{CheckedAs, CheckedCast}; use core::{ cmp::Ordering, ops::{ Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, }, }; use libc::{c_long, c_ulong}; impl Neg for Float { type Output = Float; #[inline] fn neg(mut self) -> Float { self.neg_assign(); self } } impl NegAssign for Float { #[inline] fn neg_assign(&mut self) { xmpfr::neg(self, (), Round::Nearest); } } impl<'a> Neg for &'a Float { type Output = NegIncomplete<'a>; #[inline] fn neg(self) -> NegIncomplete<'a> { NegIncomplete { val: self } } } #[derive(Debug)] pub struct NegIncomplete<'a> { val: &'a Float, } impl AssignRound> for Float { type Round = Round; type Ordering = Ordering; #[inline] fn assign_round(&mut self, src: NegIncomplete<'_>, round: Round) -> Ordering { xmpfr::neg(self, src.val, round) } } arith_binary_self_round! { Float, Round, Round::Nearest, Ordering; xmpfr::add; Add { add } AddAssign { add_assign } AddAssignRound { add_assign_round } AddFrom { add_from } AddFromRound { add_from_round } AddIncomplete } arith_binary_self_round! { Float, Round, Round::Nearest, Ordering; xmpfr::sub; Sub { sub } SubAssign { sub_assign } SubAssignRound { sub_assign_round } SubFrom { sub_from } SubFromRound { sub_from_round } SubIncomplete } arith_binary_self_round! { Float, Round, Round::Nearest, Ordering; xmpfr::mul; Mul { mul } MulAssign { mul_assign } MulAssignRound { mul_assign_round } MulFrom { mul_from } MulFromRound { mul_from_round } MulIncomplete } arith_binary_self_round! { Float, Round, Round::Nearest, Ordering; xmpfr::div; Div { div } DivAssign { div_assign } DivAssignRound { div_assign_round } DivFrom { div_from } DivFromRound { div_from_round } DivIncomplete } arith_binary_self_round! { Float, Round, Round::Nearest, Ordering; xmpfr::fmod; Rem { rem } RemAssign { rem_assign } RemAssignRound { rem_assign_round } RemFrom { rem_from } RemFromRound { rem_from_round } RemIncomplete } arith_binary_self_round! { Float, Round, Round::Nearest, Ordering; xmpfr::pow; Pow { pow } PowAssign { pow_assign } PowAssignRound { pow_assign_round } PowFrom { pow_from } PowFromRound { pow_from_round } PowIncomplete } #[cfg(feature = "integer")] arith_commut_round! { Float, Round, Round::Nearest, Ordering; xmpfr::add_z; Add { add } AddAssign { add_assign } AddAssignRound { add_assign_round } AddFrom { add_from } AddFromRound { add_from_round } Integer; AddIntegerIncomplete, AddOwnedIntegerIncomplete } #[cfg(feature = "integer")] arith_noncommut_round! { Float, Round, Round::Nearest, Ordering; xmpfr::sub_z, xmpfr::z_sub; Sub { sub } SubAssign { sub_assign } SubAssignRound { sub_assign_round } SubFrom { sub_from } SubFromRound { sub_from_round } Integer; SubIntegerIncomplete, SubOwnedIntegerIncomplete; SubFromIntegerIncomplete, SubFromOwnedIntegerIncomplete } #[cfg(feature = "integer")] arith_commut_round! { Float, Round, Round::Nearest, Ordering; xmpfr::mul_z; Mul { mul } MulAssign { mul_assign } MulAssignRound { mul_assign_round } MulFrom { mul_from } MulFromRound { mul_from_round } Integer; MulIntegerIncomplete, MulOwnedIntegerIncomplete } #[cfg(feature = "integer")] arith_noncommut_round! { Float, Round, Round::Nearest, Ordering; xmpfr::div_z, xmpfr::z_div; Div { div } DivAssign { div_assign } DivAssignRound { div_assign_round } DivFrom { div_from } DivFromRound { div_from_round } Integer; DivIntegerIncomplete, DivOwnedIntegerIncomplete; DivFromIntegerIncomplete, DivFromOwnedIntegerIncomplete } #[cfg(feature = "integer")] arith_forward_round! { Float, Round, Round::Nearest, Ordering; xmpfr::pow_z; Pow { pow } PowAssign { pow_assign } PowAssignRound { pow_assign_round } Integer; PowIntegerIncomplete, PowOwnedIntegerIncomplete } #[cfg(feature = "rational")] arith_commut_round! { Float, Round, Round::Nearest, Ordering; xmpfr::add_q; Add { add } AddAssign { add_assign } AddAssignRound { add_assign_round } AddFrom { add_from } AddFromRound { add_from_round } Rational; AddRationalIncomplete, AddOwnedRationalIncomplete } #[cfg(feature = "rational")] arith_noncommut_round! { Float, Round, Round::Nearest, Ordering; xmpfr::sub_q, xmpfr::q_sub; Sub { sub } SubAssign { sub_assign } SubAssignRound { sub_assign_round } SubFrom { sub_from } SubFromRound { sub_from_round } Rational; SubRationalIncomplete, SubOwnedRationalIncomplete; SubFromRationalIncomplete, SubFromOwnedRationalIncomplete } #[cfg(feature = "rational")] arith_commut_round! { Float, Round, Round::Nearest, Ordering; xmpfr::mul_q; Mul { mul } MulAssign { mul_assign } MulAssignRound { mul_assign_round } MulFrom { mul_from } MulFromRound { mul_from_round } Rational; MulRationalIncomplete, MulOwnedRationalIncomplete } #[cfg(feature = "rational")] arith_noncommut_round! { Float, Round, Round::Nearest, Ordering; xmpfr::div_q, xmpfr::q_div; Div { div } DivAssign { div_assign } DivAssignRound { div_assign_round } DivFrom { div_from } DivFromRound { div_from_round } Rational; DivRationalIncomplete, DivOwnedRationalIncomplete; DivFromRationalIncomplete, DivFromOwnedRationalIncomplete } arith_prim_commut_round! { Float, Round, Round::Nearest, Ordering; PrimOps::add; Add { add } AddAssign { add_assign } AddAssignRound { add_assign_round } AddFrom { add_from } AddFromRound { add_from_round } i8, AddI8Incomplete; i16, AddI16Incomplete; i32, AddI32Incomplete; i64, AddI64Incomplete; i128, AddI128Incomplete; u8, AddU8Incomplete; u16, AddU16Incomplete; u32, AddU32Incomplete; u64, AddU64Incomplete; u128, AddU128Incomplete; f32, AddF32Incomplete; f64, AddF64Incomplete; } arith_prim_noncommut_round! { Float, Round, Round::Nearest, Ordering; PrimOps::sub, PrimOps::sub_from; Sub { sub } SubAssign { sub_assign } SubAssignRound { sub_assign_round } SubFrom { sub_from } SubFromRound { sub_from_round } i8, SubI8Incomplete, SubFromI8Incomplete; i16, SubI16Incomplete, SubFromI16Incomplete; i32, SubI32Incomplete, SubFromI32Incomplete; i64, SubI64Incomplete, SubFromI64Incomplete; i128, SubI128Incomplete, SubFromI128Incomplete; u8, SubU8Incomplete, SubFromU8Incomplete; u16, SubU16Incomplete, SubFromU16Incomplete; u32, SubU32Incomplete, SubFromU32Incomplete; u64, SubU64Incomplete, SubFromU64Incomplete; u128, SubU128Incomplete, SubFromU128Incomplete; f32, SubF32Incomplete, SubFromF32Incomplete; f64, SubF64Incomplete, SubFromF64Incomplete; } arith_prim_commut_round! { Float, Round, Round::Nearest, Ordering; PrimOps::mul; Mul { mul } MulAssign { mul_assign } MulAssignRound { mul_assign_round } MulFrom { mul_from } MulFromRound { mul_from_round } i8, MulI8Incomplete; i16, MulI16Incomplete; i32, MulI32Incomplete; i64, MulI64Incomplete; i128, MulI128Incomplete; u8, MulU8Incomplete; u16, MulU16Incomplete; u32, MulU32Incomplete; u64, MulU64Incomplete; u128, MulU128Incomplete; f32, MulF32Incomplete; f64, MulF64Incomplete; } arith_prim_noncommut_round! { Float, Round, Round::Nearest, Ordering; PrimOps::div, PrimOps::div_from; Div { div } DivAssign { div_assign } DivAssignRound { div_assign_round } DivFrom { div_from } DivFromRound { div_from_round } i8, DivI8Incomplete, DivFromI8Incomplete; i16, DivI16Incomplete, DivFromI16Incomplete; i32, DivI32Incomplete, DivFromI32Incomplete; i64, DivI64Incomplete, DivFromI64Incomplete; i128, DivI128Incomplete, DivFromI128Incomplete; u8, DivU8Incomplete, DivFromU8Incomplete; u16, DivU16Incomplete, DivFromU16Incomplete; u32, DivU32Incomplete, DivFromU32Incomplete; u64, DivU64Incomplete, DivFromU64Incomplete; u128, DivU128Incomplete, DivFromU128Incomplete; f32, DivF32Incomplete, DivFromF32Incomplete; f64, DivF64Incomplete, DivFromF64Incomplete; } arith_prim_noncommut_round! { Float, Round, Round::Nearest, Ordering; PrimOps::rem, PrimOps::rem_from; Rem { rem } RemAssign { rem_assign } RemAssignRound { rem_assign_round } RemFrom { rem_from } RemFromRound { rem_from_round } i8, RemI8Incomplete, RemFromI8Incomplete; i16, RemI16Incomplete, RemFromI16Incomplete; i32, RemI32Incomplete, RemFromI32Incomplete; i64, RemI64Incomplete, RemFromI64Incomplete; i128, RemI128Incomplete, RemFromI128Incomplete; u8, RemU8Incomplete, RemFromU8Incomplete; u16, RemU16Incomplete, RemFromU16Incomplete; u32, RemU32Incomplete, RemFromU32Incomplete; u64, RemU64Incomplete, RemFromU64Incomplete; u128, RemU128Incomplete, RemFromU128Incomplete; f32, RemF32Incomplete, RemFromF32Incomplete; f64, RemF64Incomplete, RemFromF64Incomplete; } arith_prim_noncommut_round! { Float, Round, Round::Nearest, Ordering; PrimOps::pow, PrimOps::pow_from; Pow { pow } PowAssign { pow_assign } PowAssignRound { pow_assign_round } PowFrom { pow_from } PowFromRound { pow_from_round } i8, PowI8Incomplete, PowFromI8Incomplete; i16, PowI16Incomplete, PowFromI16Incomplete; i32, PowI32Incomplete, PowFromI32Incomplete; i64, PowI64Incomplete, PowFromI64Incomplete; i128, PowI128Incomplete, PowFromI128Incomplete; u8, PowU8Incomplete, PowFromU8Incomplete; u16, PowU16Incomplete, PowFromU16Incomplete; u32, PowU32Incomplete, PowFromU32Incomplete; u64, PowU64Incomplete, PowFromU64Incomplete; u128, PowU128Incomplete, PowFromU128Incomplete; f32, PowF32Incomplete, PowFromF32Incomplete; f64, PowF64Incomplete, PowFromF64Incomplete; } arith_prim_exact_round! { Float, Round, Round::Nearest, Ordering; xmpfr::shl_u32; Shl { shl } ShlAssign { shl_assign } u32, ShlU32Incomplete; } arith_prim_exact_round! { Float, Round, Round::Nearest, Ordering; xmpfr::shr_u32; Shr { shr } ShrAssign { shr_assign } u32, ShrU32Incomplete; } arith_prim_exact_round! { Float, Round, Round::Nearest, Ordering; xmpfr::shl_i32; Shl { shl } ShlAssign { shl_assign } i32, ShlI32Incomplete; } arith_prim_exact_round! { Float, Round, Round::Nearest, Ordering; xmpfr::shr_i32; Shr { shr } ShrAssign { shr_assign } i32, ShrI32Incomplete; } mul_op_commut_round! { Float, Round, Round::Nearest, Ordering; add_mul; Add { add } AddAssign { add_assign } AddAssignRound { add_assign_round } AddFrom { add_from } AddFromRound { add_from_round } MulIncomplete; AddMulIncomplete } mul_op_noncommut_round! { Float, Round, Round::Nearest, Ordering; sub_mul, mul_sub; Sub { sub } SubAssign { sub_assign } SubAssignRound { sub_assign_round } SubFrom { sub_from } SubFromRound { sub_from_round } MulIncomplete; SubMulIncomplete, SubMulFromIncomplete } trait PrimOps: AsLong { fn add(rop: &mut Float, op1: O, op2: Self, rnd: Round) -> Ordering; fn sub(rop: &mut Float, op1: O, op2: Self, rnd: Round) -> Ordering; fn sub_from(rop: &mut Float, op1: Self, op2: O, rnd: Round) -> Ordering; fn mul(rop: &mut Float, op1: O, op2: Self, rnd: Round) -> Ordering; fn div(rop: &mut Float, op1: O, op2: Self, rnd: Round) -> Ordering; fn div_from(rop: &mut Float, op1: Self, op2: O, rnd: Round) -> Ordering; fn rem(rop: &mut Float, op1: O, op2: Self, rnd: Round) -> Ordering; fn rem_from(rop: &mut Float, op1: Self, op2: O, rnd: Round) -> Ordering; fn pow(rop: &mut Float, op1: O, op2: Self, rnd: Round) -> Ordering; fn pow_from(rop: &mut Float, op1: Self, op2: O, rnd: Round) -> Ordering; } trait AsLong: Copy { type Long; } macro_rules! as_long { ($Long:ty: $($Prim:ty)*) => { $( impl AsLong for $Prim { type Long = $Long; } )* } } as_long! { c_long: i8 i16 i32 i64 i128 isize } as_long! { c_ulong: u8 u16 u32 u64 u128 usize } as_long! { f64: f32 f64 } macro_rules! forward { (fn $fn:ident() -> $deleg_long:path, $deleg:path) => { #[inline] fn $fn(rop: &mut Float, op1: O, op2: Self, rnd: Round) -> Ordering { if let Some(op2) = op2.checked_as() { $deleg_long(rop, op1, op2, rnd) } else { let small: SmallFloat = op2.into(); $deleg(rop, op1, &*small, rnd) } } }; (fn $fn:ident() -> $deleg:path) => { #[inline] fn $fn(rop: &mut Float, op1: O, op2: Self, rnd: Round) -> Ordering { let small: SmallFloat = op2.into(); $deleg(rop, op1, &*small, rnd) } }; } macro_rules! reverse { (fn $fn:ident() -> $deleg_long:path, $deleg:path) => { #[inline] fn $fn(rop: &mut Float, op1: Self, op2: O, rnd: Round) -> Ordering { if let Some(op1) = op1.checked_as() { $deleg_long(rop, op1, op2, rnd) } else { let small: SmallFloat = op1.into(); $deleg(rop, &*small, op2, rnd) } } }; (fn $fn:ident() -> $deleg:path) => { #[inline] fn $fn(rop: &mut Float, op1: Self, op2: O, rnd: Round) -> Ordering { let small: SmallFloat = op1.into(); $deleg(rop, &*small, op2, rnd) } }; } impl PrimOps for T where T: AsLong + CheckedCast + Into, { forward! { fn add() -> xmpfr::add_si, xmpfr::add } forward! { fn sub() -> xmpfr::sub_si, xmpfr::sub } reverse! { fn sub_from() -> xmpfr::si_sub, xmpfr::sub } forward! { fn mul() -> xmpfr::mul_si, xmpfr::mul } forward! { fn div() -> xmpfr::div_si, xmpfr::div } reverse! { fn div_from() -> xmpfr::si_div, xmpfr::div } forward! { fn rem() -> xmpfr::fmod } reverse! { fn rem_from() -> xmpfr::fmod } forward! { fn pow() -> xmpfr::pow_si, xmpfr::pow } reverse! { fn pow_from() -> xmpfr::pow } } impl PrimOps for T where T: AsLong + CheckedCast + Into, { forward! { fn add() -> xmpfr::add_ui, xmpfr::add } forward! { fn sub() -> xmpfr::sub_ui, xmpfr::sub } reverse! { fn sub_from() -> xmpfr::ui_sub, xmpfr::sub } forward! { fn mul() -> xmpfr::mul_ui, xmpfr::mul } forward! { fn div() -> xmpfr::div_ui, xmpfr::div } reverse! { fn div_from() -> xmpfr::ui_div, xmpfr::div } forward! { fn rem() -> xmpfr::fmod } reverse! { fn rem_from() -> xmpfr::fmod } forward! { fn pow() -> xmpfr::pow_ui, xmpfr::pow } reverse! { fn pow_from() -> xmpfr::ui_pow, xmpfr::pow } } impl PrimOps for T where T: AsLong + CheckedCast + Into, { forward! { fn add() -> xmpfr::add_d, xmpfr::add } forward! { fn sub() -> xmpfr::sub_d, xmpfr::sub } reverse! { fn sub_from() -> xmpfr::d_sub, xmpfr::sub } forward! { fn mul() -> xmpfr::mul_d, xmpfr::mul } forward! { fn div() -> xmpfr::div_d, xmpfr::div } reverse! { fn div_from() -> xmpfr::d_div, xmpfr::div } forward! { fn rem() -> xmpfr::fmod } reverse! { fn rem_from() -> xmpfr::fmod } forward! { fn pow() -> xmpfr::pow } reverse! { fn pow_from() -> xmpfr::pow } } impl<'a> Add for MulIncomplete<'a> { type Output = MulAddMulIncomplete<'a>; #[inline] fn add(self, rhs: MulIncomplete<'a>) -> MulAddMulIncomplete<'_> { MulAddMulIncomplete { lhs: self, rhs } } } #[derive(Debug)] pub struct MulAddMulIncomplete<'a> { lhs: MulIncomplete<'a>, rhs: MulIncomplete<'a>, } impl AssignRound> for Float { type Round = Round; type Ordering = Ordering; #[inline] fn assign_round(&mut self, src: MulAddMulIncomplete<'_>, round: Round) -> Ordering { xmpfr::fmma( self, src.lhs.lhs, src.lhs.rhs, src.rhs.lhs, src.rhs.rhs, round, ) } } impl<'a> Sub for MulIncomplete<'a> { type Output = MulSubMulIncomplete<'a>; #[inline] fn sub(self, rhs: MulIncomplete<'a>) -> MulSubMulIncomplete<'_> { MulSubMulIncomplete { lhs: self, rhs } } } #[derive(Debug)] pub struct MulSubMulIncomplete<'a> { lhs: MulIncomplete<'a>, rhs: MulIncomplete<'a>, } impl AssignRound> for Float { type Round = Round; type Ordering = Ordering; #[inline] fn assign_round(&mut self, src: MulSubMulIncomplete<'_>, round: Round) -> Ordering { xmpfr::fmms( self, src.lhs.lhs, src.lhs.rhs, src.rhs.lhs, src.rhs.rhs, round, ) } } #[inline] fn add_mul(rop: &mut Float, add: O, mul: MulIncomplete<'_>, rnd: Round) -> Ordering { xmpfr::fma(rop, mul.lhs, mul.rhs, add, rnd) } #[inline] fn sub_mul(rop: &mut Float, add: O, mul: MulIncomplete<'_>, rnd: Round) -> Ordering { xmpfr::submul(rop, add, mul.lhs, mul.rhs, rnd) } #[inline] fn mul_sub(rop: &mut Float, mul: MulIncomplete<'_>, sub: O, rnd: Round) -> Ordering { xmpfr::fms(rop, mul.lhs, mul.rhs, sub, rnd) } #[cfg(test)] #[allow(clippy::cognitive_complexity)] pub(crate) mod tests { #[cfg(feature = "rational")] use crate::Rational; use crate::{ float::{self, FreeCache, Special}, ops::Pow, Float, }; #[cfg(feature = "integer")] use {crate::Integer, core::str::FromStr}; pub fn same(a: Float, b: Float) -> bool { if a.is_nan() && b.is_nan() { return true; } if a == b { return true; } if a.prec() == b.prec() { return false; } a == Float::with_val(a.prec(), b) } macro_rules! test_ref_op { ($first:expr, $second:expr) => { assert_eq!( Float::with_val(53, $first), $second, "({}) != ({})", stringify!($first), stringify!($second) ); }; } #[test] fn check_ref_op() { let lhs = &Float::with_val(53, 12.25); let rhs = &Float::with_val(53, -1.375); let pu = 30_u32; let pi = -15_i32; let ps = 31.625_f32; let pd = -1.5_f64; 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 % rhs, lhs.clone() % rhs); test_ref_op!(lhs.pow(rhs), lhs.clone().pow(rhs)); test_ref_op!(lhs + pu, lhs.clone() + pu); test_ref_op!(lhs - pu, lhs.clone() - pu); test_ref_op!(lhs * pu, lhs.clone() * pu); test_ref_op!(lhs / pu, lhs.clone() / pu); test_ref_op!(lhs % pu, lhs.clone() % pu); 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!(pu + lhs, pu + lhs.clone()); test_ref_op!(pu - lhs, pu - lhs.clone()); test_ref_op!(pu * lhs, pu * lhs.clone()); test_ref_op!(pu / lhs, pu / lhs.clone()); test_ref_op!(pu % lhs, pu % lhs.clone()); test_ref_op!(Pow::pow(pu, lhs), Pow::pow(pu, lhs.clone())); test_ref_op!(lhs + pi, lhs.clone() + pi); test_ref_op!(lhs - pi, lhs.clone() - pi); test_ref_op!(lhs * pi, lhs.clone() * pi); test_ref_op!(lhs / pi, lhs.clone() / pi); test_ref_op!(lhs % pi, lhs.clone() % pi); 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_ref_op!(pi + lhs, pi + lhs.clone()); test_ref_op!(pi - lhs, pi - lhs.clone()); test_ref_op!(pi * lhs, pi * lhs.clone()); test_ref_op!(pi / lhs, pi / lhs.clone()); test_ref_op!(pi % lhs, pi % lhs.clone()); test_ref_op!(lhs + ps, lhs.clone() + ps); test_ref_op!(lhs - ps, lhs.clone() - ps); test_ref_op!(lhs * ps, lhs.clone() * ps); test_ref_op!(lhs / ps, lhs.clone() / ps); test_ref_op!(lhs % ps, lhs.clone() % ps); test_ref_op!(ps + lhs, ps + lhs.clone()); test_ref_op!(ps - lhs, ps - lhs.clone()); test_ref_op!(ps * lhs, ps * lhs.clone()); test_ref_op!(ps / lhs, ps / lhs.clone()); test_ref_op!(ps % lhs, ps % lhs.clone()); test_ref_op!(lhs + pd, lhs.clone() + pd); test_ref_op!(lhs - pd, lhs.clone() - pd); test_ref_op!(lhs * pd, lhs.clone() * pd); test_ref_op!(lhs / pd, lhs.clone() / pd); test_ref_op!(lhs % pd, lhs.clone() % pd); test_ref_op!(pd + lhs, pd + lhs.clone()); test_ref_op!(pd - lhs, pd - lhs.clone()); test_ref_op!(pd * lhs, pd * lhs.clone()); test_ref_op!(pd / lhs, pd / lhs.clone()); test_ref_op!(pd % lhs, pd % lhs.clone()); float::free_cache(FreeCache::All); } macro_rules! check_others { (&$list:expr, $against:expr) => { for op in &$list { let fop = Float::with_val(150, op); for b in &$against { assert!(same(b.clone() + op, b.clone() + &fop)); assert!(same(b.clone() - op, b.clone() - &fop)); assert!(same(b.clone() * op, b.clone() * &fop)); assert!(same(b.clone() / op, b.clone() / &fop)); assert!(same(op + b.clone(), fop.clone() + b)); assert!(same(op - b.clone(), fop.clone() - b)); assert!(same(op * b.clone(), fop.clone() * b)); assert!(same(op / b.clone(), fop.clone() / b)); } } }; ($list:expr, $against:expr) => { for op in $list { let fop = Float::with_val(150, *op); for b in &$against { assert!(same(b.clone() + *op, b.clone() + &fop)); assert!(same(b.clone() - *op, b.clone() - &fop)); assert!(same(b.clone() * *op, b.clone() * &fop)); assert!(same(b.clone() / *op, b.clone() / &fop)); assert!(same(b.clone() % *op, b.clone() % &fop)); assert!(same(*op + b.clone(), fop.clone() + b)); assert!(same(*op - b.clone(), fop.clone() - b)); assert!(same(*op * b.clone(), fop.clone() * b)); assert!(same(*op / b.clone(), fop.clone() / b)); assert!(same(*op % b.clone(), fop.clone() % b)); assert!(same(b.clone().pow(*op), b.clone().pow(&fop))); assert!(same(op.pow(b.clone()), fop.clone().pow(b))); } } }; } #[test] fn check_arith_others() { use crate::tests::{F32, F64, I128, I16, I32, I64, I8, U128, U16, U32, U64, U8}; let large = [ Float::with_val(20, Special::Zero), Float::with_val(20, Special::NegZero), Float::with_val(20, Special::Infinity), Float::with_val(20, Special::NegInfinity), Float::with_val(20, Special::Nan), Float::with_val(20, 1), Float::with_val(20, -1), Float::with_val(20, 999_999e100), Float::with_val(20, 999_999e-100), Float::with_val(20, -999_999e100), Float::with_val(20, -999_999e-100), ]; #[cfg(feature = "integer")] let z = [ Integer::from(0), Integer::from(1), Integer::from(-1), Integer::from_str("-1000000000000").unwrap(), Integer::from_str("1000000000000").unwrap(), ]; #[cfg(feature = "rational")] let q = [ Rational::from(0), Rational::from(1), Rational::from(-1), Rational::from_str("-1000000000000/33333333333").unwrap(), Rational::from_str("1000000000000/33333333333").unwrap(), ]; let against = large .iter() .cloned() .chain(U32.iter().map(|&x| Float::with_val(20, x))) .chain(I32.iter().map(|&x| Float::with_val(20, x))) .chain(U64.iter().map(|&x| Float::with_val(20, x))) .chain(I64.iter().map(|&x| Float::with_val(20, x))) .chain(U128.iter().map(|&x| Float::with_val(20, x))) .chain(I128.iter().map(|&x| Float::with_val(20, x))) .chain(F32.iter().map(|&x| Float::with_val(20, x))) .chain(F64.iter().map(|&x| Float::with_val(20, x))) .collect::>(); #[cfg(feature = "integer")] let mut against = against; #[cfg(feature = "integer")] against.extend(z.iter().map(|x| Float::with_val(20, x))); #[cfg(feature = "rational")] against.extend(q.iter().map(|x| Float::with_val(20, x))); check_others!(I8, against); check_others!(I16, against); check_others!(I32, against); check_others!(I64, against); check_others!(I128, against); check_others!(U8, against); check_others!(U16, against); check_others!(U32, against); check_others!(U64, against); check_others!(U128, against); check_others!(F32, against); check_others!(F64, against); #[cfg(feature = "integer")] check_others!(&z, against); #[cfg(feature = "rational")] check_others!(&q, against); float::free_cache(FreeCache::All); } }