use core::{
fmt::Debug,
ops::{Div, DivAssign, Rem, RemAssign},
};
use dashu_base::{Approximation::*, DivEuclid, DivRemEuclid, RemEuclid};
use dashu_float::{round::Rounding::*, Context};
mod helper_macros;
fn test_div<'a, T>(a: &'a T, b: &'a T, c: &'a T)
where
T: Div<T, Output = T>,
T: Div<&'a T, Output = T>,
&'a T: Div<T, Output = T>,
&'a T: Div<&'a T, Output = T>,
T: DivAssign<T>,
T: DivAssign<&'a T>,
T: Clone,
T: Debug,
T: Eq,
{
assert_eq!(a / b, *c);
assert_eq!(a.clone() / b, *c);
assert_eq!(a / b.clone(), *c);
assert_eq!(a.clone() / b.clone(), *c);
let mut x = a.clone();
x /= b;
assert_eq!(x, *c);
let mut x = a.clone();
x /= b.clone();
assert_eq!(x, *c);
}
fn test_rem<'a, T>(a: &'a T, b: &'a T, c: &'a T)
where
T: Rem<T, Output = T>,
T: Rem<&'a T, Output = T>,
&'a T: Rem<T, Output = T>,
&'a T: Rem<&'a T, Output = T>,
T: RemAssign<T>,
T: RemAssign<&'a T>,
T: Clone,
T: Debug,
T: Eq,
{
assert_eq!(a % b, *c);
assert_eq!(a.clone() % b, *c);
assert_eq!(a % b.clone(), *c);
assert_eq!(a.clone() % b.clone(), *c);
let mut x = a.clone();
x %= b;
assert_eq!(x, *c);
let mut x = a.clone();
x %= b.clone();
assert_eq!(x, *c);
}
#[test]
#[rustfmt::skip::macros(fbig)]
fn test_div_binary() {
let exact_cases = [
(fbig!(0), fbig!(1), fbig!(0)),
(fbig!(1), fbig!(1), fbig!(1)),
(fbig!(0x1000), fbig!(0x1000), fbig!(1)),
(fbig!(0x1000), fbig!(0x10), fbig!(0x100)),
(fbig!(0x1000), fbig!(-0x10), fbig!(-0x100)),
(fbig!(-0xffff), fbig!(-0xff), fbig!(0x101)),
(fbig!(0x1b), fbig!(0x3), fbig!(0x9)),
];
for (a, b, c) in &exact_cases {
test_div(a, b, c);
if let Ok(Exact(v)) = Context::max(a.context(), b.context()).div(a.repr(), b.repr()) {
assert_eq!(v, *c);
} else {
panic!("the result should be exact!")
}
}
let inexact_cases = [
(fbig!(0x43), fbig!(0x21), fbig!(0x81p-6)),
(fbig!(0x654), fbig!(-0x321), fbig!(-0x817p-10)),
(fbig!(-0x98765), fbig!(-0x43210), fbig!(0x915b1p-18)),
(fbig!(0x1), fbig!(0x9), fbig!(0xep-7)),
(fbig!(0x1), fbig!(0x09), fbig!(0xe3p-11)),
(fbig!(0x1), fbig!(0x009), fbig!(0x1c7p-12)),
(fbig!(0x13), fbig!(-0x9), fbig!(-0x87p-6)),
(fbig!(0x169), fbig!(-0x9), fbig!(-0xa07p-6)),
];
for (a, b, c) in &inexact_cases {
test_div(a, b, c);
if let Ok(Inexact(v, e)) = Context::max(a.context(), b.context()).div(a.repr(), b.repr()) {
assert_eq!(v, *c);
assert_eq!(e, NoOp);
} else {
panic!("the result should be inexact!")
}
}
}
#[test]
fn test_div_decimal() {
let exact_cases = [
(dbig!(0), dbig!(1), dbig!(0)),
(dbig!(1), dbig!(1), dbig!(1)),
(dbig!(1000), dbig!(1000), dbig!(1)),
(dbig!(1000), dbig!(10), dbig!(100)),
(dbig!(1000), dbig!(-10), dbig!(-100)),
(dbig!(-9999), dbig!(-99), dbig!(101)),
(dbig!(27), dbig!(3), dbig!(9)),
];
for (a, b, c) in &exact_cases {
test_div(a, b, c);
if let Ok(Exact(v)) = Context::max(a.context(), b.context()).div(a.repr(), b.repr()) {
assert_eq!(v, *c);
} else {
panic!("the result should be exact!")
}
}
let inexact_cases = [
(dbig!(43), dbig!(21), dbig!(2), NoOp),
(dbig!(654), dbig!(-321), dbig!(-204e-2), SubOne),
(dbig!(-98765), dbig!(-43210), dbig!(22857e-4), AddOne),
(dbig!(1), dbig!(9), dbig!(1e-1), NoOp),
(dbig!(1), dbig!(09), dbig!(11e-2), NoOp),
(dbig!(1), dbig!(009), dbig!(111e-3), NoOp),
(dbig!(13), dbig!(-9), dbig!(-14e-1), NoOp),
(dbig!(169), dbig!(-9), dbig!(-188e-1), SubOne),
(dbig!(1), dbig!(4), dbig!(3e-1), AddOne),
(dbig!(1), dbig!(-4), dbig!(-3e-1), SubOne),
];
for (a, b, c, rnd) in &inexact_cases {
test_div(a, b, c);
if let Ok(Inexact(v, e)) = Context::max(a.context(), b.context()).div(a.repr(), b.repr()) {
assert_eq!(v, *c);
assert_eq!(e, *rnd);
} else {
panic!("the result should be inexact!")
}
}
}
#[test]
#[should_panic]
fn test_div_by_inf() {
let _ = dashu_float::DBig::ONE / dashu_float::DBig::INFINITY;
}
#[test]
#[should_panic]
fn test_div_by_0() {
let _ = dashu_float::DBig::ONE / dashu_float::DBig::ZERO;
}
#[test]
#[should_panic]
fn test_div_by_unlimited_precision() {
let _ = dbig!(1).with_precision(0).unwrap() / dbig!(3).with_precision(0).unwrap();
}
#[test]
#[rustfmt::skip::macros(fbig)]
fn test_rem_binary() {
let test_cases = [
(fbig!(0), fbig!(1), fbig!(0)),
(fbig!(1), fbig!(1), fbig!(0)),
(fbig!(0x1000), fbig!(0x1000), fbig!(0)),
(fbig!(0x1000), fbig!(0x10), fbig!(0)),
(fbig!(0x1000), fbig!(-0x10), fbig!(0)),
(fbig!(0x3), fbig!(0x2), fbig!(1)),
(fbig!(0x3), fbig!(-0x2), fbig!(1)),
(fbig!(-0x3), fbig!(0x2), fbig!(-1)),
(fbig!(0x43), fbig!(0x21), fbig!(0x1)),
(fbig!(0x43), fbig!(0x23), fbig!(0x20)),
(fbig!(0x654), fbig!(-0x321), fbig!(0x12)),
(fbig!(-0x98765), fbig!(-0x43210), fbig!(-0x12345)),
(fbig!(0x1), fbig!(0x9), fbig!(0x1)),
(fbig!(0x1), fbig!(0x9p-4), fbig!(0x7p-4)),
(fbig!(0x1), fbig!(0x9p-8), fbig!(0x4p-8)),
(fbig!(0x13), fbig!(-0x9), fbig!(0x1)),
(fbig!(0x169), fbig!(-0x9), fbig!(0x1)),
];
for (n, d, r) in &test_cases {
test_rem(n, d, r);
}
}
#[test]
fn test_rem_decimal() {
let test_cases = [
(dbig!(0), dbig!(1), dbig!(0)),
(dbig!(1), dbig!(1), dbig!(0)),
(dbig!(1000), dbig!(1000), dbig!(0)),
(dbig!(1000), dbig!(10), dbig!(0)),
(dbig!(1000), dbig!(-10), dbig!(0)),
(dbig!(-9999), dbig!(-99), dbig!(0)),
(dbig!(3), dbig!(2), dbig!(-1)),
(dbig!(3), dbig!(-2), dbig!(-1)),
(dbig!(-3), dbig!(2), dbig!(1)),
(dbig!(27), dbig!(3), dbig!(0)),
(dbig!(43), dbig!(21), dbig!(1)),
(dbig!(43), dbig!(23), dbig!(-3)),
(dbig!(654), dbig!(-321), dbig!(12)),
(dbig!(-98765), dbig!(-43210), dbig!(-12345)),
(dbig!(1), dbig!(9), dbig!(1)),
(dbig!(1), dbig!(9e-1), dbig!(1e-1)),
(dbig!(1), dbig!(9e-2), dbig!(1e-2)),
(dbig!(13), dbig!(-9), dbig!(4)),
(dbig!(169), dbig!(-9), dbig!(-2)),
];
for (n, d, r) in &test_cases {
test_rem(n, d, r);
}
}
#[test]
fn test_rem_rounding_modes() {
use core::str::FromStr;
use dashu_float::{round::mode, FBig};
macro_rules! rem_case {
($mode:ty, $n:literal, $d:literal, $r:literal) => {
assert_eq!(
FBig::<$mode, 10>::from_str($n).unwrap() % FBig::<$mode, 10>::from_str($d).unwrap(),
FBig::<$mode, 10>::from_str($r).unwrap(),
"{} % {} under {}",
$n,
$d,
stringify!($mode)
);
};
}
type Zero = mode::Zero;
type Away = mode::Away;
type Down = mode::Down;
type Up = mode::Up;
type HalfEven = mode::HalfEven;
type HalfAway = mode::HalfAway;
rem_case!(Zero, "20", "10", "0");
rem_case!(Away, "20", "10", "0");
rem_case!(Down, "20", "10", "0");
rem_case!(Up, "20", "10", "0");
rem_case!(HalfEven, "20", "10", "0");
rem_case!(HalfAway, "20", "10", "0");
rem_case!(Zero, "14", "10", "4");
rem_case!(Away, "14", "10", "-6");
rem_case!(Down, "14", "10", "4");
rem_case!(Up, "14", "10", "-6");
rem_case!(HalfEven, "14", "10", "4");
rem_case!(HalfAway, "14", "10", "4");
rem_case!(Zero, "16", "10", "6");
rem_case!(Away, "16", "10", "-4");
rem_case!(Down, "16", "10", "6");
rem_case!(Up, "16", "10", "-4");
rem_case!(HalfEven, "16", "10", "-4");
rem_case!(HalfAway, "16", "10", "-4");
rem_case!(Zero, "15", "10", "5");
rem_case!(Away, "15", "10", "-5");
rem_case!(Down, "15", "10", "5");
rem_case!(Up, "15", "10", "-5");
rem_case!(HalfEven, "15", "10", "-5");
rem_case!(HalfAway, "15", "10", "-5");
rem_case!(Zero, "25", "10", "5");
rem_case!(Away, "25", "10", "-5");
rem_case!(Down, "25", "10", "5");
rem_case!(Up, "25", "10", "-5");
rem_case!(HalfEven, "25", "10", "5");
rem_case!(HalfAway, "25", "10", "-5");
rem_case!(Zero, "-15", "10", "-5");
rem_case!(Away, "-15", "10", "5");
rem_case!(Down, "-15", "10", "5");
rem_case!(Up, "-15", "10", "-5");
rem_case!(HalfEven, "-15", "10", "5");
rem_case!(HalfAway, "-15", "10", "5");
}
#[test]
#[rustfmt::skip::macros(fbig)]
fn test_rem_binary_ties() {
use core::str::FromStr;
use dashu_float::{round::mode, FBig};
type HE = FBig<mode::HalfEven, 2>;
let s = |x: &str| HE::from_str(x).unwrap();
assert_eq!(s("0x3") % s("0x2"), s("-0x1"));
assert_eq!(fbig!(0x3) % fbig!(0x2), fbig!(0x1));
assert_eq!(s("0x3") % s("0x1p1"), s("-0x1"));
assert_eq!(fbig!(0x3) % fbig!(0x1p1), fbig!(0x1));
assert_eq!(s("0x18p3") % s("0x80"), s("-0x40"));
assert_eq!(s("0x18p3") % s("0x180"), s("0xc0"));
assert_eq!(fbig!(0x18p3) % fbig!(0x80), fbig!(0x40)); }
#[test]
#[should_panic]
fn test_rem_by_inf() {
let _ = dashu_float::DBig::ONE % dashu_float::DBig::INFINITY;
}
#[test]
#[should_panic]
fn test_rem_by_0() {
let _ = dashu_float::DBig::ONE % dashu_float::DBig::ZERO;
}
#[test]
#[rustfmt::skip::macros(fbig)]
fn test_div_rem_euclid_binary() {
let test_cases = [
(fbig!(0), fbig!(1), ibig!(0), fbig!(0)),
(fbig!(1), fbig!(1), ibig!(1), fbig!(0)),
(fbig!(0x1000), fbig!(0x1000), ibig!(1), fbig!(0)),
(fbig!(0x1000), fbig!(0x10), ibig!(0x100), fbig!(0)),
(fbig!(0x1000), fbig!(-0x10), ibig!(-0x100), fbig!(0)),
(fbig!(-0xffff), fbig!(-0xff), ibig!(0x101), fbig!(0)),
(fbig!(0x1b), fbig!(0x3), ibig!(9), fbig!(0)),
(fbig!(0x43), fbig!(0x21), ibig!(2), fbig!(0x1)),
(fbig!(0x654), fbig!(-0x321), ibig!(-2), fbig!(0x12)),
(fbig!(-0x98765), fbig!(-0x43210), ibig!(3), fbig!(0x30ecb)),
(fbig!(0x1), fbig!(0x9), ibig!(0), fbig!(0x1)),
(fbig!(0x1), fbig!(0x9p-4), ibig!(1), fbig!(0x7p-4)),
(fbig!(0x1), fbig!(0x9p-8), ibig!(28), fbig!(0x4p-8)),
(fbig!(0x13), fbig!(-0x9), ibig!(-2), fbig!(0x1)),
(fbig!(0x169), fbig!(-0x9), ibig!(-40), fbig!(0x1)),
];
for (n, d, q, r) in &test_cases {
assert_eq!(n.div_euclid(d), *q);
assert_eq!(n.rem_euclid(d), *r);
let (quotient, remainder) = n.div_rem_euclid(d);
assert_eq!(quotient, *q);
assert_eq!(remainder, *r);
}
}
#[test]
fn test_div_rem_euclid_decimal() {
let test_cases = [
(dbig!(0), dbig!(1), ibig!(0), dbig!(0)),
(dbig!(1), dbig!(1), ibig!(1), dbig!(0)),
(dbig!(1000), dbig!(1000), ibig!(1), dbig!(0)),
(dbig!(1000), dbig!(10), ibig!(100), dbig!(0)),
(dbig!(1000), dbig!(-10), ibig!(-100), dbig!(0)),
(dbig!(-9999), dbig!(-99), ibig!(101), dbig!(0)),
(dbig!(27), dbig!(3), ibig!(9), dbig!(0)),
(dbig!(43), dbig!(21), ibig!(2), dbig!(1)),
(dbig!(654), dbig!(-321), ibig!(-2), dbig!(12)),
(dbig!(-98765), dbig!(-43210), ibig!(3), dbig!(30865)),
(dbig!(1), dbig!(9), ibig!(0), dbig!(1)),
(dbig!(1), dbig!(9e-1), ibig!(1), dbig!(1e-1)),
(dbig!(1), dbig!(9e-2), ibig!(11), dbig!(1e-2)),
(dbig!(13), dbig!(-9), ibig!(-1), dbig!(4)),
(dbig!(169), dbig!(-9), ibig!(-18), dbig!(7)),
];
for (n, d, q, r) in &test_cases {
assert_eq!(n.div_euclid(d), *q);
assert_eq!(n.rem_euclid(d), *r);
let (quotient, remainder) = n.div_rem_euclid(d);
assert_eq!(quotient, *q);
assert_eq!(remainder, *r);
}
}