package ps1; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** This class contains a set of test cases that can be used to test the implementation of the RatPoly class.

*/ public class RatPolyTest extends TestCase { // get a RatNum for an integer private RatNum num(int i) { return new RatNum(i); } private RatNum nanNum = (new RatNum(1)).div(new RatNum(0)); // convenient way to make a RatPoly private RatPoly poly(int coef, int expt) { return new RatPoly(coef, expt); } // Convenient way to make a quadratic polynomial, arguments // are just the coefficients, highest degree term to lowest private RatPoly quadPoly(int x2, int x1, int x0) { RatPoly ratPoly = new RatPoly(x2, 2); return ratPoly.add(poly(x1, 1)).add(poly(x0, 0)); } // convenience for parse private RatPoly parse(String s) { return RatPoly.parse(s); } // convenience for zero RatPoly private RatPoly zero() { return new RatPoly(); } public RatPolyTest(String name) { super(name); } // only unparse is tested here private void eq(RatPoly p, String target) { String t = p.unparse(); assertEquals(target, t); } private void eq(RatPoly p, String target, String message) { String t = p.unparse(); assertEquals(message, target, t); } // parses s into p, and then checks that it is as anticipated // forall i, parse(s).coeff(anticipDegree - i) = anticipCoeffForExpts(i) // (anticipDegree - i) means that we expect coeffs to be expressed // corresponding to decreasing expts private void eqP(String s, int anticipDegree, RatNum[] anticipCoeffs) { RatPoly p = parse(s); assertTrue(p.degree() == anticipDegree); for (int i = 0; i <= anticipDegree; i++) { assertTrue( "wrong coeff; \n" + "anticipated: " + anticipCoeffs[i] + "; received: " + p.coeff(anticipDegree - i) + "\n" + " received: " + p + " anticipated:" + s, p.coeff(anticipDegree - i).equals(anticipCoeffs[i])); } } // added convenience: express coeffs as ints private void eqP(String s, int anticipDegree, int[] intCoeffs) { RatNum[] coeffs = new RatNum[intCoeffs.length]; for (int i = 0; i < coeffs.length; i++) { coeffs[i] = num(intCoeffs[i]); } eqP(s, anticipDegree, coeffs); } // make sure that unparsing a parsed string yields the string itself private void assertUnparseWorks(String s) { assertEquals(s, parse(s).unparse()); } public void testParseSimple() { eqP("0", 0, new int[] { 0 }); eqP("x", 1, new int[] { 1, 0 }); eqP("x^2", 2, new int[] { 1, 0, 0 }); } public void testParseMultTerms() { eqP("x^3+x^2", 3, new int[] { 1, 1, 0, 0 }); eqP("x^3-x^2", 3, new int[] { 1, -1, 0, 0 }); eqP("x^10+x^2", 10, new int[] { 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }); } public void testParseLeadingNeg() { eqP("-x^2", 2, new int[] { -1, 0, 0 }); eqP("-x^2+1", 2, new int[] { -1, 0, 1 }); eqP("-x^2+x", 2, new int[] { -1, 1, 0 }); } public void testParseLeadingConstants() { eqP("10*x", 1, new int[] { 10, 0 }); eqP("10*x^4+x^2", 4, new int[] { 10, 0, 1, 0, 0 }); eqP("10*x^4+100*x^2", 4, new int[] { 10, 0, 100, 0, 0 }); eqP("-10*x^4+100*x^2", 4, new int[] { -10, 0, 100, 0, 0 }); } public void testParseRationals() { eqP("1/2", 0, new RatNum[] { num(1).div(num(2))}); eqP("1/2*x", 1, new RatNum[] { num(1).div(num(2)), num(0)}); eqP("x+1/3", 1, new RatNum[] { num(1), num(1).div(num(3))}); eqP( "1/2*x+1/3", 1, new RatNum[] { num(1).div(num(2)), num(1).div(num(3))}); eqP( "1/2*x+3/2", 1, new RatNum[] { num(1).div(num(2)), num(3).div(num(2))}); eqP( "1/2*x^3+3/2", 3, new RatNum[] { num(1).div(num(2)), num(0), num(0), num(3).div(num(2))}); eqP( "1/2*x^3+3/2*x^2+1", 3, new RatNum[] { num(1).div(num(2)), num(3).div(num(2)), num(0), num(1)}); } public void testParseNaN() { assertTrue(parse("NaN").isNaN()); } public void testUnparseSimple() { assertUnparseWorks("0"); assertUnparseWorks("x"); assertUnparseWorks("x^2"); } public void testUnparseMultTerms() { assertUnparseWorks("x^3+x^2"); assertUnparseWorks("x^3-x^2"); assertUnparseWorks("x^100+x^2"); } public void testUnparseLeadingNeg() { assertUnparseWorks("-x^2"); assertUnparseWorks("-x^2+1"); assertUnparseWorks("-x^2+x"); } public void testUnparseLeadingConstants() { assertUnparseWorks("10*x"); assertUnparseWorks("10*x^100+x^2"); assertUnparseWorks("10*x^100+100*x^2"); assertUnparseWorks("-10*x^100+100*x^2"); } public void testUnparseRationals() { assertUnparseWorks("1/2"); assertUnparseWorks("1/2*x"); assertUnparseWorks("x+1/3"); assertUnparseWorks("1/2*x+1/3"); assertUnparseWorks("1/2*x+3/2"); assertUnparseWorks("1/2*x^10+3/2"); assertUnparseWorks("1/2*x^10+3/2*x^2+1"); } public void testUnparseNaN() { assertUnparseWorks("NaN"); } public void testNoArgCtor() { eq(new RatPoly(), "0"); } public void testTwoArgCtor() { eq(poly(0, 0), "0"); eq(poly(0, 1), "0"); eq(poly(1, 0), "1"); eq(poly(-1, 0), "-1"); eq(poly(1, 1), "x"); eq(poly(1, 2), "x^2"); eq(poly(2, 2), "2*x^2"); eq(poly(2, 3), "2*x^3"); eq(poly(-2, 3), "-2*x^3"); eq(poly(-1, 1), "-x"); eq(poly(-1, 3), "-x^3"); } public void testDegree() { assertTrue("x^0 degree 0", poly(1, 0).degree() == 0); assertTrue("x^1 degree 1", poly(1, 1).degree() == 1); assertTrue("x^100 degree 100", poly(1, 100).degree() == 100); assertTrue("0*x^100 degree 0", poly(0, 100).degree() == 0); assertTrue("0*x^0 degree 0", poly(0, 0).degree() == 0); } public void testAdd() { RatPoly _XSqPlus2X = poly(1, 2).add(poly(1, 1)).add(poly(1, 1)); RatPoly _2XSqPlusX = poly(1, 2).add(poly(1, 2)).add(poly(1, 1)); eq(poly(1, 0).add(poly(1, 0)), "2"); eq(poly(1, 0).add(poly(5, 0)), "6"); eq(poly(1, 1).add(poly(1, 1)), "2*x"); eq(poly(1, 2).add(poly(1, 2)), "2*x^2"); eq(poly(1, 2).add(poly(1, 1)), "x^2+x"); eq(_XSqPlus2X, "x^2+2*x"); eq(_2XSqPlusX, "2*x^2+x"); eq(poly(1, 3).add(poly(1, 1)), "x^3+x"); } public void testSub() { eq(poly(1, 1).sub(poly(1, 0)), "x-1"); eq(poly(1, 1).add(poly(1, 0)), "x+1"); } public void testMul() { eq(poly(0, 0).mul(poly(0, 0)), "0"); eq(poly(1, 0).mul(poly(1, 0)), "1"); eq(poly(1, 0).mul(poly(2, 0)), "2"); eq(poly(2, 0).mul(poly(2, 0)), "4"); eq(poly(1, 0).mul(poly(1, 1)), "x"); eq(poly(1, 1).mul(poly(1, 1)), "x^2"); eq(poly(1, 1).sub(poly(1, 0)).mul(poly(1, 1).add(poly(1, 0))), "x^2-1"); } public void testOpsWithNaN(RatPoly p) { RatPoly nan = RatPoly.parse("NaN"); eq(p.add(nan), "NaN"); eq(nan.add(p), "NaN"); eq(p.sub(nan), "NaN"); eq(nan.sub(p), "NaN"); eq(p.mul(nan), "NaN"); eq(nan.mul(p), "NaN"); eq(p.div(nan), "NaN"); eq(nan.div(p), "NaN"); } public void testOpsWithNaN() { testOpsWithNaN(poly(0, 0)); testOpsWithNaN(poly(0, 1)); testOpsWithNaN(poly(1, 0)); testOpsWithNaN(poly(1, 1)); testOpsWithNaN(poly(2, 0)); testOpsWithNaN(poly(2, 1)); testOpsWithNaN(poly(0, 2)); testOpsWithNaN(poly(1, 2)); } public void testImmutabilityOfOperations() { // not the most thorough test possible, but hopefully will // catch the easy cases early on... RatPoly one = poly(1, 0); RatPoly two = poly(2, 0); one.degree(); two.degree(); eq(one, "1", "Degree mutates receiver!"); eq(two, "2", "Degree mutates receiver!"); one.coeff(0); two.coeff(0); eq(one, "1", "Coeff mutates receiver!"); eq(two, "2", "Coeff mutates receiver!"); one.isNaN(); two.isNaN(); eq(one, "1", "isNaN mutates receiver!"); eq(two, "2", "isNaN mutates receiver!"); one.eval(0.0); two.eval(0.0); eq(one, "1", "eval mutates receiver!"); eq(two, "2", "eval mutates receiver!"); one.negate(); two.negate(); eq(one, "1", "Negate mutates receiver!"); eq(two, "2", "Negate mutates receiver!"); one.add(two); eq(one, "1", "Add mutates receiver!"); eq(two, "2", "Add mutates argument!"); one.sub(two); eq(one, "1", "Sub mutates receiver!"); eq(two, "2", "Sub mutates argument!"); one.mul(two); eq(one, "1", "Mul mutates receiver!"); eq(two, "2", "Mul mutates argument!"); one.div(two); eq(one, "1", "Div mutates receiver!"); eq(two, "2", "Div mutates argument!"); } public void testEval() { RatPoly zero = new RatPoly(); RatPoly one = new RatPoly(1, 0); RatPoly _X = new RatPoly(1, 1); RatPoly _2X = new RatPoly(2, 1); RatPoly _XSq = new RatPoly(1, 2); assertEquals(" 0 at 0 ", 0.0, zero.eval(0.0), 0.0001); assertEquals(" 0 at 1 ", 0.0, zero.eval(1.0), 0.0001); assertEquals(" 0 at 2 ", 0.0, zero.eval(2.0), 0.0001); assertEquals(" 1 at 0 ", 1.0, one.eval(0.0), 0.0001); assertEquals(" 1 at 1 ", 1.0, one.eval(1.0), 0.0001); assertEquals(" 1 at 1 ", 1.0, one.eval(2.0), 0.0001); assertEquals(" x at 0 ", 0.0, _X.eval(0.0), 0.0001); assertEquals(" x at 1 ", 1.0, _X.eval(1.0), 0.0001); assertEquals(" x at 2 ", 2.0, _X.eval(2.0), 0.0001); assertEquals(" 2*x at 0 ", 0.0, _2X.eval(0.0), 0.0001); assertEquals(" 2*x at 1 ", 2.0, _2X.eval(1.0), 0.0001); assertEquals(" 2*x at 2 ", 4.0, _2X.eval(2.0), 0.0001); assertEquals(" x^2 at 0 ", 0.0, _XSq.eval(0.0), 0.0001); assertEquals(" x^2 at 1 ", 1.0, _XSq.eval(1.0), 0.0001); assertEquals(" x^2 at 2 ", 4.0, _XSq.eval(2.0), 0.0001); RatPoly _XSq_minus_2X = _XSq.sub(_2X); assertEquals(" x^2-2*x at 0 ", 0.0, _XSq_minus_2X.eval(0.0), 0.0001); assertEquals(" x^2-2*x at 1 ", -1.0, _XSq_minus_2X.eval(1.0), 0.0001); assertEquals(" x^2-2*x at 2 ", 0.0, _XSq_minus_2X.eval(2.0), 0.0001); assertEquals(" x^2-2*x at 3 ", 3.0, _XSq_minus_2X.eval(3.0), 0.0001); } public void testCoeff() { // coeff already gets some grunt testing in eqP; checking an interesting // input here... RatPoly _XSqPlus2X = poly(1, 2).add(poly(1, 1)).add(poly(1, 1)); RatPoly _2XSqPlusX = poly(1, 2).add(poly(1, 2)).add(poly(1, 1)); assertTrue(_XSqPlus2X.coeff(-1).equals(num(0))); assertTrue(_XSqPlus2X.coeff(-10).equals(num(0))); assertTrue(_2XSqPlusX.coeff(-1).equals(num(0))); assertTrue(_2XSqPlusX.coeff(-10).equals(num(0))); assertTrue(zero().coeff(-10).equals(num(0))); assertTrue(zero().coeff(-1).equals(num(0))); } public void testDiv() { // 0/x = 0 eq(poly(0, 1).div(poly(1, 1)), "0"); // 2/1 = 2 eq(poly(2, 0).div(poly(1, 0)), "2"); // x/x = 1 eq(poly(1, 1).div(poly(1, 1)), "1"); // -x/x = -1 eq(poly(-1, 1).div(poly(1, 1)), "-1"); // x/-x = -1 eq(poly(1, 1).div(poly(-1, 1)), "-1"); // -x/-x = 1 eq(poly(-1, 1).div(poly(-1, 1)), "1"); // -x^2/x = -x eq(poly(-1, 2).div(poly(1, 1)), "-x"); // x^100/x^1000 = 0 eq(poly(1, 100).div(poly(1, 1000)), "0"); // x^100/x = x^99 eq(poly(1, 100).div(poly(1, 1)), "x^99"); // x^99/x^98 = x eq(poly(1, 99).div(poly(1, 98)), "x"); // x^10 / x = x^9 (r: 0) eq(poly(1, 10).div(poly(1, 1)), "x^9"); // x^10 / x^3+x^2 = x^7-x^6+x^5-x^4+x^3-x^2+x-1 (r: -x^2) eq( poly(1, 10).div(poly(1, 3).add(poly(1, 2))), "x^7-x^6+x^5-x^4+x^3-x^2+x-1"); // x^10 / x^3+x^2+x = x^7-x^6+x^4-x^3+x-1 (r: -x) eq( poly(1, 10).div(poly(1, 3).add(poly(1, 2).add(poly(1, 1)))), "x^7-x^6+x^4-x^3+x-1"); // x^10+x^5 / x = x^9+x^4 (r: 0) eq(poly(1, 10).add(poly(1, 5)).div(poly(1, 1)), "x^9+x^4"); // x^10+x^5 / x^3 = x^7+x^2 (r: 0) eq(poly(1, 10).add(poly(1, 5)).div(poly(1, 3)), "x^7+x^2"); // x^10+x^5 / x^3+x+3 = x^7-x^5-3*x^4+x^3+7*x^2+8*x-10 (r: 29*x^2+14*x-30) eq( poly(1, 10).add(poly(1, 5)).div( poly(1, 3).add(poly(1, 1)).add(poly(3, 0))), "x^7-x^5-3*x^4+x^3+7*x^2+8*x-10"); } public void testDivComplexI() { // (x+1)*(x+1) = x^2+2*x+1 eq( poly(1, 2).add(poly(2, 1)).add(poly(1, 0)).div( poly(1, 1).add(poly(1, 0))), "x+1"); // (x-1)*(x+1) = x^2-1 eq(poly(1, 2).add(poly(-1, 0)).div(poly(1, 1).add(poly(1, 0))), "x-1"); } public void testDivComplexII() { // x^8+2*x^6+8*x^5+2*x^4+17*x^3+11*x^2+8*x+3 = // (x^3+2*x+1) * (x^5+7*x^2+2*x+3) RatPoly large = poly(1, 8) .add(poly(2, 6)) .add(poly(8, 5)) .add(poly(2, 4)) .add(poly(17, 3)) .add(poly(11, 2)) .add(poly(8, 1)) .add(poly(3, 0)); // x^3+2*x+1 RatPoly sub1 = poly(1, 3).add(poly(2, 1)).add(poly(1, 0)); // x^5+7*x^2+2*x+3 RatPoly sub2 = poly(1, 5).add(poly(7, 2)).add(poly(2, 1)).add(poly(3, 0)); // just a last minute typo check... eq(sub1.mul(sub2), large.unparse()); eq(sub2.mul(sub1), large.unparse()); eq(large.div(sub2), "x^3+2*x+1"); eq(large.div(sub1), "x^5+7*x^2+2*x+3"); } public void testDivExamplesFromSpec() { // seperated this test case out because it has a dependency on // both "parse" and "div" functioning properly // example 1 from spec eq(parse("x^3-2*x+3").div(parse("3*x^2")), "1/3*x"); // example 2 from spec eq(parse("x^2+2*x+15").div(parse("2*x^3")), "0"); } public void testDivExampleFromPset() { eq( parse("x^8+x^6+10*x^4+10*x^3+8*x^2+2*x+8").div( parse("3*x^6+5*x^4+9*x^2+4*x+8")), "1/3*x^2-2/9"); } private void assertIsNaNanswer(RatPoly nanAnswer) { eq(nanAnswer, "NaN"); } public void testDivByZero() { RatPoly nanAnswer; nanAnswer = poly(1, 0).div(zero()); assertIsNaNanswer(nanAnswer); nanAnswer = poly(1, 1).div(zero()); assertIsNaNanswer(nanAnswer); } public void testDivByPolyWithNaN() { RatPoly nan_x2 = poly(1, 2).mul(poly(1, 1).div(zero())); RatPoly one_x1 = new RatPoly(1, 1); assertIsNaNanswer(nan_x2.div(one_x1)); assertIsNaNanswer(one_x1.div(nan_x2)); assertIsNaNanswer(nan_x2.div(zero())); assertIsNaNanswer(zero().div(nan_x2)); assertIsNaNanswer(nan_x2.div(nan_x2)); } public void testIsNaN() { assertTrue(RatPoly.parse("NaN").isNaN()); assertTrue(!RatPoly.parse("1").isNaN()); assertTrue(!RatPoly.parse("1/2").isNaN()); assertTrue(!RatPoly.parse("x+1").isNaN()); assertTrue(!RatPoly.parse("x^2+x+1").isNaN()); } public void testDifferentiate() { eq(poly(1, 1).differentiate(), "1"); eq(quadPoly(7, 5, 99).differentiate(), "14*x+5"); eq(quadPoly(3, 2, 1).differentiate(), "6*x+2"); eq(quadPoly(1, 0, 1).differentiate(), "2*x"); assertIsNaNanswer(RatPoly.parse("NaN").differentiate()); } public void testAntiDifferentiate() { eq(poly(1, 0).antiDifferentiate(new RatNum(1)), "x+1"); eq(poly(2, 1).antiDifferentiate(new RatNum(1)), "x^2+1"); eq(quadPoly(0, 6, 2).antiDifferentiate(new RatNum(1)), "3*x^2+2*x+1"); eq(quadPoly(4, 6, 2).antiDifferentiate(new RatNum(0)), "4/3*x^3+3*x^2+2*x"); assertIsNaNanswer(RatPoly.parse("NaN").antiDifferentiate(new RatNum(1))); assertIsNaNanswer(poly(1, 0).antiDifferentiate(new RatNum(1, 0))); } public void testIntegrate() { assertEquals("one from 0 to 1", 1.0, poly(1,0).integrate(0,1), 0.0001 ); assertEquals("2x from 1 to -2", 3.0, poly(2,1).integrate(1,-2), 0.0001 ); assertEquals("7*x^2+6*x+2 from 1 to 5", 369.33333333, quadPoly(7,6,2).integrate(1,5), 0.0001 ); } // Tell JUnit what order to run the tests in public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest(new RatPolyTest("testParseSimple")); suite.addTest(new RatPolyTest("testParseMultTerms")); suite.addTest(new RatPolyTest("testParseLeadingNeg")); suite.addTest(new RatPolyTest("testParseLeadingConstants")); suite.addTest(new RatPolyTest("testParseRationals")); suite.addTest(new RatPolyTest("testParseNaN")); suite.addTest(new RatPolyTest("testUnparseSimple")); suite.addTest(new RatPolyTest("testUnparseMultTerms")); suite.addTest(new RatPolyTest("testUnparseLeadingNeg")); suite.addTest(new RatPolyTest("testUnparseLeadingConstants")); suite.addTest(new RatPolyTest("testUnparseRationals")); suite.addTest(new RatPolyTest("testUnparseNaN")); suite.addTest(new RatPolyTest("testNoArgCtor")); suite.addTest(new RatPolyTest("testTwoArgCtor")); suite.addTest(new RatPolyTest("testDegree")); suite.addTest(new RatPolyTest("testAdd")); suite.addTest(new RatPolyTest("testSub")); suite.addTest(new RatPolyTest("testMul")); suite.addTest(new RatPolyTest("testOpsWithNaN")); suite.addTest(new RatPolyTest("testCoeff")); suite.addTest(new RatPolyTest("testDiv")); suite.addTest(new RatPolyTest("testDivComplexI")); suite.addTest(new RatPolyTest("testDivComplexII")); suite.addTest(new RatPolyTest("testDivExamplesFromSpec")); suite.addTest(new RatPolyTest("testDivExampleFromPset")); suite.addTest(new RatPolyTest("testDivByZero")); suite.addTest(new RatPolyTest("testDivByPolyWithNaN")); suite.addTest(new RatPolyTest("testIsNaN")); suite.addTest(new RatPolyTest("testEval")); suite.addTest(new RatPolyTest("testImmutabilityOfOperations")); suite.addTest(new RatPolyTest("testDifferentiate")); suite.addTest(new RatPolyTest("testAntiDifferentiate")); suite.addTest(new RatPolyTest("testIntegrate")); return suite; } }