Rational test.ck

From CSWiki
Revision as of 14:31, 15 December 2009 by Rdpoor (talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Test and verification suite for rational.ck

// rational_test.ck: test suite for Rational class.
//
// Usage:
//	% chuck rational.ck rational_test.ck
// 
// Set errp (below) according to how much printing you want.
//
// Robert Poor <rdpoor at gmail dot com>, October 2009

// control what gets printed
0 => int PRINT_ALL;
1 => int PRINT_ERRORS;
2 => int PRINT_NONE;
PRINT_ERRORS => int errp;
// PRINT_ALL => int errp;

fun int do_test(string testname, int got, int expected) {
    if (expected == got) {
	if (errp == PRINT_ALL) <<< testname, ": okay" >>>;
	return 0;
    } else {
	if (errp != PRINT_NONE) <<< testname, ": got:", got, "expected:", expected >>>;
	return 1;
    }
}

fun int do_test(string testname, float got, float expected) {
    if (expected == got) {
	if (errp == PRINT_ALL) <<< testname, ": okay" >>>;
	return 0;
    } else {
	if (errp != PRINT_NONE) <<< testname, ": got:", got, "expected:", expected >>>;
	return 1;
    }
}

fun int do_test(string testname, string got, string expected) {
    if (expected == got) {
	if (errp == PRINT_ALL) <<< testname, ": okay" >>>;
	return 0;
    } else {
	if (errp != PRINT_NONE) <<< testname, ": got:", got, "expected:", expected >>>;
	return 1;
    }
}

fun int do_test(string testname, Rational got, Rational expected) {
    if (expected.cmp(got) == Rational.CMP_EQ) {
	if (errp == PRINT_ALL) <<< testname, ": okay" >>>;
	return 0;
    } else {
	if (errp != PRINT_NONE) <<< testname, ": got:", got.toString(), "expected:", expected.toString() >>>;
	return 1;
    }
}

// Define a few constant Rationals

Rational.create(-2) @=> Rational @ M_TWO;
Rational.create(-1) @=> Rational @ M_ONE;
Rational.create(-1, 2) @=> Rational @ M_HALF;
Rational.create(0) @=> Rational @ ZERO;
Rational.create(1, 2) @=> Rational @ HALF;
Rational.create(1) @=> Rational @ ONE;
Rational.create(2) @=> Rational @ TWO;
Rational.create(Math.sqrt(2.0)) @=> Rational SQRT_2;

// Test basic printing
<<< "Defined constant Rational values:" >>>;
<<< "M_TWO:", M_TWO.toString() >>>;
<<< "M_ONE:", M_ONE.toString() >>>;
<<< "M_HALF:", M_HALF.toString() >>>;
<<< "ZERO:", ZERO.toString() >>>;
<<< "HALF:", HALF.toString() >>>;
<<< "ONE:", ONE.toString() >>>;
<<< "TWO:", TWO.toString() >>>;
<<< "SQRT_2:", SQRT_2.toString() >>>;

// Test floating point constructor
do_test("Rational.create(-2.0)", Rational.create(-2.0), M_TWO);
do_test("Rational.create(-1.0)", Rational.create(-1.0), M_ONE);
do_test("Rational.create(-0.5)", Rational.create(-0.5), M_HALF);
// -0.0 should normalize to be 0,0
do_test("Rational.create(-0.0)", Rational.create(-0.0), ZERO);
do_test("Rational.create(0.0)", Rational.create(0.0), ZERO);
do_test("Rational.create(0.5)", Rational.create(0.5), HALF);
do_test("Rational.create(1.0)", Rational.create(1.0), ONE);
do_test("Rational.create(2.0)", Rational.create(2.0), TWO);

// Test some famous values
do_test("Rational.create(pi, 100)", Rational.create(pi, 100), Rational.create(22,7));
do_test("Rational.create(pi, 1000)", Rational.create(pi, 1000), Rational.create(355,113));
do_test("Rational.create(pi, 10000)", Rational.create(pi, 10000), Rational.create(355,113));
do_test("Rational.create(pi, 100000)", Rational.create(pi, 100000), Rational.create(312689,99532));

// Test copying constructor
do_test("Rational.create(TWO).cmp(TWO)", Rational.create(TWO).cmp(TWO), Rational.CMP_EQ);

// Test num, den constructor, verify normalization
do_test("Rational.create(1, 2).cmp(HALF)", Rational.create(1, 2).cmp(HALF), Rational.CMP_EQ);
do_test("Rational.create(2, 4).cmp(HALF)", Rational.create(2, 4).cmp(HALF), Rational.CMP_EQ);
do_test("Rational.create(-20, -40).cmp(HALF)", Rational.create(-20, -40).cmp(HALF), Rational.CMP_EQ);
do_test("Rational.create(-2, 4).cmp(M_HALF)", Rational.create(-2, 4).cmp(M_HALF), Rational.CMP_EQ);
do_test("Rational.create(2, -4).cmp(M_HALF)", Rational.create(2, -4).cmp(M_HALF), Rational.CMP_EQ);

for (-6 => int n; n <= 6; n++) {
    for (-6 => int d; d <= 6; d++) {
	// verify that denominator never has negative sign
	Rational.create(n, d) @=> Rational @ r;
	"Rational.create("+n+","+d+").den() >= 0" => string label;
	do_test(label, r.den() >= 0, true);
	// verify that n/d is in reduced form
    }
}


// test num(), den()
do_test("Rational.create(pi, 1000).num()", Rational.create(pi, 1000).num(), 355);
do_test("Rational.create(pi, 1000).den()", Rational.create(pi, 1000).den(), 113);

// test converstion to float and to string
do_test("Rational.create(3, 4).toFloat()", Rational.create(3, 4).toFloat(), 0.75);
do_test("Rational.create(3, 4).toString()", Rational.create(3, 4).toString(), "3/4");

// signum
do_test("M_HALF.signum()", M_HALF.signum(), Rational.CMP_LT);
do_test("ZERO.signum()", ZERO.signum(), Rational.CMP_EQ);
do_test("HALF.signum()", HALF.signum(), Rational.CMP_GT);

// compare against integer
do_test("M_ONE.cmp(-2)", M_ONE.cmp(-2), Rational.CMP_GT);
do_test("M_ONE.cmp(-1)", M_ONE.cmp(-1), Rational.CMP_EQ);
do_test("M_ONE.cmp(0)", M_ONE.cmp(0), Rational.CMP_LT);
do_test("ONE.cmp(0)", ONE.cmp(0), Rational.CMP_GT);
do_test("ONE.cmp(1)", ONE.cmp(1), Rational.CMP_EQ);
do_test("ONE.cmp(2)", ONE.cmp(2), Rational.CMP_LT);

// compare against Rational
do_test("ONE.cmp(SQRT_2)", ONE.cmp(SQRT_2), Rational.CMP_LT);
do_test("TWO.cmp(SQRT_2)", TWO.cmp(SQRT_2), Rational.CMP_GT);
do_test("SQRT_2.cmp(ONE)", SQRT_2.cmp(ONE), Rational.CMP_GT);
do_test("SQRT_2.cmp(TWO)", SQRT_2.cmp(TWO), Rational.CMP_LT);

// trunc
do_test("M_TWO.trunc()", M_TWO.trunc(), -2);
do_test("M_ONE.trunc()", M_ONE.trunc(), -1);
do_test("M_HALF.trunc()", M_HALF.trunc(), 0);
do_test("ZERO.trunc()", ZERO.trunc(), 0);
do_test("HALF.trunc()", HALF.trunc(), 0);
do_test("ONE.trunc()", ONE.trunc(), 1);
do_test("TWO.trunc()", TWO.trunc(), 2);
do_test("SQRT_2.trunc()", SQRT_2.trunc(), 1);

// floor
do_test("M_TWO.floor()", M_TWO.floor(), -2);
do_test("M_ONE.floor()", M_ONE.floor(), -1);
do_test("M_HALF.floor()", M_HALF.floor(), -1);
do_test("ZERO.floor()", ZERO.floor(), 0);
do_test("HALF.floor()", HALF.floor(), 0);
do_test("ONE.floor()", ONE.floor(), 1);
do_test("TWO.floor()", TWO.floor(), 2);
do_test("SQRT_2.floor()", SQRT_2.floor(), 1);

// ceil
do_test("M_TWO.ceil()", M_TWO.ceil(), -2);
do_test("M_ONE.ceil()", M_ONE.ceil(), -1);
do_test("M_HALF.ceil()", M_HALF.ceil(), 0);
do_test("ZERO.ceil()", ZERO.ceil(), 0);
do_test("HALF.ceil()", HALF.ceil(), 1);
do_test("ONE.ceil()", ONE.ceil(), 1);
do_test("TWO.ceil()", TWO.ceil(), 2);
do_test("SQRT_2.ceil()", SQRT_2.ceil(), 2);

// round
do_test("M_TWO.round()", M_TWO.round(), -2);
do_test("M_ONE.round()", M_ONE.round(), -1);
do_test("M_HALF.round()", M_HALF.round(), 0);
do_test("ZERO.round()", ZERO.round(), 0);
do_test("HALF.round()", HALF.round(), 0);
do_test("ONE.round()", ONE.round(), 1);
do_test("TWO.round()", TWO.round(), 2);
do_test("SQRT_2.round()", SQRT_2.round(), 1);

// ### div, mod need testing

// abs
do_test("M_TWO.abs().cmp(TWO)", M_TWO.abs().cmp(TWO), Rational.CMP_EQ);
do_test("M_ONE.abs().cmp(ONE)", M_ONE.abs().cmp(ONE), Rational.CMP_EQ);
do_test("M_HALF.abs().cmp(HALF)", M_HALF.abs().cmp(HALF), Rational.CMP_EQ);
do_test("ZERO.abs().cmp(ZERO)", ZERO.abs().cmp(ZERO), Rational.CMP_EQ);
do_test("ONE.abs().cmp(ONE)", ONE.abs().cmp(ONE), Rational.CMP_EQ);
do_test("TWO.abs().cmp(TWO)", TWO.abs().cmp(TWO), Rational.CMP_EQ);

// negate
do_test("M_TWO.negate().cmp(TWO)", M_TWO.negate().cmp(TWO), Rational.CMP_EQ);
do_test("M_ONE.negate().cmp(ONE)", M_ONE.negate().cmp(ONE), Rational.CMP_EQ);
do_test("M_HALF.negate().cmp(HALF)", M_HALF.negate().cmp(HALF), Rational.CMP_EQ);
do_test("ZERO.negate().cmp(ZERO)", ZERO.negate().cmp(ZERO), Rational.CMP_EQ);
do_test("ONE.negate().cmp(M_ONE)", ONE.negate().cmp(M_ONE), Rational.CMP_EQ);
do_test("TWO.negate().cmp(M_TWO)", TWO.negate().cmp(M_TWO), Rational.CMP_EQ);

// reciprocal
do_test("M_TWO.reciprocal().cmp(M_HALF)", M_TWO.reciprocal().cmp(M_HALF), Rational.CMP_EQ);
do_test("M_ONE.reciprocal().cmp(M_ONE)", M_ONE.reciprocal().cmp(M_ONE), Rational.CMP_EQ);
do_test("M_HALF.reciprocal().cmp(M_TWO)", M_HALF.reciprocal().cmp(M_TWO), Rational.CMP_EQ);
// do_test("ZERO.reciprocal().cmp(ZERO)", ZERO.reciprocal().cmp(ZERO), Rational.CMP_EQ);
do_test("ONE.reciprocal().cmp(ONE)", ONE.reciprocal().cmp(ONE), Rational.CMP_EQ);
do_test("TWO.reciprocal().cmp(HALF)", TWO.reciprocal().cmp(HALF), Rational.CMP_EQ);

// add
// -2, -1, -.5, 0, .5, 1, 2
//
do_test("M_TWO.add(ONE).cmp(M_ONE)", M_TWO.add(ONE).cmp(M_ONE), Rational.CMP_EQ);
do_test("M_TWO.add(TWO).cmp(ZERO)", M_TWO.add(TWO).cmp(ZERO), Rational.CMP_EQ);
do_test("M_ONE.add(HALF).cmp(M_HALF)", M_ONE.add(HALF).cmp(M_HALF), Rational.CMP_EQ);
do_test("M_ONE.add(ONE).cmp(ZERO)", M_ONE.add(ONE).cmp(ZERO), Rational.CMP_EQ);
do_test("M_ONE.add(TWO).cmp(ONE)", M_ONE.add(TWO).cmp(ONE), Rational.CMP_EQ);
do_test("M_HALF.add(HALF).cmp(ZERO)", M_HALF.add(HALF).cmp(ZERO), Rational.CMP_EQ);
do_test("ZERO.add(M_TWO).cmp(M_TWO)", ZERO.add(M_TWO).cmp(M_TWO), Rational.CMP_EQ);
do_test("ZERO.add(M_ONE).cmp(M_ONE)", ZERO.add(M_ONE).cmp(M_ONE), Rational.CMP_EQ);
do_test("ZERO.add(M_HALF).cmp(M_HALF)", ZERO.add(M_HALF).cmp(M_HALF), Rational.CMP_EQ);
do_test("ZERO.add(ZERO).cmp(ZERO)", ZERO.add(ZERO).cmp(ZERO), Rational.CMP_EQ);
do_test("ZERO.add(HALF).cmp(HALF)", ZERO.add(HALF).cmp(HALF), Rational.CMP_EQ);
do_test("ZERO.add(ONE).cmp(ONE)", ZERO.add(ONE).cmp(ONE), Rational.CMP_EQ);
do_test("ZERO.add(TWO).cmp(TWO)", ZERO.add(TWO).cmp(TWO), Rational.CMP_EQ);
do_test("HALF.add(M_HALF).cmp(ZERO)", HALF.add(M_HALF).cmp(ZERO), Rational.CMP_EQ);
do_test("ONE.add(M_TWO).cmp(M_ONE)", ONE.add(M_TWO).cmp(M_ONE), Rational.CMP_EQ);
do_test("ONE.add(M_ONE).cmp(ZERO)", ONE.add(M_ONE).cmp(ZERO), Rational.CMP_EQ);
do_test("ONE.add(M_HALF).cmp(HALF)", ONE.add(M_HALF).cmp(HALF), Rational.CMP_EQ);
do_test("ONE.add(ZERO).cmp(ONE)", ONE.add(ZERO).cmp(ONE), Rational.CMP_EQ);
do_test("ONE.add(ONE).cmp(TWO)", ONE.add(ONE).cmp(TWO), Rational.CMP_EQ);
do_test("TWO.add(M_TWO).cmp(ZERO)", TWO.add(M_TWO).cmp(ZERO), Rational.CMP_EQ);
do_test("TWO.add(M_ONE).cmp(ONE)", TWO.add(M_ONE).cmp(ONE), Rational.CMP_EQ);
do_test("TWO.add(ZERO).cmp(TWO)", TWO.add(ZERO).cmp(TWO), Rational.CMP_EQ);
;;
do_test("M_TWO.add(0).cmp(M_TWO)", M_TWO.add(0).cmp(M_TWO), Rational.CMP_EQ);
do_test("M_TWO.add(1).cmp(M_ONE)", M_TWO.add(1).cmp(M_ONE), Rational.CMP_EQ);
do_test("M_TWO.add(2).cmp(ZERO)", M_TWO.add(2).cmp(ZERO), Rational.CMP_EQ);
do_test("M_TWO.add(3).cmp(ONE)", M_TWO.add(3).cmp(ONE), Rational.CMP_EQ);
do_test("M_TWO.add(4).cmp(TWO)", M_TWO.add(4).cmp(TWO), Rational.CMP_EQ);
do_test("M_ONE.add(-1).cmp(M_TWO)", M_ONE.add(-1).cmp(M_TWO), Rational.CMP_EQ);
do_test("M_ONE.add(0).cmp(M_ONE)", M_ONE.add(0).cmp(M_ONE), Rational.CMP_EQ);
do_test("M_ONE.add(1).cmp(ZERO)", M_ONE.add(1).cmp(ZERO), Rational.CMP_EQ);
do_test("M_ONE.add(2).cmp(ONE)", M_ONE.add(2).cmp(ONE), Rational.CMP_EQ);
do_test("M_ONE.add(3).cmp(TWO)", M_ONE.add(3).cmp(TWO), Rational.CMP_EQ);
do_test("M_HALF.add(1).cmp(HALF)", M_HALF.add(1).cmp(HALF), Rational.CMP_EQ);
do_test("ZERO.add(-2).cmp(M_TWO)", ZERO.add(-2).cmp(M_TWO), Rational.CMP_EQ);
do_test("ZERO.add(-1).cmp(M_ONE)", ZERO.add(-1).cmp(M_ONE), Rational.CMP_EQ);
do_test("ZERO.add(0).cmp(ZERO)", ZERO.add(0).cmp(ZERO), Rational.CMP_EQ);
do_test("ZERO.add(1).cmp(ONE)", ZERO.add(1).cmp(ONE), Rational.CMP_EQ);
do_test("ZERO.add(2).cmp(TWO)", ZERO.add(2).cmp(TWO), Rational.CMP_EQ);
do_test("ONE.add(-3).cmp(M_TWO)", ONE.add(-3).cmp(M_TWO), Rational.CMP_EQ);
do_test("ONE.add(-2).cmp(M_ONE)", ONE.add(-2).cmp(M_ONE), Rational.CMP_EQ);
do_test("ONE.add(-1).cmp(ZERO)", ONE.add(-1).cmp(ZERO), Rational.CMP_EQ);
do_test("ONE.add(0).cmp(ONE)", ONE.add(0).cmp(ONE), Rational.CMP_EQ);
do_test("ONE.add(1).cmp(TWO)", ONE.add(1).cmp(TWO), Rational.CMP_EQ);
do_test("HALF.add(-1).cmp(M_HALF)", HALF.add(-1).cmp(M_HALF), Rational.CMP_EQ);
do_test("TWO.add(-4).cmp(M_TWO)", TWO.add(-4).cmp(M_TWO), Rational.CMP_EQ);
do_test("TWO.add(-3).cmp(M_ONE)", TWO.add(-3).cmp(M_ONE), Rational.CMP_EQ);
do_test("TWO.add(-2).cmp(ZERO)", TWO.add(-2).cmp(ZERO), Rational.CMP_EQ);
do_test("TWO.add(-1).cmp(ONE)", TWO.add(-1).cmp(ONE), Rational.CMP_EQ);
do_test("TWO.add(0).cmp(TWO)", TWO.add(0).cmp(TWO), Rational.CMP_EQ);

// sub
// -2, -1, -.5, 0, .5, 1, 2
//
do_test("M_TWO.sub(M_TWO).cmp(ZERO)", M_TWO.sub(M_TWO).cmp(ZERO), Rational.CMP_EQ);
do_test("M_TWO.sub(M_ONE).cmp(M_ONE)", M_TWO.sub(M_ONE).cmp(M_ONE), Rational.CMP_EQ);
do_test("M_TWO.sub(ZERO).cmp(M_TWO)", M_TWO.sub(ZERO).cmp(M_TWO), Rational.CMP_EQ);
do_test("M_ONE.sub(M_TWO).cmp(ONE)", M_ONE.sub(M_TWO).cmp(ONE), Rational.CMP_EQ);
do_test("M_ONE.sub(M_ONE).cmp(ZERO)", M_ONE.sub(M_ONE).cmp(ZERO), Rational.CMP_EQ);
do_test("M_ONE.sub(ZERO).cmp(M_ONE)", M_ONE.sub(ZERO).cmp(M_ONE), Rational.CMP_EQ);
do_test("M_ONE.sub(ONE).cmp(M_TWO)", M_ONE.sub(ONE).cmp(M_TWO), Rational.CMP_EQ);
do_test("M_HALF.sub(M_ONE).cmp(HALF)", M_HALF.sub(M_ONE).cmp(HALF), Rational.CMP_EQ);
do_test("ZERO.sub(M_TWO).cmp(TWO)", ZERO.sub(M_TWO).cmp(TWO), Rational.CMP_EQ);
do_test("ZERO.sub(M_ONE).cmp(ONE)", ZERO.sub(M_ONE).cmp(ONE), Rational.CMP_EQ);
do_test("ZERO.sub(ZERO).cmp(ZERO)", ZERO.sub(ZERO).cmp(ZERO), Rational.CMP_EQ);
do_test("ZERO.sub(ONE).cmp(M_ONE)", ZERO.sub(ONE).cmp(M_ONE), Rational.CMP_EQ);
do_test("ZERO.sub(TWO).cmp(M_TWO)", ZERO.sub(TWO).cmp(M_TWO), Rational.CMP_EQ);
do_test("HALF.sub(ONE).cmp(M_HALF)", HALF.sub(ONE).cmp(M_HALF), Rational.CMP_EQ);
do_test("ONE.sub(M_ONE).cmp(TWO)", ONE.sub(M_ONE).cmp(TWO), Rational.CMP_EQ);
do_test("ONE.sub(ZERO).cmp(ONE)", ONE.sub(ZERO).cmp(ONE), Rational.CMP_EQ);
do_test("ONE.sub(ONE).cmp(ZERO)", ONE.sub(ONE).cmp(ZERO), Rational.CMP_EQ);
do_test("ONE.sub(TWO).cmp(M_ONE)", ONE.sub(TWO).cmp(M_ONE), Rational.CMP_EQ);
do_test("TWO.sub(ZERO).cmp(TWO)", TWO.sub(ZERO).cmp(TWO), Rational.CMP_EQ);
do_test("TWO.sub(ONE).cmp(ONE)", TWO.sub(ONE).cmp(ONE), Rational.CMP_EQ);
do_test("TWO.sub(TWO).cmp(ZERO)", TWO.sub(TWO).cmp(ZERO), Rational.CMP_EQ);
;;
do_test("M_TWO.sub(0).cmp(M_TWO)", M_TWO.sub(0).cmp(M_TWO), Rational.CMP_EQ);
do_test("M_TWO.sub(-1).cmp(M_ONE)", M_TWO.sub(-1).cmp(M_ONE), Rational.CMP_EQ);
do_test("M_TWO.sub(-2).cmp(ZERO)", M_TWO.sub(-2).cmp(ZERO), Rational.CMP_EQ);
do_test("M_TWO.sub(-3).cmp(ONE)", M_TWO.sub(-3).cmp(ONE), Rational.CMP_EQ);
do_test("M_TWO.sub(-4).cmp(TWO)", M_TWO.sub(-4).cmp(TWO), Rational.CMP_EQ);
do_test("M_ONE.sub(1).cmp(M_TWO)", M_ONE.sub(1).cmp(M_TWO), Rational.CMP_EQ);
do_test("M_ONE.sub(0).cmp(M_ONE)", M_ONE.sub(0).cmp(M_ONE), Rational.CMP_EQ);
do_test("M_ONE.sub(-1).cmp(ZERO)", M_ONE.sub(-1).cmp(ZERO), Rational.CMP_EQ);
do_test("M_ONE.sub(-2).cmp(ONE)", M_ONE.sub(-2).cmp(ONE), Rational.CMP_EQ);
do_test("M_ONE.sub(-3).cmp(TWO)", M_ONE.sub(-3).cmp(TWO), Rational.CMP_EQ);
do_test("M_HALF.sub(-1).cmp(HALF)", M_HALF.sub(-1).cmp(HALF), Rational.CMP_EQ);
do_test("ZERO.sub(2).cmp(M_TWO)", ZERO.sub(2).cmp(M_TWO), Rational.CMP_EQ);
do_test("ZERO.sub(1).cmp(M_ONE)", ZERO.sub(1).cmp(M_ONE), Rational.CMP_EQ);
do_test("ZERO.sub(0).cmp(ZERO)", ZERO.sub(0).cmp(ZERO), Rational.CMP_EQ);
do_test("ZERO.sub(-1).cmp(ONE)", ZERO.sub(-1).cmp(ONE), Rational.CMP_EQ);
do_test("ZERO.sub(-2).cmp(TWO)", ZERO.sub(-2).cmp(TWO), Rational.CMP_EQ);
do_test("ONE.sub(3).cmp(M_TWO)", ONE.sub(3).cmp(M_TWO), Rational.CMP_EQ);
do_test("ONE.sub(2).cmp(M_ONE)", ONE.sub(2).cmp(M_ONE), Rational.CMP_EQ);
do_test("ONE.sub(1).cmp(ZERO)", ONE.sub(1).cmp(ZERO), Rational.CMP_EQ);
do_test("ONE.sub(0).cmp(ONE)", ONE.sub(0).cmp(ONE), Rational.CMP_EQ);
do_test("ONE.sub(-1).cmp(TWO)", ONE.sub(-1).cmp(TWO), Rational.CMP_EQ);
do_test("HALF.sub(1).cmp(M_HALF)", HALF.sub(1).cmp(M_HALF), Rational.CMP_EQ);
do_test("TWO.sub(4).cmp(M_TWO)", TWO.sub(4).cmp(M_TWO), Rational.CMP_EQ);
do_test("TWO.sub(3).cmp(M_ONE)", TWO.sub(3).cmp(M_ONE), Rational.CMP_EQ);
do_test("TWO.sub(2).cmp(ZERO)", TWO.sub(2).cmp(ZERO), Rational.CMP_EQ);
do_test("TWO.sub(1).cmp(ONE)", TWO.sub(1).cmp(ONE), Rational.CMP_EQ);
do_test("TWO.sub(0).cmp(TWO)", TWO.sub(0).cmp(TWO), Rational.CMP_EQ);

// a mess o' multiplies
//
for (-6 => int n1; n1 <= 6; n1++) {
    for (-6 => int d1; d1 <= 6; d1++) {
	for (-6 => int n2; n2 <= 6; n2++) {
	    for (-6 => int d2; d2 <= 6; d2++) {
		if ((d1 != 0) && (d2 != 0)) {
		    Rational.create(n1, d1) @=> Rational @ r1;
		    Rational.create(n2, d2) @=> Rational @ r2;
		    r1.mul(r2) @=> Rational @ r3;
		    ((n1 * n2) $ float) / ((d1 * d2) $ float) => float f3;
		    "("+r1.toString()+")*("+r2.toString()+")=("+r3.toString()+")="+f3 =>  string label;
		    do_test(label, r3.toFloat() == f3, true);
		}
	    }
	}
    }
}

// similar, but with integer multiplication
//
for (-6 => int n1; n1 <= 6; n1++) {
    for (-6 => int d1; d1 <= 6; d1++) {
	for (-6 => int i2; i2 <= 6; i2++) {
	    if (d1 != 0) {
		Rational.create(n1, d1) @=> Rational @ r1;
		r1.mul(i2) @=> Rational @ r3;
		((n1 * i2) $ float) / (d1 $ float) => float f3;
		"("+r1.toString()+")*"+i2+"=("+r3.toString()+")="+f3 =>  string label;
		do_test(label, r3.toFloat() == f3, true);
	    }
	}
    }
}


// a mess o' divides
//
for (-6 => int n1; n1 <= 6; n1++) {
    for (-6 => int d1; d1 <= 6; d1++) {
	for (-6 => int n2; n2 <= 6; n2++) {
	    for (-6 => int d2; d2 <= 6; d2++) {
		if ((d1 != 0) && (d2 != 0) && (n2 != 0)) {
		    Rational.create(n1, d1) @=> Rational @ r1;
		    Rational.create(n2, d2) @=> Rational @ r2;
		    r1.quo(r2) @=> Rational @ r3;
		    ((n1 * d2) $ float) / ((d1 * n2) $ float) => float f3;
		    "("+r1.toString()+")/("+r2.toString()+")=("+r3.toString()+")="+f3 =>  string label;
		    do_test(label, r3.toFloat() == f3, true);
		}
	    }
	}
    }
}

// PRINT_ALL => errp;

// similar, but with integer division
//
for (-6 => int n1; n1 <= 6; n1++) {
    for (-6 => int d1; d1 <= 6; d1++) {
	for (-6 => int i2; i2 <= 6; i2++) {
	    if ((d1 != 0) && (i2 != 0)) {
		Rational.create(n1, d1) @=> Rational @ r1;
		r1.quo(i2) @=> Rational @ r3;
		(n1 $ float) / ((d1 * i2) $ float) => float f3;
		"("+r1.toString()+")/"+i2+"=("+r3.toString()+")="+f3 =>  string label;
		do_test(label, r3.toFloat() == f3, true);
	    }
	}
    }
}