use spirix::*;
type S = ScalarF3E3;
#[derive(Debug, Clone, Copy, PartialEq)]
enum Class {
Zero,
Vanished,
Normal,
Exploded,
Infinity,
Undefined,
}
fn classify(s: &S) -> Class {
if s.is_undefined() {
Class::Undefined
} else if s.is_zero() {
Class::Zero
} else if s.is_infinite() {
Class::Infinity
} else if s.exploded() {
Class::Exploded
} else if s.vanished() {
Class::Vanished
} else {
Class::Normal
}
}
fn class_name(c: Class) -> &'static str {
match c {
Class::Zero => "[0]",
Class::Vanished => "[↓]",
Class::Normal => "[#]",
Class::Exploded => "[↑]",
Class::Infinity => "[∞]",
Class::Undefined => "[℘]",
}
}
fn check(op: &str, a: S, b: S, result: S, expected: &[Class]) {
let rc = classify(&result);
if !expected.contains(&rc) {
let ac = classify(&a);
let bc = classify(&b);
panic!(
"{} {} {} = {} (class {}), expected one of {:?}\n a = {:?}, b = {:?}, result = {:?}",
class_name(ac),
op,
class_name(bc),
class_name(rc),
rc as u8,
expected.iter().map(|c| class_name(*c)).collect::<Vec<_>>(),
a,
b,
result
);
}
}
fn zeros() -> Vec<S> {
vec![S::ZERO]
}
fn vanished_pos() -> Vec<S> {
vec![S::VANISHED_POS]
}
fn vanished_neg() -> Vec<S> {
vec![S::VANISHED_NEG]
}
fn normals_pos() -> Vec<S> {
vec![S::from(1), S::from(42)]
}
fn normals_neg() -> Vec<S> {
vec![S::from(-1), S::from(-42)]
}
fn exploded_pos() -> Vec<S> {
vec![S::EXPLODED_POS]
}
fn exploded_neg() -> Vec<S> {
vec![S::EXPLODED_NEG]
}
fn infinities() -> Vec<S> {
vec![S::INFINITY]
}
fn undefineds() -> Vec<S> {
vec![S::ZERO / S::ZERO] }
use Class::*;
#[test]
fn addition_truth_table() {
let z = S::ZERO;
let vp = S::VANISHED_POS;
let vn = S::VANISHED_NEG;
let np = S::from(7);
let nn = S::from(-7);
let ep = S::EXPLODED_POS;
let en = S::EXPLODED_NEG;
let inf = S::INFINITY;
let und = S::ZERO / S::ZERO;
check("+", z, z, z + z, &[Zero]);
check("+", z, vp, z + vp, &[Vanished]);
check("+", z, vn, z + vn, &[Vanished]);
check("+", vp, z, vp + z, &[Vanished]);
check("+", z, np, z + np, &[Normal]);
check("+", np, z, np + z, &[Normal]);
check("+", vp, vp, vp + vp, &[Undefined]);
check("+", vp, vn, vp + vn, &[Undefined]);
check("+", vp, np, vp + np, &[Normal]);
check("+", np, vp, np + vp, &[Normal]);
check("+", np, nn, np + nn, &[Zero, Vanished, Normal, Exploded]);
check("+", np, np, np + np, &[Zero, Vanished, Normal, Exploded]);
check("+", ep, z, ep + z, &[Exploded]);
check("+", en, z, en + z, &[Exploded]);
check("+", ep, vp, ep + vp, &[Exploded]);
check("+", ep, vn, ep + vn, &[Exploded]);
check("+", ep, np, ep + np, &[Undefined]);
check("+", z, ep, z + ep, &[Exploded]);
check("+", vp, ep, vp + ep, &[Exploded]);
check("+", np, ep, np + ep, &[Undefined]);
check("+", ep, ep, ep + ep, &[Undefined]);
check("+", ep, en, ep + en, &[Undefined]);
check("+", inf, z, inf + z, &[Infinity]);
check("+", inf, vp, inf + vp, &[Infinity]);
check("+", inf, np, inf + np, &[Infinity]);
check("+", inf, ep, inf + ep, &[Infinity]);
check("+", inf, inf, inf + inf, &[Infinity]);
check("+", z, inf, z + inf, &[Infinity]);
check("+", vp, inf, vp + inf, &[Infinity]);
check("+", np, inf, np + inf, &[Infinity]);
check("+", ep, inf, ep + inf, &[Infinity]);
check("+", und, z, und + z, &[Undefined]);
check("+", und, np, und + np, &[Undefined]);
check("+", und, inf, und + inf, &[Undefined]);
check("+", z, und, z + und, &[Undefined]);
check("+", np, und, np + und, &[Undefined]);
check("+", inf, und, inf + und, &[Undefined]);
}
#[test]
fn subtraction_truth_table() {
let z = S::ZERO;
let vp = S::VANISHED_POS;
let vn = S::VANISHED_NEG;
let np = S::from(7);
let nn = S::from(-7);
let ep = S::EXPLODED_POS;
let en = S::EXPLODED_NEG;
let inf = S::INFINITY;
let und = S::ZERO / S::ZERO;
check("-", z, z, z - z, &[Zero]);
check("-", np, z, np - z, &[Normal, Vanished, Exploded]);
check("-", nn, z, nn - z, &[Normal, Vanished, Exploded]);
check("-", z, np, z - np, &[Normal, Vanished, Exploded]);
check("-", vp, vp, vp - vp, &[Undefined]);
check("-", vp, vn, vp - vn, &[Undefined]);
check("-", np, np, np - np, &[Zero, Vanished, Normal, Exploded]);
check("-", np, nn, np - nn, &[Zero, Vanished, Normal, Exploded]);
check("-", ep, z, ep - z, &[Exploded]);
check("-", ep, vp, ep - vp, &[Exploded]);
check("-", ep, vn, ep - vn, &[Exploded]);
check("-", ep, np, ep - np, &[Undefined]);
check("-", z, ep, z - ep, &[Exploded]);
check("-", vp, ep, vp - ep, &[Exploded]);
check("-", np, ep, np - ep, &[Undefined]);
check("-", ep, ep, ep - ep, &[Undefined]);
check("-", inf, z, inf - z, &[Infinity]);
check("-", inf, vp, inf - vp, &[Infinity]);
check("-", inf, np, inf - np, &[Infinity]);
check("-", inf, ep, inf - ep, &[Infinity]);
check("-", inf, inf, inf - inf, &[Infinity]);
check("-", z, inf, z - inf, &[Infinity]);
check("-", vp, inf, vp - inf, &[Infinity]);
check("-", np, inf, np - inf, &[Infinity]);
check("-", ep, inf, ep - inf, &[Infinity]);
check("-", und, np, und - np, &[Undefined]);
check("-", und, inf, und - inf, &[Undefined]);
check("-", np, und, np - und, &[Undefined]);
check("-", inf, und, inf - und, &[Undefined]);
}
#[test]
fn multiplication_truth_table() {
let z = S::ZERO;
let vp = S::VANISHED_POS;
let vn = S::VANISHED_NEG;
let np = S::from(7);
let nn = S::from(-3);
let ep = S::EXPLODED_POS;
let en = S::EXPLODED_NEG;
let inf = S::INFINITY;
let und = S::ZERO / S::ZERO;
check("×", z, z, z * z, &[Zero]);
check("×", z, vp, z * vp, &[Zero]);
check("×", z, np, z * np, &[Zero]);
check("×", z, ep, z * ep, &[Zero]);
check("×", np, z, np * z, &[Zero]);
check("×", ep, z, ep * z, &[Zero]);
check("×", z, inf, z * inf, &[Undefined]);
check("×", inf, z, inf * z, &[Undefined]);
check("×", vp, vp, vp * vp, &[Vanished]);
check("×", vp, vn, vp * vn, &[Vanished]);
check("×", vp, np, vp * np, &[Vanished]);
check("×", np, vp, np * vp, &[Vanished]);
check("×", vp, ep, vp * ep, &[Undefined]);
check("×", ep, vp, ep * vp, &[Undefined]);
check("×", vp, inf, vp * inf, &[Infinity]);
check("×", inf, vp, inf * vp, &[Infinity]);
check("×", np, nn, np * nn, &[Normal, Vanished, Exploded]);
check("×", np, np, np * np, &[Normal, Vanished, Exploded]);
check("×", np, ep, np * ep, &[Exploded]);
check("×", ep, np, ep * np, &[Exploded]);
check("×", np, inf, np * inf, &[Infinity]);
check("×", inf, np, inf * np, &[Infinity]);
check("×", ep, ep, ep * ep, &[Exploded]);
check("×", ep, en, ep * en, &[Exploded]);
check("×", ep, inf, ep * inf, &[Infinity]);
check("×", inf, ep, inf * ep, &[Infinity]);
check("×", inf, inf, inf * inf, &[Infinity]);
check("×", und, np, und * np, &[Undefined]);
check("×", np, und, np * und, &[Undefined]);
}
#[test]
fn division_truth_table() {
let z = S::ZERO;
let vp = S::VANISHED_POS;
let np = S::from(7);
let nn = S::from(-3);
let ep = S::EXPLODED_POS;
let en = S::EXPLODED_NEG;
let inf = S::INFINITY;
let und = S::ZERO / S::ZERO;
check("÷", z, np, z / np, &[Zero]);
check("÷", z, nn, z / nn, &[Zero]);
check("÷", z, ep, z / ep, &[Zero]);
check("÷", z, inf, z / inf, &[Zero]);
check("÷", np, z, np / z, &[Infinity]);
check("÷", nn, z, nn / z, &[Infinity]);
check("÷", vp, np, vp / np, &[Vanished]);
check("÷", np, vp, np / vp, &[Exploded]);
check("÷", np, nn, np / nn, &[Normal, Vanished, Exploded]);
check("÷", np, np, np / np, &[Normal, Vanished, Exploded]);
check("÷", np, ep, np / ep, &[Vanished]);
check("÷", np, inf, np / inf, &[Zero]);
check("÷", ep, np, ep / np, &[Exploded]);
check("÷", ep, ep, ep / ep, &[Undefined]);
check("÷", ep, en, ep / en, &[Undefined]);
check("÷", ep, inf, ep / inf, &[Zero]);
check("÷", inf, np, inf / np, &[Infinity]);
check("÷", inf, inf, inf / inf, &[Undefined]);
check("÷", inf, ep, inf / ep, &[Infinity]);
check("÷", und, np, und / np, &[Undefined]);
check("÷", np, und, np / und, &[Undefined]);
}
#[test]
fn modulus_truth_table() {
let z = S::ZERO;
let vp = S::VANISHED_POS;
let vn = S::VANISHED_NEG;
let np = S::from(7);
let nn = S::from(-3);
let ep = S::EXPLODED_POS;
let en = S::EXPLODED_NEG;
let inf = S::INFINITY;
let und = S::ZERO / S::ZERO;
check("%", z, z, z % z, &[Zero]);
check("%", z, np, z % np, &[Zero]);
check("%", z, vp, z % vp, &[Zero]);
check("%", z, ep, z % ep, &[Zero]);
check("%", z, inf, z % inf, &[Zero]);
check("%", np, z, np % z, &[Zero]);
check("%", vp, z, vp % z, &[Zero]);
check("%", ep, z, ep % z, &[Zero]);
check("%", np, nn, np % nn, &[Zero, Vanished, Normal]);
check(
"%",
S::from(6),
S::from(3),
S::from(6) % S::from(3),
&[Zero],
);
check(
"%",
S::from(7),
S::from(3),
S::from(7) % S::from(3),
&[Zero, Vanished, Normal],
);
check("%", np, vp, np % vp, &[Undefined]);
check("%", vp, vp, vp % vp, &[Undefined]);
check("%", vp, vn, vp % vn, &[Undefined]);
check("%", vp, np, vp % np, &[Vanished]); check("%", vn, np, vn % np, &[Normal]);
check("%", np, ep, np % ep, &[Normal]); check("%", np, en, np % en, &[Undefined]);
check("%", vp, ep, vp % ep, &[Vanished]); check("%", vn, ep, vn % ep, &[Exploded]);
check("%", ep, np, ep % np, &[Undefined]);
check("%", ep, vp, ep % vp, &[Undefined]);
check("%", ep, ep, ep % ep, &[Undefined]);
check("%", inf, np, inf % np, &[Undefined]);
check("%", inf, ep, inf % ep, &[Undefined]);
check("%", inf, inf, inf % inf, &[Undefined]);
check("%", np, inf, np % inf, &[Undefined]);
check("%", vp, inf, vp % inf, &[Undefined]);
check("%", und, np, und % np, &[Undefined]);
check("%", np, und, np % und, &[Undefined]);
}
fn check_unary(op: &str, input_name: &str, x: S, result: S, expected: &[Class]) {
let rc = classify(&result);
if !expected.contains(&rc) {
panic!(
"{}({}) = {:?} (class {} = {})\n expected one of {:?}\n input : {:?}\n result : {:?}",
op, input_name, rc, rc as u8, class_name(rc),
expected.iter().map(|c| class_name(*c)).collect::<Vec<_>>(),
x, result
);
}
}
#[test]
fn sqrt_unary_truth_table() {
let z = S::ZERO;
let vp = S::VANISHED_POS;
let vn = S::VANISHED_NEG;
let np = S::from(4); let nn = S::from(-4); let ep = S::EXPLODED_POS;
let en = S::EXPLODED_NEG;
let inf = S::INFINITY;
let und = S::ZERO / S::ZERO;
check_unary("sqrt", "[0]", z, z.sqrt(), &[Zero]);
check_unary("sqrt", "[+↓]", vp, vp.sqrt(), &[Undefined]);
check_unary("sqrt", "[-↓]", vn, vn.sqrt(), &[Undefined]);
check_unary("sqrt", "[+#]", np, np.sqrt(), &[Normal]);
check_unary("sqrt", "[-#]", nn, nn.sqrt(), &[Undefined]);
check_unary("sqrt", "[+↑]", ep, ep.sqrt(), &[Undefined]);
check_unary("sqrt", "[-↑]", en, en.sqrt(), &[Undefined]);
check_unary("sqrt", "[∞]", inf, inf.sqrt(), &[Infinity]);
check_unary("sqrt", "[℘]", und, und.sqrt(), &[Undefined]);
}
#[test]
fn lb_unary_truth_table() {
let z = S::ZERO;
let vp = S::VANISHED_POS;
let vn = S::VANISHED_NEG;
let np = S::from(2); let nn = S::from(-2); let one = S::ONE; let ep = S::EXPLODED_POS;
let en = S::EXPLODED_NEG;
let inf = S::INFINITY;
let und = S::ZERO / S::ZERO;
check_unary("lb", "[0]", z, z.lb(), &[Infinity]);
check_unary("lb", "[+↓]", vp, vp.lb(), &[Undefined]);
check_unary("lb", "[-↓]", vn, vn.lb(), &[Undefined]);
check_unary("lb", "[+# >1]", np, np.lb(), &[Normal]);
check_unary("lb", "[+# =1]", one, one.lb(), &[Zero, Normal]); check_unary("lb", "[-#]", nn, nn.lb(), &[Undefined]);
check_unary("lb", "[+↑]", ep, ep.lb(), &[Undefined]);
check_unary("lb", "[-↑]", en, en.lb(), &[Undefined]);
check_unary("lb", "[∞]", inf, inf.lb(), &[Infinity]);
check_unary("lb", "[℘]", und, und.lb(), &[Undefined]);
}
#[test]
fn ln_unary_truth_table() {
let z = S::ZERO;
let np = S::from(2);
let one = S::ONE;
let nn = S::from(-2);
let und = S::ZERO / S::ZERO;
check_unary("ln", "[0]", z, z.ln(), &[Infinity]);
check_unary("ln", "[+#]", np, np.ln(), &[Normal]);
check_unary("ln", "[+# =1]", one, one.ln(), &[Zero, Normal]);
check_unary("ln", "[-#]", nn, nn.ln(), &[Undefined]);
check_unary("ln", "[℘]", und, und.ln(), &[Undefined]);
}
#[test]
fn exp_unary_truth_table() {
let z = S::ZERO;
let vp = S::VANISHED_POS;
let vn = S::VANISHED_NEG;
let np = S::from(2); let nn = S::from(-2); let ep = S::EXPLODED_POS;
let en = S::EXPLODED_NEG;
let inf = S::INFINITY;
let und = S::ZERO / S::ZERO;
check_unary("exp", "[0]", z, z.exp(), &[Normal]); check_unary("exp", "[+↓]", vp, vp.exp(), &[Normal]); check_unary("exp", "[-↓]", vn, vn.exp(), &[Normal]); check_unary("exp", "[+#]", np, np.exp(), &[Normal, Exploded]);
check_unary("exp", "[-#]", nn, nn.exp(), &[Normal, Vanished, Zero]);
check_unary(
"exp",
"[+↑]",
ep,
ep.exp(),
&[Exploded, Infinity, Undefined],
);
check_unary("exp", "[-↑]", en, en.exp(), &[Zero]); check_unary("exp", "[∞]", inf, inf.exp(), &[Infinity]);
check_unary("exp", "[℘]", und, und.exp(), &[Undefined]);
}
#[test]
fn not_unary_truth_table() {
let z = S::ZERO;
let inf = S::INFINITY;
let vp = S::VANISHED_POS;
let vn = S::VANISHED_NEG;
let np = S::from(2);
let nn = S::from(-2);
let ep = S::EXPLODED_POS;
let en = S::EXPLODED_NEG;
let und = S::ZERO / S::ZERO;
let not_z = (!z).into();
let not_inf = (!inf).into();
assert_eq!(classify(¬_z), Infinity, "NOT([0]) should be [∞]");
assert_eq!(classify(¬_inf), Zero, "NOT([∞]) should be [0]");
let cases: &[(&str, S, Class)] = &[
("[+↓]", vp, Vanished),
("[-↓]", vn, Vanished),
("[+#]", np, Normal),
("[-#]", nn, Normal),
("[+↑]", ep, Exploded),
("[-↑]", en, Exploded),
];
for (name, x, expected_class) in cases {
let r: S = (!*x).into();
assert_eq!(
classify(&r),
*expected_class,
"NOT({}) class should be {:?}",
name,
expected_class
);
assert_ne!(
x.is_negative(),
r.is_negative(),
"NOT({}) should flip sign",
name
);
}
assert_eq!(classify(&!und), Undefined);
}
fn unary_reps() -> (S, S, S, S, S, S, S, S, S) {
(
S::ZERO,
S::VANISHED_POS,
S::VANISHED_NEG,
S::from(2),
S::from(-2),
S::EXPLODED_POS,
S::EXPLODED_NEG,
S::INFINITY,
S::ZERO / S::ZERO,
)
}
#[test]
fn neg_unary_truth_table() {
let (z, vp, vn, np, nn, ep, en, inf, und) = unary_reps();
check_unary("neg", "[0]", z, -z, &[Zero]);
check_unary("neg", "[+↓]", vp, -vp, &[Vanished]);
check_unary("neg", "[-↓]", vn, -vn, &[Vanished]);
check_unary("neg", "[+#]", np, -np, &[Normal]);
check_unary("neg", "[-#]", nn, -nn, &[Normal]);
check_unary("neg", "[+↑]", ep, -ep, &[Exploded]);
check_unary("neg", "[-↑]", en, -en, &[Exploded]);
check_unary("neg", "[∞]", inf, -inf, &[Infinity]);
check_unary("neg", "[℘]", und, -und, &[Undefined]);
}
#[test]
fn abs_unary_truth_table() {
let (z, vp, vn, np, nn, ep, en, inf, und) = unary_reps();
check_unary("abs", "[0]", z, z.magnitude(), &[Zero]);
check_unary("abs", "[+↓]", vp, vp.magnitude(), &[Vanished]);
check_unary("abs", "[-↓]", vn, vn.magnitude(), &[Vanished]);
check_unary("abs", "[+#]", np, np.magnitude(), &[Normal]);
check_unary("abs", "[-#]", nn, nn.magnitude(), &[Normal]);
check_unary("abs", "[+↑]", ep, ep.magnitude(), &[Exploded]);
check_unary("abs", "[-↑]", en, en.magnitude(), &[Exploded]);
check_unary("abs", "[∞]", inf, inf.magnitude(), &[Infinity]);
check_unary("abs", "[℘]", und, und.magnitude(), &[Undefined]);
for x in [vn, nn, en] {
assert!(!x.magnitude().is_negative(), "abs should be non-negative");
}
}
#[test]
fn sign_unary_truth_table() {
let (z, vp, vn, np, nn, ep, en, inf, und) = unary_reps();
check_unary("sign", "[0]", z, z.sign(), &[Undefined]);
check_unary("sign", "[+↓]", vp, vp.sign(), &[Normal]);
check_unary("sign", "[-↓]", vn, vn.sign(), &[Normal]);
check_unary("sign", "[+#]", np, np.sign(), &[Normal]);
check_unary("sign", "[-#]", nn, nn.sign(), &[Normal]);
check_unary("sign", "[+↑]", ep, ep.sign(), &[Normal]);
check_unary("sign", "[-↑]", en, en.sign(), &[Normal]);
check_unary("sign", "[∞]", inf, inf.sign(), &[Undefined]);
check_unary("sign", "[℘]", und, und.sign(), &[Undefined]);
}
#[test]
fn recip_unary_truth_table() {
let (z, vp, vn, np, nn, ep, en, inf, und) = unary_reps();
check_unary("recip", "[0]", z, z.reciprocal(), &[Infinity]);
check_unary("recip", "[+↓]", vp, vp.reciprocal(), &[Exploded]);
check_unary("recip", "[-↓]", vn, vn.reciprocal(), &[Exploded]);
check_unary("recip", "[+#]", np, np.reciprocal(), &[Normal]); check_unary("recip", "[-#]", nn, nn.reciprocal(), &[Normal]);
check_unary("recip", "[+↑]", ep, ep.reciprocal(), &[Vanished]);
check_unary("recip", "[-↑]", en, en.reciprocal(), &[Vanished]);
check_unary("recip", "[∞]", inf, inf.reciprocal(), &[Zero]);
check_unary("recip", "[℘]", und, und.reciprocal(), &[Undefined]);
}
#[test]
fn floor_ceil_round_unary_truth_tables() {
let (z, _vp, _vn, _np, _nn, ep, en, inf, und) = unary_reps();
let half = S::from(1) / S::from(2); let neg_half = S::from(-1) / S::from(2); let two = S::from(2);
check_unary("floor", "0.5", half, half.floor(), &[Zero]);
check_unary("floor", "-0.5", neg_half, neg_half.floor(), &[Normal]); check_unary("floor", "2", two, two.floor(), &[Normal]);
check_unary("floor", "[+↑]", ep, ep.floor(), &[Exploded]);
check_unary("floor", "[-↑]", en, en.floor(), &[Exploded]);
check_unary("floor", "[∞]", inf, inf.floor(), &[Infinity]);
check_unary("floor", "[℘]", und, und.floor(), &[Undefined]);
check_unary("ceil", "0.5", half, half.ceil(), &[Normal]); check_unary("ceil", "-0.5", neg_half, neg_half.ceil(), &[Zero]);
check_unary("ceil", "[+↑]", ep, ep.ceil(), &[Exploded]);
check_unary("ceil", "[∞]", inf, inf.ceil(), &[Infinity]);
check_unary("ceil", "[℘]", und, und.ceil(), &[Undefined]);
check_unary("round", "0.5", half, half.round(), &[Zero]);
let two_half = S::from(5) / S::from(2); check_unary("round", "2.5", two_half, two_half.round(), &[Normal]);
check_unary("round", "[+↑]", ep, ep.round(), &[Exploded]);
check_unary("round", "[∞]", inf, inf.round(), &[Infinity]);
check_unary("round", "0", z, z.round(), &[Zero]);
}
#[test]
fn frac_unary_truth_table() {
let (z, vp, _vn, _np, _nn, ep, en, inf, und) = unary_reps();
let half = S::from(1) / S::from(2);
let two = S::from(2);
check_unary("frac", "0", z, z.frac(), &[Zero]);
check_unary("frac", "2", two, two.frac(), &[Zero]); check_unary("frac", "0.5", half, half.frac(), &[Normal]);
check_unary("frac", "[+↓]", vp, vp.frac(), &[Zero, Vanished]); check_unary("frac", "[+↑]", ep, ep.frac(), &[Zero]); check_unary("frac", "[-↑]", en, en.frac(), &[Zero]);
check_unary("frac", "[∞]", inf, inf.frac(), &[Undefined]); check_unary("frac", "[℘]", und, und.frac(), &[Undefined]);
}
#[test]
fn sin_cos_tan_truth_tables() {
let (z, vp, vn, _np, _nn, ep, en, inf, und) = unary_reps();
let half = S::from(1) / S::from(2);
let one = S::ONE;
let inrange = &[Zero, Vanished, Normal];
check_unary("sin", "[0]", z, z.sin(), &[Zero]);
check_unary("sin", "[+↓]", vp, vp.sin(), &[Vanished]);
check_unary("sin", "[-↓]", vn, vn.sin(), &[Vanished]);
check_unary("sin", "0.5", half, half.sin(), inrange);
check_unary("sin", "1", one, one.sin(), inrange);
check_unary("sin", "[+↑]", ep, ep.sin(), &[Undefined]);
check_unary("sin", "[-↑]", en, en.sin(), &[Undefined]);
check_unary("sin", "[∞]", inf, inf.sin(), &[Undefined]);
check_unary("sin", "[℘]", und, und.sin(), &[Undefined]);
check_unary("cos", "[0]", z, z.cos(), &[Normal]); check_unary("cos", "[+↓]", vp, vp.cos(), &[Normal]); check_unary("cos", "0.5", half, half.cos(), inrange);
check_unary("cos", "[+↑]", ep, ep.cos(), &[Undefined]);
check_unary("cos", "[∞]", inf, inf.cos(), &[Undefined]);
check_unary("cos", "[℘]", und, und.cos(), &[Undefined]);
check_unary("tan", "[0]", z, z.tan(), &[Zero]);
check_unary("tan", "[+↓]", vp, vp.tan(), &[Vanished]);
check_unary(
"tan",
"1",
one,
one.tan(),
&[Zero, Vanished, Normal, Exploded, Infinity],
);
check_unary("tan", "[+↑]", ep, ep.tan(), &[Undefined]);
check_unary("tan", "[∞]", inf, inf.tan(), &[Undefined]);
check_unary("tan", "[℘]", und, und.tan(), &[Undefined]);
}
#[test]
fn asin_acos_atan_truth_tables() {
let (z, vp, vn, _np, _nn, ep, en, inf, und) = unary_reps();
let half = S::from(1) / S::from(2);
let neg_half = S::from(-1) / S::from(2);
let two = S::from(2);
check_unary("asin", "[0]", z, z.asin(), &[Zero]);
check_unary("asin", "[+↓]", vp, vp.asin(), &[Vanished]);
check_unary("asin", "[-↓]", vn, vn.asin(), &[Vanished]);
check_unary("asin", "0.5", half, half.asin(), &[Normal]);
check_unary("asin", "-0.5", neg_half, neg_half.asin(), &[Normal]);
check_unary("asin", "2 (>1)", two, two.asin(), &[Undefined]);
check_unary("asin", "[+↑]", ep, ep.asin(), &[Undefined]);
check_unary("asin", "[∞]", inf, inf.asin(), &[Undefined]);
check_unary("asin", "[℘]", und, und.asin(), &[Undefined]);
check_unary("acos", "[0]", z, z.acos(), &[Normal]); check_unary("acos", "0.5", half, half.acos(), &[Normal]);
check_unary("acos", "2 (>1)", two, two.acos(), &[Undefined]);
check_unary("acos", "[+↑]", ep, ep.acos(), &[Undefined]);
check_unary("acos", "[∞]", inf, inf.acos(), &[Undefined]);
check_unary("acos", "[℘]", und, und.acos(), &[Undefined]);
check_unary("atan", "[0]", z, z.atan(), &[Zero]);
check_unary("atan", "[+↓]", vp, vp.atan(), &[Vanished]);
check_unary("atan", "0.5", half, half.atan(), &[Normal]);
check_unary("atan", "[+↑]", ep, ep.atan(), &[Normal]); check_unary("atan", "[-↑]", en, en.atan(), &[Normal]); check_unary("atan", "[∞]", inf, inf.atan(), &[Undefined]); check_unary("atan", "[℘]", und, und.atan(), &[Undefined]);
}
#[test]
fn sinh_cosh_tanh_truth_tables() {
let (z, vp, vn, _np, _nn, ep, en, inf, und) = unary_reps();
let two = S::from(2);
let neg_two = S::from(-2);
check_unary("sinh", "[0]", z, z.sinh(), &[Zero]);
check_unary("sinh", "[+↓]", vp, vp.sinh(), &[Vanished]);
check_unary("sinh", "[-↓]", vn, vn.sinh(), &[Vanished]);
check_unary("sinh", "2", two, two.sinh(), &[Normal, Exploded]);
check_unary("sinh", "[+↑]", ep, ep.sinh(), &[Exploded]);
check_unary("sinh", "[-↑]", en, en.sinh(), &[Exploded]);
check_unary("sinh", "[∞]", inf, inf.sinh(), &[Infinity]);
check_unary("sinh", "[℘]", und, und.sinh(), &[Undefined]);
check_unary("cosh", "[0]", z, z.cosh(), &[Normal]); check_unary("cosh", "[+↓]", vp, vp.cosh(), &[Normal]);
check_unary("cosh", "2", two, two.cosh(), &[Normal, Exploded]);
check_unary("cosh", "-2", neg_two, neg_two.cosh(), &[Normal, Exploded]);
check_unary("cosh", "[+↑]", ep, ep.cosh(), &[Exploded]);
check_unary("cosh", "[-↑]", en, en.cosh(), &[Exploded]);
check_unary("cosh", "[∞]", inf, inf.cosh(), &[Infinity]);
check_unary("cosh", "[℘]", und, und.cosh(), &[Undefined]);
assert!(!neg_two.cosh().is_negative());
check_unary("tanh", "[0]", z, z.tanh(), &[Zero]);
check_unary("tanh", "[+↓]", vp, vp.tanh(), &[Vanished]);
check_unary("tanh", "2", two, two.tanh(), &[Normal]); check_unary("tanh", "[+↑]", ep, ep.tanh(), &[Normal]); check_unary("tanh", "[-↑]", en, en.tanh(), &[Normal]); check_unary("tanh", "[℘]", und, und.tanh(), &[Undefined]);
}
#[test]
fn log_truth_table() {
let two = S::from(2);
let four = S::from(4);
let five = S::from(5);
let z = S::ZERO;
let inf = S::INFINITY;
let ep = S::EXPLODED_POS;
let und = z / z;
check("$", four, two, four.log(two), &[Normal]); check("$", S::ONE, two, S::ONE.log(two), &[Zero]); check("$", inf, two, inf.log(two), &[Infinity]); check("$", z, two, z.log(two), &[Infinity]); check("$", five, inf, five.log(inf), &[Zero]); check("$", five, z, five.log(z), &[Zero]); check("$", five, S::ONE, five.log(S::ONE), &[Undefined]); check("$", S::from(-4), two, S::from(-4).log(two), &[Undefined]); check("$", four, S::from(-2), four.log(S::from(-2)), &[Undefined]); check("$", ep, two, ep.log(two), &[Undefined]); check("$", five, ep, five.log(ep), &[Undefined]); check("$", inf, inf, inf.log(inf), &[Undefined]); check("$", und, two, und.log(two), &[Undefined]); check("$", two, und, two.log(und), &[Undefined]);
}
#[test]
fn pow_truth_table() {
let two = S::from(2);
let three = S::from(3);
let half = S::from(1) / S::from(2);
let z = S::ZERO;
let inf = S::INFINITY;
let und = z / z;
check("^", two, three, two.pow(three), &[Normal, Exploded]); check("^", three, two, three.pow(two), &[Normal]); check("^", three, z, three.pow(z), &[Normal]);
check("^", z, z, z.pow(z), &[Normal]); check("^", z, two, z.pow(two), &[Zero]);
check("^", z, half, z.pow(half), &[Zero]);
check("^", z, S::from(-2), z.pow(S::from(-2)), &[Infinity]);
check("^", two, S::from(200), two.pow(S::from(200)), &[Exploded]);
check("^", two, S::from(-200), two.pow(S::from(-200)), &[Vanished]);
check("^", S::from(-2), half, S::from(-2).pow(half), &[Undefined]);
check("^", und, two, und.pow(two), &[Undefined]);
check("^", two, und, two.pow(und), &[Undefined]);
let inf = S::INFINITY;
check("^", inf, two, inf.pow(two), &[Infinity]);
check("^", inf, half, inf.pow(half), &[Infinity]);
check("^", inf, S::from(-2), inf.pow(S::from(-2)), &[Zero]);
check("^", inf, half, inf.pow(-half), &[Zero]);
check("^", inf, z, inf.pow(z), &[Normal]); check("^", inf, inf, inf.pow(S::EXPLODED_POS), &[Infinity]);
check("^", z, z, z.pow(S::VANISHED_POS), &[Zero]);
check("^", z, z, z.pow(S::VANISHED_NEG), &[Infinity]);
}
#[test]
fn pow_escaped_base_truth_table() {
let vp = S::VANISHED_POS;
let vn = S::VANISHED_NEG;
let ep = S::EXPLODED_POS;
let en = S::EXPLODED_NEG;
let one = S::ONE;
let two = S::from(2);
let three = S::from(3);
let half = S::from(1) / S::from(2);
let five_halves = S::from(5) / S::from(2);
let inf = S::INFINITY;
let r = vp.pow(one);
assert_eq!(
(r.fraction, r.exponent),
(vp.fraction, vp.exponent),
"↓^1 must be the identity"
);
check("^", vp, two, vp.pow(two), &[Vanished]); check("^", vn, two, vn.pow(two), &[Vanished]);
assert!(!vn.pow(two).is_negative(), "(-↓)² is positive");
check("^", vn, three, vn.pow(three), &[Vanished]);
assert!(vn.pow(three).is_negative(), "(-↓)³ is negative");
check("^", vp, S::from(-2), vp.pow(S::from(-2)), &[Exploded]); check("^", ep, two, ep.pow(two), &[Exploded]); check("^", en, three, en.pow(three), &[Exploded]);
assert!(en.pow(three).is_negative(), "(-↑)³ is negative");
check("^", ep, S::from(-2), ep.pow(S::from(-2)), &[Vanished]);
check("^", vp, five_halves, vp.pow(five_halves), &[Vanished]);
check("^", ep, five_halves, ep.pow(five_halves), &[Exploded]);
check("^", vp, -five_halves, vp.pow(-five_halves), &[Exploded]);
check("^", ep, -five_halves, ep.pow(-five_halves), &[Vanished]);
check("^", vn, five_halves, vn.pow(five_halves), &[Undefined]);
check("^", en, half, en.pow(half), &[Undefined]);
check("^", vp, half, vp.pow(half), &[Undefined]);
check("^", ep, half, ep.pow(half), &[Undefined]);
check("^", vp, ep, vp.pow(ep), &[Vanished]); check("^", vp, en, vp.pow(en), &[Exploded]); check("^", ep, ep, ep.pow(ep), &[Exploded]);
check("^", ep, en, ep.pow(en), &[Vanished]);
check("^", vn, ep, vn.pow(ep), &[Undefined]); check("^", vp, vp, vp.pow(vp), &[Undefined]);
check("^", ep, inf, ep.pow(inf), &[Undefined]);
check("^", vp, S::ZERO, vp.pow(S::ZERO), &[Normal]);
check("^", ep, S::ZERO, ep.pow(S::ZERO), &[Normal]);
}
#[test]
fn shift_truth_table() {
let two = S::from(2);
let z = S::ZERO;
let inf = S::INFINITY;
check("<<", two, S::ZERO, two << 0, &[Normal]); check("<<", two, S::ZERO, two << 1, &[Normal]); check("<<", two, S::ZERO, two << 127, &[Exploded]); check(">>", two, S::ZERO, two >> 1, &[Normal]); let tiny_normal = S::ONE >> 120; check(">>", tiny_normal, S::ZERO, tiny_normal >> 20, &[Vanished]);
check("<<", z, S::ZERO, z << 5, &[Zero]);
check("<<", inf, S::ZERO, inf << 5, &[Infinity]);
}
#[test]
fn min_max_clamp_truth_table() {
let two = S::from(2);
let five = S::from(5);
let neg_three = S::from(-3);
let inf = S::INFINITY;
let z = S::ZERO;
let und = z / z;
check("min", two, five, two.min(five), &[Normal]); check("max", two, five, two.max(five), &[Normal]); check("min", two, inf, two.min(inf), &[Undefined]);
check("max", two, inf, two.max(inf), &[Undefined]);
check("min", two, und, two.min(und), &[Undefined]);
check("max", two, und, two.max(und), &[Undefined]);
assert_eq!(
classify(&neg_three.clamp(z, five)),
Zero,
"clamp(-3, [0,5]) → 0"
);
assert_eq!(
classify(&two.clamp(neg_three, five)),
Normal,
"clamp inside"
);
assert_eq!(
classify(&inf.clamp(neg_three, five)),
Undefined,
"clamp(∞, [-3,5]) is non-orderable → ℘∩"
);
}
#[test]
fn powb_unary_truth_table() {
let (z, vp, vn, _np, _nn, ep, en, inf, und) = unary_reps();
let two = S::from(2);
let neg_two = S::from(-2);
check_unary("powb", "[0]", z, z.powb(), &[Normal]); check_unary("powb", "[+↓]", vp, vp.powb(), &[Normal]);
check_unary("powb", "[-↓]", vn, vn.powb(), &[Normal]);
check_unary("powb", "2", two, two.powb(), &[Normal]); check_unary("powb", "-2", neg_two, neg_two.powb(), &[Normal]); check_unary(
"powb",
"[+↑]",
ep,
ep.powb(),
&[Exploded, Infinity, Undefined],
);
check_unary("powb", "[-↑]", en, en.powb(), &[Zero]); check_unary("powb", "[∞]", inf, inf.powb(), &[Infinity]);
check_unary("powb", "[℘]", und, und.powb(), &[Undefined]);
}
#[test]
fn square_unary_truth_table() {
let z = S::ZERO;
let vp = S::VANISHED_POS;
let vn = S::VANISHED_NEG;
let np = S::from(2);
let nn = S::from(-2);
let ep = S::EXPLODED_POS;
let en = S::EXPLODED_NEG;
let inf = S::INFINITY;
let und = S::ZERO / S::ZERO;
check_unary("square", "[0]", z, z.square(), &[Zero]);
check_unary("square", "[+↓]", vp, vp.square(), &[Vanished, Zero]);
check_unary("square", "[-↓]", vn, vn.square(), &[Vanished, Zero]);
check_unary(
"square",
"[+#]",
np,
np.square(),
&[Normal, Vanished, Exploded],
);
check_unary(
"square",
"[-#]",
nn,
nn.square(),
&[Normal, Vanished, Exploded],
);
check_unary("square", "[+↑]", ep, ep.square(), &[Exploded]);
check_unary("square", "[-↑]", en, en.square(), &[Exploded]);
check_unary("square", "[∞]", inf, inf.square(), &[Infinity]);
check_unary("square", "[℘]", und, und.square(), &[Undefined]);
for &x in &[np, nn, ep, en] {
let sq = x.square();
if sq.is_normal() {
assert!(
!sq.is_negative(),
"square({:?}) should be non-negative, got negative",
x
);
}
}
}
#[test]
fn subtraction_min_boundary_exploded() {
let min_neg = S::MAX_NEG; let result = S::ZERO - min_neg;
let at_boundary = S::ONE; let neg_boundary = S::NEG_ONE; let r1 = S::ZERO - at_boundary;
assert!(r1.is_negative(), "0-1 should be negative");
let r2 = S::ZERO - neg_boundary;
assert!(r2.is_positive(), "0-(-1) should be positive");
}
#[test]
fn and_truth_table() {
let z = S::ZERO;
let vp = S::VANISHED_POS;
let vn = S::VANISHED_NEG;
let np = S::from(7);
let nn = S::from(-7);
let ep = S::EXPLODED_POS;
let en = S::EXPLODED_NEG;
let inf = S::INFINITY;
let und = S::ZERO / S::ZERO;
check("&", z, z, z & z, &[Zero]);
check("&", z, vp, z & vp, &[Zero]);
check("&", z, vn, z & vn, &[Zero]);
check("&", z, np, z & np, &[Zero]);
check("&", z, nn, z & nn, &[Zero]);
check("&", z, ep, z & ep, &[Zero]);
check("&", z, en, z & en, &[Zero]);
check("&", z, inf, z & inf, &[Zero]);
check("&", vp, z, vp & z, &[Zero]);
check("&", np, z, np & z, &[Zero]);
check("&", ep, z, ep & z, &[Zero]);
check("&", inf, z, inf & z, &[Zero]);
check("&", inf, vp, inf & vp, &[Vanished]);
check("&", inf, np, inf & np, &[Normal]);
check("&", inf, ep, inf & ep, &[Exploded]);
check("&", inf, inf, inf & inf, &[Infinity]);
check("&", vp, inf, vp & inf, &[Vanished]);
check("&", np, inf, np & inf, &[Normal]);
check("&", ep, inf, ep & inf, &[Exploded]);
check("&", vp, vp, vp & vp, &[Undefined]);
check("&", vp, vn, vp & vn, &[Undefined]);
check("&", ep, ep, ep & ep, &[Undefined]);
check("&", ep, en, ep & en, &[Undefined]);
check("&", vp, ep, vp & ep, &[Zero, Exploded]);
check("&", vp, en, vp & en, &[Zero, Exploded]);
check("&", vn, ep, vn & ep, &[Zero, Exploded]);
check("&", ep, vp, ep & vp, &[Zero, Exploded]);
check("&", vp, np, vp & np, &[Undefined]);
check("&", vn, np, vn & np, &[Undefined]);
check("&", np, vp, np & vp, &[Undefined]);
check("&", ep, np, ep & np, &[Undefined]);
check("&", en, np, en & np, &[Undefined]);
check("&", np, ep, np & ep, &[Undefined]);
check("&", np, np, np & np, &[Normal, Zero, Vanished]);
check("&", np, nn, np & nn, &[Normal, Zero, Vanished]);
check("&", nn, nn, nn & nn, &[Normal, Zero, Vanished]);
check("&", und, z, und & z, &[Undefined]);
check("&", und, vp, und & vp, &[Undefined]);
check("&", und, np, und & np, &[Undefined]);
check("&", und, ep, und & ep, &[Undefined]);
check("&", und, inf, und & inf, &[Undefined]);
check("&", und, und, und & und, &[Undefined]);
check("&", z, und, z & und, &[Undefined]);
check("&", np, und, np & und, &[Undefined]);
check("&", inf, und, inf & und, &[Undefined]);
}
#[test]
fn or_truth_table() {
let z = S::ZERO;
let vp = S::VANISHED_POS;
let vn = S::VANISHED_NEG;
let np = S::from(7);
let nn = S::from(-7);
let ep = S::EXPLODED_POS;
let en = S::EXPLODED_NEG;
let inf = S::INFINITY;
let und = S::ZERO / S::ZERO;
check("|", z, z, z | z, &[Zero]);
check("|", z, vp, z | vp, &[Vanished]);
check("|", z, np, z | np, &[Normal]);
check("|", z, ep, z | ep, &[Exploded]);
check("|", z, inf, z | inf, &[Infinity]);
check("|", vp, z, vp | z, &[Vanished]);
check("|", np, z, np | z, &[Normal]);
check("|", ep, z, ep | z, &[Exploded]);
check("|", inf, z, inf | z, &[Infinity]);
check("|", inf, vp, inf | vp, &[Infinity]);
check("|", inf, np, inf | np, &[Infinity]);
check("|", inf, ep, inf | ep, &[Infinity]);
check("|", inf, inf, inf | inf, &[Infinity]);
check("|", vp, inf, vp | inf, &[Infinity]);
check("|", np, inf, np | inf, &[Infinity]);
check("|", ep, inf, ep | inf, &[Infinity]);
check("|", vp, vp, vp | vp, &[Undefined]);
check("|", vp, vn, vp | vn, &[Undefined]);
check("|", ep, ep, ep | ep, &[Undefined]);
check("|", ep, en, ep | en, &[Undefined]);
check("|", vp, ep, vp | ep, &[Vanished, Exploded]);
check("|", vp, en, vp | en, &[Vanished, Exploded]);
check("|", vn, ep, vn | ep, &[Vanished, Exploded]);
check("|", ep, vp, ep | vp, &[Vanished, Exploded]);
check("|", vp, np, vp | np, &[Undefined]);
check("|", np, vp, np | vp, &[Undefined]);
check("|", ep, np, ep | np, &[Undefined]);
check("|", np, ep, np | ep, &[Undefined]);
check("|", np, np, np | np, &[Normal, Vanished]);
check("|", np, nn, np | nn, &[Normal, Vanished]);
check("|", nn, nn, nn | nn, &[Normal, Vanished]);
check("|", und, z, und | z, &[Undefined]);
check("|", und, vp, und | vp, &[Undefined]);
check("|", und, np, und | np, &[Undefined]);
check("|", und, ep, und | ep, &[Undefined]);
check("|", und, und, und | und, &[Undefined]);
check("|", z, und, z | und, &[Undefined]);
check("|", np, und, np | und, &[Undefined]);
}
#[test]
fn xor_truth_table() {
let z = S::ZERO;
let vp = S::VANISHED_POS;
let vn = S::VANISHED_NEG;
let np = S::from(7);
let nn = S::from(-7);
let ep = S::EXPLODED_POS;
let en = S::EXPLODED_NEG;
let inf = S::INFINITY;
let und = S::ZERO / S::ZERO;
check("^", z, z, z ^ z, &[Zero]);
check("^", z, vp, z ^ vp, &[Vanished]);
check("^", z, np, z ^ np, &[Normal]);
check("^", z, ep, z ^ ep, &[Exploded]);
check("^", z, inf, z ^ inf, &[Infinity]);
check("^", inf, z, inf ^ z, &[Infinity]);
check("^", inf, vp, inf ^ vp, &[Vanished]);
check("^", inf, np, inf ^ np, &[Normal]);
check("^", inf, ep, inf ^ ep, &[Exploded]);
check("^", inf, inf, inf ^ inf, &[Zero]);
check("^", np, np, np ^ np, &[Zero]);
check("^", vp, vp, vp ^ vp, &[Undefined]);
check("^", vp, vn, vp ^ vn, &[Undefined]);
check("^", ep, ep, ep ^ ep, &[Undefined]);
check("^", ep, en, ep ^ en, &[Undefined]);
check("^", vp, ep, vp ^ ep, &[Exploded]);
check("^", vp, en, vp ^ en, &[Exploded]);
check("^", vn, ep, vn ^ ep, &[Exploded]);
check("^", ep, vp, ep ^ vp, &[Exploded]);
check("^", vp, np, vp ^ np, &[Undefined]);
check("^", np, vp, np ^ vp, &[Undefined]);
check("^", ep, np, ep ^ np, &[Undefined]);
check("^", np, ep, np ^ ep, &[Undefined]);
check("^", np, nn, np ^ nn, &[Zero, Vanished, Normal]);
check("^", und, z, und ^ z, &[Undefined]);
check("^", und, vp, und ^ vp, &[Undefined]);
check("^", und, np, und ^ np, &[Undefined]);
check("^", und, ep, und ^ ep, &[Undefined]);
check("^", und, inf, und ^ inf, &[Undefined]);
check("^", und, und, und ^ und, &[Undefined]);
check("^", z, und, z ^ und, &[Undefined]);
check("^", np, und, np ^ und, &[Undefined]);
check("^", inf, und, inf ^ und, &[Undefined]);
}
type C = CircleF3E3;
fn classify_c(c: &C) -> Class {
if c.is_undefined() {
Class::Undefined
} else if c.is_zero() {
Class::Zero
} else if c.is_infinite() {
Class::Infinity
} else if c.exploded() {
Class::Exploded
} else if c.vanished() {
Class::Vanished
} else {
Class::Normal
}
}
fn check_c(op: &str, a: C, b: C, result: C, expected: &[Class]) {
let rc = classify_c(&result);
if !expected.contains(&rc) {
panic!(
"Circle {} {} {} = {} (class {}), expected one of {:?}\n a = {:?}, b = {:?}, result = {:?}",
class_name(classify_c(&a)),
op,
class_name(classify_c(&b)),
class_name(rc),
rc as u8,
expected.iter().map(|c| class_name(*c)).collect::<Vec<_>>(),
a,
b,
result
);
}
}
#[test]
fn circle_multiplication_truth_table() {
let z = C::ZERO;
let vp = C::MIN_POS / 4u16;
let np = C::from((3.0f32, 4.0));
let ep = C::MAX * C::from((2.0f32, 0.0));
let inf = C::INFINITY;
let und = z / z;
check_c("×", inf, np, inf * np, &[Infinity]);
check_c("×", np, inf, np * inf, &[Infinity]);
check_c("×", inf, ep, inf * ep, &[Infinity]);
check_c("×", ep, inf, ep * inf, &[Infinity]);
check_c("×", inf, vp, inf * vp, &[Infinity]);
check_c("×", vp, inf, vp * inf, &[Infinity]);
check_c("×", inf, inf, inf * inf, &[Infinity]);
check_c("×", inf, z, inf * z, &[Undefined]);
check_c("×", z, inf, z * inf, &[Undefined]);
check_c("×", und, inf, und * inf, &[Undefined]);
check_c("×", inf, und, inf * und, &[Undefined]);
check_c("×", z, z, z * z, &[Zero]);
check_c("×", z, np, z * np, &[Zero]);
check_c("×", np, ep, np * ep, &[Exploded]);
check_c("×", ep, ep, ep * ep, &[Exploded]);
check_c("×", vp, vp, vp * vp, &[Vanished]);
check_c("×", vp, np, vp * np, &[Vanished]);
check_c("×", vp, ep, vp * ep, &[Undefined]);
check_c("×", ep, vp, ep * vp, &[Undefined]);
}
#[test]
fn circle_addition_truth_table() {
let z = C::ZERO;
let vp = C::MIN_POS / 4u16;
let np = C::from((3.0f32, 4.0));
let ep = C::MAX * C::from((2.0f32, 0.0));
let inf = C::INFINITY;
let und = z / z;
check_c("+", inf, z, inf + z, &[Infinity]);
check_c("+", inf, np, inf + np, &[Infinity]);
check_c("+", inf, ep, inf + ep, &[Infinity]);
check_c("+", inf, vp, inf + vp, &[Infinity]);
check_c("+", inf, inf, inf + inf, &[Infinity]);
check_c("+", z, inf, z + inf, &[Infinity]);
check_c("+", ep, inf, ep + inf, &[Infinity]);
check_c("+", und, inf, und + inf, &[Undefined]);
check_c("+", inf, und, inf + und, &[Undefined]);
check_c("+", ep, np, ep + np, &[Undefined]);
check_c("+", np, ep, np + ep, &[Undefined]);
check_c("+", ep, ep, ep + ep, &[Undefined]);
check_c("+", z, z, z + z, &[Zero]);
check_c("+", z, np, z + np, &[Normal]);
check_c("+", ep, z, ep + z, &[Exploded]);
check_c("+", ep, vp, ep + vp, &[Exploded]);
}
#[test]
fn circle_division_truth_table() {
let z = C::ZERO;
let vp = C::MIN_POS / 4u16;
let np = C::from((3.0f32, 4.0));
let ep = C::MAX * C::from((2.0f32, 0.0));
let inf = C::INFINITY;
let und = z / z;
check_c("/", inf, np, inf / np, &[Infinity]);
check_c("/", inf, ep, inf / ep, &[Infinity]);
check_c("/", inf, vp, inf / vp, &[Infinity]);
check_c("/", inf, z, inf / z, &[Infinity]);
check_c("/", inf, inf, inf / inf, &[Undefined]);
check_c("/", np, inf, np / inf, &[Zero]);
check_c("/", ep, inf, ep / inf, &[Zero]);
check_c("/", vp, inf, vp / inf, &[Zero]);
check_c("/", z, inf, z / inf, &[Zero]);
check_c("/", np, z, np / z, &[Infinity]);
check_c("/", ep, z, ep / z, &[Infinity]);
check_c("/", vp, z, vp / z, &[Infinity]);
check_c("/", z, z, z / z, &[Undefined]);
check_c("/", ep, ep, ep / ep, &[Undefined]);
check_c("/", vp, vp, vp / vp, &[Undefined]);
}
#[test]
fn circle_modulus_truth_table() {
let z = C::ZERO;
let vp = C::MIN_POS / 4u16;
let np = C::from((3.0f32, 4.0));
let ep = C::MAX * C::from((2.0f32, 0.0));
let inf = C::INFINITY;
let und = z / z;
check_c("%", z, z, z % z, &[Zero]);
check_c("%", z, np, z % np, &[Zero]);
check_c("%", np, z, np % z, &[Zero]);
check_c("%", ep, z, ep % z, &[Zero]);
check_c("%", und, np, und % np, &[Undefined]);
check_c("%", np, und, np % und, &[Undefined]);
check_c("%", inf, np, inf % np, &[Undefined]);
check_c("%", inf, ep, inf % ep, &[Undefined]);
check_c("%", inf, inf, inf % inf, &[Undefined]);
check_c("%", ep, np, ep % np, &[Undefined]);
check_c("%", ep, vp, ep % vp, &[Undefined]);
check_c("%", ep, ep, ep % ep, &[Undefined]);
check_c("%", np, inf, np % inf, &[Undefined]);
check_c("%", vp, inf, vp % inf, &[Undefined]);
check_c("%", np, vp, np % vp, &[Undefined]);
check_c("%", vp, vp, vp % vp, &[Undefined]);
check_c("%", np, ep, np % ep, &[Normal]);
}
#[test]
fn circle_and_truth_table() {
let z = C::ZERO;
let vp = C::MIN_POS / 4u16;
let np = C::from((3.0f32, 4.0));
let ep = C::MAX * C::from((2.0f32, 0.0));
let inf = C::INFINITY;
let und = z / z;
check_c("&", z, z, z & z, &[Zero]);
check_c("&", z, np, z & np, &[Zero]);
check_c("&", z, inf, z & inf, &[Zero]);
check_c("&", inf, z, inf & z, &[Zero]);
check_c("&", inf, np, inf & np, &[Normal]);
check_c("&", inf, vp, inf & vp, &[Vanished]);
check_c("&", inf, ep, inf & ep, &[Exploded]);
check_c("&", inf, inf, inf & inf, &[Infinity]);
check_c("&", np, inf, np & inf, &[Normal]);
check_c("&", vp, np, vp & np, &[Undefined]);
check_c("&", np, vp, np & vp, &[Undefined]);
check_c("&", ep, np, ep & np, &[Undefined]);
check_c("&", np, ep, np & ep, &[Undefined]);
check_c("&", vp, vp, vp & vp, &[Undefined]);
check_c("&", ep, ep, ep & ep, &[Undefined]);
check_c("&", und, np, und & np, &[Undefined]);
check_c("&", inf, und, inf & und, &[Undefined]);
}
#[test]
fn circle_or_truth_table() {
let z = C::ZERO;
let vp = C::MIN_POS / 4u16;
let np = C::from((3.0f32, 4.0));
let ep = C::MAX * C::from((2.0f32, 0.0));
let inf = C::INFINITY;
let und = z / z;
check_c("|", z, np, z | np, &[Normal]);
check_c("|", np, z, np | z, &[Normal]);
check_c("|", z, inf, z | inf, &[Infinity]);
check_c("|", inf, np, inf | np, &[Infinity]);
check_c("|", inf, vp, inf | vp, &[Infinity]);
check_c("|", inf, ep, inf | ep, &[Infinity]);
check_c("|", inf, inf, inf | inf, &[Infinity]);
check_c("|", np, inf, np | inf, &[Infinity]);
check_c("|", vp, np, vp | np, &[Undefined]);
check_c("|", ep, np, ep | np, &[Undefined]);
check_c("|", np, vp, np | vp, &[Undefined]);
check_c("|", np, ep, np | ep, &[Undefined]);
check_c("|", vp, vp, vp | vp, &[Undefined]);
check_c("|", ep, ep, ep | ep, &[Undefined]);
check_c("|", und, np, und | np, &[Undefined]);
}
#[test]
fn circle_xor_truth_table() {
let z = C::ZERO;
let vp = C::MIN_POS / 4u16;
let np = C::from((3.0f32, 4.0));
let ep = C::MAX * C::from((2.0f32, 0.0));
let inf = C::INFINITY;
let und = z / z;
check_c("^", z, np, z ^ np, &[Normal]);
check_c("^", np, z, np ^ z, &[Normal]);
check_c("^", z, ep, z ^ ep, &[Exploded]);
check_c("^", z, inf, z ^ inf, &[Infinity]);
check_c("^", inf, np, inf ^ np, &[Normal]);
check_c("^", inf, vp, inf ^ vp, &[Vanished]);
check_c("^", inf, ep, inf ^ ep, &[Exploded]);
check_c("^", inf, inf, inf ^ inf, &[Zero]);
check_c("^", vp, np, vp ^ np, &[Undefined]);
check_c("^", ep, np, ep ^ np, &[Undefined]);
check_c("^", np, vp, np ^ vp, &[Undefined]);
check_c("^", np, ep, np ^ ep, &[Undefined]);
check_c("^", vp, vp, vp ^ vp, &[Undefined]);
check_c("^", ep, ep, ep ^ ep, &[Undefined]);
check_c("^", und, np, und ^ np, &[Undefined]);
}
type C4 = CircleF4E3;
type S4 = ScalarF4E3;
fn classify_c4(c: &C4) -> Class {
if c.is_undefined() {
Class::Undefined
} else if c.is_zero() {
Class::Zero
} else if c.is_infinite() {
Class::Infinity
} else if c.exploded() {
Class::Exploded
} else if c.vanished() {
Class::Vanished
} else {
Class::Normal
}
}
fn check_c4(what: &str, result: C4, expected: &[Class]) {
let rc = classify_c4(&result);
assert!(
expected.contains(&rc),
"{what}: got {rc:?}, expected {expected:?} (result {result:?})"
);
}
fn angle_deg(c: &C4) -> f64 {
(c.imaginary as f64).atan2(c.real as f64).to_degrees()
}
fn assert_angle(what: &str, c: &C4, want: f64) {
let got = angle_deg(c);
let mut d = (got - want).abs() % 360.0;
if d > 180.0 {
d = 360.0 - d;
}
assert!(d < 0.5, "{what}: angle {got:.2}° != {want:.2}° (Δ {d:.2}°)");
}
fn escaped_circles() -> (C4, C4) {
let base = C4::from((3, 4));
let mut e = base;
for _ in 0..600 {
if e.exploded() {
break;
}
e = e + e;
}
let mut t = base;
let half = S4::from(1) / S4::from(2);
for _ in 0..600 {
if t.vanished() {
break;
}
t = t * half;
}
assert!(
e.exploded() && t.vanished(),
"representative construction failed"
);
(e, t)
}
#[test]
fn circle_pow_scalar_truth_table() {
let (e, t) = escaped_circles();
let z = C4::ZERO;
let inf = C4::INFINITY;
let two = S4::from(2);
let neg_two = S4::from(-2);
let two5 = S4::from(5) / S4::from(2);
let half = S4::from(1) / S4::from(2);
check_c4("0^0", z.pow(S4::ZERO), &[Normal]);
check_c4("0^2", z.pow(two), &[Zero]);
check_c4("0^-2", z.pow(neg_two), &[Infinity]);
check_c4("∞^2", inf.pow(two), &[Infinity]);
check_c4("∞^-2", inf.pow(neg_two), &[Zero]);
check_c4("∞^0", inf.pow(S4::ZERO), &[Normal]);
let e1 = e.pow(S4::from(1));
assert!(
e1.exploded() && e1.real == e.real && e1.imaginary == e.imaginary,
"↑^1 must be the identity"
);
let e2 = e.pow(two);
assert!(e2.exploded());
assert_angle("↑^2", &e2, 106.26);
let em1 = e.pow(S4::from(-1));
assert!(em1.vanished());
assert_angle("↑^-1", &em1, -53.13);
let t2 = t.pow(two);
assert!(t2.vanished());
assert_angle("↓^2", &t2, 106.26);
let tm2 = t.pow(neg_two);
assert!(tm2.exploded());
assert_angle("↓^-2", &tm2, -106.26);
let e25 = e.pow(two5);
assert!(e25.exploded());
assert_angle("↑^2.5", &e25, 132.83);
let t25 = t.pow(two5);
assert!(t25.vanished());
assert_angle("↓^2.5", &t25, 132.83);
let em25 = e.pow(-two5);
assert!(em25.vanished());
assert_angle("↑^-2.5", &em25, -132.83);
check_c4("↑^0.5", e.pow(half), &[Undefined]);
check_c4("↓^0.5", t.pow(half), &[Undefined]);
}
#[test]
fn circle_pow_circle_truth_table() {
let (e, _t) = escaped_circles();
let z = C4::ZERO;
let inf = C4::INFINITY;
let i = C4::POS_I;
check_c4("0^(2+0i)", z.pow(C4::from((2, 0))), &[Zero]);
check_c4("0^(-2+0i)", z.pow(C4::from((-2, 0))), &[Infinity]);
check_c4("0^(0)", z.pow(C4::ZERO), &[Normal]); let e2 = e.pow(C4::from((2, 0)));
assert!(e2.exploded());
assert_angle("↑^(2+0i)", &e2, 106.26);
check_c4("0^i", z.pow(i), &[Undefined]); check_c4("∞^(2+i)", inf.pow(C4::from((2, 1))), &[Infinity]);
check_c4("∞^(-2+i)", inf.pow(C4::from((-2, 1))), &[Zero]);
check_c4("↑^(1+i)", e.pow(C4::from((1, 1))), &[Undefined]);
let sq = C4::from((1, 1)).pow(C4::from((2, 0)));
assert!(sq.is_normal());
let ii = i.pow(i);
assert!(ii.is_normal() && ii.i().is_negligible(), "i^i must be real");
}
#[test]
fn circle_exp_ln_sqrt_truth_tables() {
let (e, t) = escaped_circles();
let z = C4::ZERO;
let inf = C4::INFINITY;
let und = z / z;
check_c4("exp(0)", z.exp(), &[Normal]); check_c4("exp(↓)", t.exp(), &[Normal]); let mut west = C4::from((-3, 1));
for _ in 0..600 {
if west.exploded() {
break;
}
west = west + west;
}
check_c4("exp(↑ Re<0)", west.exp(), &[Zero]); check_c4("exp(↑ Re>0)", e.exp(), &[Undefined]); check_c4("exp(∞)", inf.exp(), &[Infinity]);
check_c4("exp(℘)", und.exp(), &[Undefined]);
check_c4("ln(0)", z.ln(), &[Infinity]);
check_c4("ln(∞)", inf.ln(), &[Infinity]);
check_c4("ln(↑)", e.ln(), &[Undefined]);
check_c4("ln(↓)", t.ln(), &[Undefined]);
check_c4("ln(℘)", und.ln(), &[Undefined]);
check_c4("sqrt(0)", z.sqrt(), &[Zero]);
check_c4("sqrt(∞)", inf.sqrt(), &[Infinity]);
check_c4("sqrt(↑)", e.sqrt(), &[Undefined]);
check_c4("sqrt(↓)", t.sqrt(), &[Undefined]);
let esq = e.square();
assert!(esq.exploded());
assert_angle("square(↑)", &esq, 106.26);
}
#[test]
fn circle_escaped_orientation_thru_ops() {
let (e, t) = escaped_circles();
assert_angle("↑ base", &e, 53.13);
assert_angle("↑ × i", &(e * C4::POS_I), 143.13);
assert_angle("-↑", &(-e), -126.87);
assert_angle("conj ↑", &e.conjugate(), -53.13);
assert_angle("↑ × ↑", &(e * e), 106.26);
let r = e.reciprocal();
assert!(r.vanished());
assert_angle("1/↑", &r, -53.13);
assert_angle("↓ × i", &(t * C4::POS_I), 143.13);
let rv = t.reciprocal();
assert!(rv.exploded());
assert_angle("1/↓", &rv, -53.13);
assert_angle("↑ × 3", &(e * S4::from(3)), 53.13);
assert_angle("↑ × -3", &(e * S4::from(-3)), -126.87);
assert_angle("↑ + ↓", &(e + t), 53.13);
let vn = t + C4::from((1, 1));
assert!(vn.is_normal());
}
#[test]
fn equality_ordering_semantics() {
let und = S::ZERO / S::ZERO;
let inf = S::INFINITY;
let ep = S::EXPLODED_POS;
let vp = S::VANISHED_POS;
let two = S::from(2);
assert!(und != und);
assert!(inf != inf);
assert!(
ep != ep,
"↑ == ↑ must be false: identical phase does not mean identical magnitude"
);
assert!(vp != vp);
assert!(two == S::from(2));
assert!(
S::ZERO == S::ZERO,
"Zero is a definite value and equals itself"
);
assert!(ep > two);
assert!(vp < two);
assert!(-vp < vp);
assert!(!(und < two) && !(two < und));
assert!(!(inf < two) && !(two < inf) && !(inf > two));
assert!(two.partial_cmp(&inf).is_none());
assert!(ep.partial_cmp(&ep).is_none());
}