//
// RatPolyTest.swift
// RatNumTests
//
import XCTest
@testable import RatNum
class RatPolyTest: XCTestCase {
//poly1 = 1*x^1 + 2*x^2 + 3*x^3 + 4*x^4 + 5*x^5
static let poly1 = RatPoly("1*x^1+2*x^2+3*x^3+4*x^4+5*x^5")!
//RatPolyTest.neg_poly1 = -1*x^1 + -2*x^2 + -3*x^3 + -4*x^4 + -5*x^5
static let neg_poly1 = -RatPolyTest.poly1
//RatPolyTest.poly2 = 6*x^2 + 7*x^3 + 8*x^4
static let poly2 = RatPoly("6*x^2+7*x^3+8*x^4")!
//RatPolyTest.neg_poly2 = -6*x^2 + -7*x^3 + -8*x^4
static let neg_poly2 = -RatPolyTest.poly2
// RatPolyTest.poly3 = 9*x^3 + 10*x^4
static let poly3 = RatPoly("9*x^3+10*x^4")!
// RatPolyTest.neg_poly3 = -9*x^3 + -10*x^4
static let neg_poly3 = -RatPolyTest.poly3
//SetUp Method depends on RatPoly add and Negate
//Tests that are intended to verify add or negate should variables declared in this setUp method
override func setUp() -> Void {
super.setUp()
}
// accuracy of double comparision
private static let accuracy = 0.000001
// get a RatNum for an integer
private func num(_ i : Int) -> RatNum {
return RatNum(i)
}
// convenient way to make a RatPoly
private func poly(_ coeff: Int, _ expt: Int) -> RatPoly {
return RatPoly(c: coeff, e: expt)
}
// Convenient way to make a quadratic polynomial, arguments
// are just the coefficients, highest degree term to lowest
private func quadPoly(_ x2 : Int, _ x1: Int, _ x0: Int) -> RatPoly {
let ratPoly = RatPoly(c: x2, e: 2)
return ratPoly + poly(x1, 1) + poly(x0, 0)
}
// convenience for zero RatPoly
private func zero() -> RatPoly {
return RatPoly()
}
// only toString is tested here
private func eq(_ p: RatPoly, _ target: String, file: StaticString = #file, line: UInt = #line) {
XCTAssertEqual(target, String(describing: p), file: file, line: line)
}
private func eq(_ p: RatPoly, _ target: String, _ message: String, file: StaticString = #file, line: UInt = #line) {
XCTAssertEqual(target, String(describing: p), message, file: file, line: line)
}
// parses s into p, and then checks that it is as anticipated
// forall i, valueOf(s).coeff(anticipDegree - i) = anticipCoeffForExpts(i)
// (anticipDegree - i) means that we expect coeffs to be expressed
// corresponding to decreasing expts
private func eqP(_ s: String, _ anticipDegree: Int, _ anticipCoeffs: [RatNum], file: StaticString = #file, line: UInt = #line) {
let p = RatPoly( s)!
XCTAssertEqual(anticipDegree, p.degree, file: file, line: line)
for i in 0...anticipDegree {
XCTAssert(p[(anticipDegree - i)].coeff == anticipCoeffs[i],
"wrong coeff anticipated \"(anticipCoeffs[i]), received: \"(p[anticipDegree - i].coeff)"
+ "\"n received: \"(p), anticipated: \"(s)", file: file, line: line)
}
}
// added convenience: express coeffs as ints
private func eqP(_ s : String, _ anticipDegree: Int, _ intCoeffs: [Int], file: StaticString = #file, line: UInt = #line) {
let coeffs = intCoeffs.map( { num($0) })
eqP(s, anticipDegree, coeffs, file: file, line: line)
}
// make sure that unparsing a parsed string yields the string itself
private func assertToStringWorks(_ s: String, file: StaticString = #file, line: UInt = #line) {
XCTAssertEqual(s, String(describing: RatPoly( s)!), file: file, line: line)
}
///////////////////////////////////////////////////////////////////////////////////////
//// Constructor
///////////////////////////////////////////////////////////////////////////////////////
public func testNoArgCtor() -> Void {
eq(RatPoly(), "0")
}
public func testTwoArgCtorWithZeroExp() -> Void {
eq(poly(0, 0), "0")
eq(poly(0, 1), "0")
eq(poly(1, 0), "1")
eq(poly(-1, 0), "-1")
}
public func testTwoArgCtorWithOneExp() -> Void {
eq(poly(1, 1), "x")
eq(poly(-1, 1), "-x")
}
public func testTwoArgCtorWithLargeExp() -> Void {
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, 3), "-x^3")
}
///////////////////////////////////////////////////////////////////////////////////////
//// isNaN Test
///////////////////////////////////////////////////////////////////////////////////////
public func testisNaN() -> Void {
XCTAssertTrue(RatPoly("NaN")!.isNaN)
}
public func testIsNotNaN() -> Void {
XCTAssertFalse(RatPoly("1")!.isNaN)
XCTAssertFalse(RatPoly("1/2")!.isNaN)
XCTAssertFalse(RatPoly("x+1")!.isNaN)
XCTAssertFalse(RatPoly("x^2+x+1")!.isNaN)
}
public func testIsNaNEmptyPolynomial() -> Void {
let empty = RatPoly()
XCTAssertTrue((empty/empty).isNaN)
}
///////////////////////////////////////////////////////////////////////////////////////
//// Value Of Test
///////////////////////////////////////////////////////////////////////////////////////
public func testValueOfSimple() -> Void {
eqP("0", 0, [ 0 ])
eqP("x", 1, [ 1, 0 ])
eqP("x^2", 2, [ 1, 0, 0 ])
}
public func testValueOfMultTerms() -> Void {
eqP("x^3+x^2", 3, [ 1, 1, 0, 0 ])
eqP("x^3-x^2", 3, [ 1, -1, 0, 0 ])
eqP("x^10+x^2", 10, [ 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ])
}
public func testValueOfLeadingNeg() -> Void {
eqP("-x^2", 2, [ -1, 0, 0 ])
eqP("-x^2+1", 2, [ -1, 0, 1 ])
eqP("-x^2+x", 2, [ -1, 1, 0 ])
}
public func testValueOfLeadingConstants() -> Void {
eqP("10*x", 1, [ 10, 0 ])
eqP("10*x^4+x^2", 4, [ 10, 0, 1, 0, 0 ])
eqP("10*x^4+100*x^2", 4, [ 10, 0, 100, 0, 0 ])
eqP("-10*x^4+100*x^2", 4, [ -10, 0, 100, 0, 0 ])
}
public func testValueOfRationalsSingleTerms() -> Void {
eqP("1/2", 0, [ num(1)/(num(2)) ])
eqP("1/2*x", 1, [ num(1)/(num(2)), num(0) ])
eqP("1/1000", 0, [ num(1)/(num(1000)) ])
eqP("1/1000*x", 1, [ num(1)/(num(1000)), num(0) ])
}
public func testValueOfRationalsMultipleTerms() -> Void {
eqP("x+1/3", 1, [ num(1), num(1)/(num(3)) ])
eqP("1/2*x+1/3", 1, [ num(1)/(num(2)),
num(1)/(num(3)) ])
eqP("1/2*x+3/2", 1, [ num(1)/(num(2)),
num(3)/(num(2)) ])
eqP("1/2*x^3+3/2", 3, [ num(1)/(num(2)), num(0),
num(0), num(3)/(num(2)) ])
eqP("1/2*x^3+3/2*x^2+1", 3, [ num(1)/(num(2)),
num(3)/(num(2)), num(0), num(1) ])
}
public func testValueOfNaN() -> Void {
XCTAssertTrue(RatPoly("NaN")!.isNaN)
}
///////////////////////////////////////////////////////////////////////////////////////
//// To String Test
///////////////////////////////////////////////////////////////////////////////////////
public func testToStringSimple() -> Void {
assertToStringWorks("0")
assertToStringWorks("x")
assertToStringWorks("x^2")
}
public func testToStringMultTerms() -> Void {
assertToStringWorks("x^3+x^2")
assertToStringWorks("x^3-x^2")
assertToStringWorks("x^100+x^2")
}
public func testToStringLeadingNeg() -> Void {
assertToStringWorks("-x^2")
assertToStringWorks("-x^2+1")
assertToStringWorks("-x^2+x")
}
public func testToStringLeadingConstants() -> Void {
assertToStringWorks("10*x")
assertToStringWorks("10*x^100+x^2")
assertToStringWorks("10*x^100+100*x^2")
assertToStringWorks("-10*x^100+100*x^2")
}
public func testToStringRationalsSingleElems() -> Void {
assertToStringWorks("1/2")
assertToStringWorks("1/2*x")
}
public func testToStringRationalsMultiplElems() -> Void {
assertToStringWorks("x+1/3")
assertToStringWorks("1/2*x+1/3")
assertToStringWorks("1/2*x+3/2")
assertToStringWorks("1/2*x^10+3/2")
assertToStringWorks("1/2*x^10+3/2*x^2+1")
}
public func testToStringNaN() -> Void {
assertToStringWorks("NaN")
}
///////////////////////////////////////////////////////////////////////////////////////
//// Degree Test
///////////////////////////////////////////////////////////////////////////////////////
// test degree is zero when it should be
public func testDegreeZero() -> Void {
XCTAssertEqual(0, poly(1, 0).degree, "x^0 degree 0")
XCTAssertEqual(0, poly(0, 100).degree, "0*x^100 degree 0")
XCTAssertEqual(0, poly(0, 0).degree, "0*x^0 degree 0")
}
public func testDegreeNonZero() -> Void {
XCTAssertEqual(1, poly(1, 1).degree, "x^1 degree 1")
XCTAssertEqual(100, poly(1, 100).degree, "x^100 degree 100")
}
// test degree for multi termed polynomial
public func testDegreeNonZeroMultiTerm() -> Void {
XCTAssertEqual(5, RatPolyTest.poly1.degree, "\"(RatPolyTest.poly1) has Correct Degree")
XCTAssertEqual(4, RatPolyTest.poly2.degree, "\"(RatPolyTest.poly2) has Correct Degree")
}
///////////////////////////////////////////////////////////////////////////////////////
//// Negate Tests
///////////////////////////////////////////////////////////////////////////////////////
// test degree is zero when it should be
public func testNegateZero() -> Void {
XCTAssertEqual(RatPoly.zero, -RatPoly.zero)
}
// test degree is zero when it should be
public func testNegateNaN() -> Void {
XCTAssertEqual(RatPoly.nan, -RatPoly.nan)
}
// test degree is zero when it should be
public func testNegatePosToNeg() -> Void {
XCTAssertEqual(RatPoly("-x-2*x^2-3*x^3-4*x^4-5*x^5"), -RatPolyTest.poly1)
XCTAssertEqual(RatPoly("-6*x^2-7*x^3-8*x^4"), -RatPolyTest.poly2)
XCTAssertEqual(RatPoly("-9*x^3-10*x^4"), -RatPolyTest.poly3)
}
// test degree is zero when it should be
public func testNegatNegToPos() -> Void {
XCTAssertEqual(RatPolyTest.poly1, -RatPoly("-x-2*x^2-3*x^3-4*x^4-5*x^5")!)
XCTAssertEqual(RatPolyTest.poly2, -RatPoly("-6*x^2-7*x^3-8*x^4")!)
XCTAssertEqual(RatPolyTest.poly3, -RatPoly("-9*x^3-10*x^4")!)
}
///////////////////////////////////////////////////////////////////////////////////////
//// Addition Test
///////////////////////////////////////////////////////////////////////////////////////
public func testAddSingleTerm() -> Void {
eq(poly(1, 0)+poly(1, 0), "2")
eq(poly(1, 0)+poly(5, 0), "6")
eq(poly(1, 0)+poly(-1, 0), "0")
eq(poly(1, 1)+poly(1, 1), "2*x")
eq(poly(1, 2)+poly(1, 2), "2*x^2")
}
public func testAddMultipleTerm() -> Void {
let _XSqPlus2X = poly(1, 2)+poly(1, 1)+poly(1, 1)
let _2XSqPlusX = poly(1, 2)+poly(1, 2)+poly(1, 1)
eq(_XSqPlus2X, "x^2+2*x")
eq(_2XSqPlusX, "2*x^2+x")
eq(poly(1, 2)+poly(1, 1), "x^2+x")
eq(poly(1, 3)+poly(1, 1), "x^3+x")
}
// Note Polynomial is annotated as p
// p1 + p2 = p3 , p1 != 0 && p2 != 0, p1.degree == p2.degree
public func testAddSameDegree() -> Void {
let temp = RatPoly("3*x^1+4*x^2+5*x^3+6*x^4+7*x^5")!
XCTAssertEqual(RatPoly("4*x^1+6*x^2+8*x^3+10*x^4+12*x^5"), RatPolyTest.poly1+(temp))
let temp2 = RatPoly("-1*x^2-2*x^3-3*x^4")!
XCTAssertEqual(RatPoly("-7*x^2-9*x^3-11*x^4"), RatPolyTest.neg_poly2+(temp2))
}
// p1 + p2 = p3 , p1 != 0 && p2 != 0, p1.degree != p2.degree
public func testAddDifferentDegree() -> Void {
XCTAssertEqual(RatPoly("1*x^1+8*x^2+10*x^3+12*x^4+5*x^5"), RatPolyTest.poly1+(RatPolyTest.poly2))
XCTAssertEqual(RatPoly("-6*x^2-16*x^3-18*x^4"), RatPolyTest.neg_poly2+(RatPolyTest.neg_poly3))
}
// p + p = 2p
public func testAddWithItSelf() -> Void {
XCTAssertEqual(RatPoly("2*x^1+4*x^2+6*x^3+8*x^4+10*x^5"), RatPolyTest.poly1+(RatPolyTest.poly1))
XCTAssertEqual(RatPoly("-12*x^2-14*x^3-16*x^4"), RatPolyTest.neg_poly2+(RatPolyTest.neg_poly2))
}
// Addition Associativity (p1 + p2) + p3 = p1 + (p2 + p3)
public func testAddAssociativity() -> Void {
let operation1 = (RatPolyTest.poly1+(RatPolyTest.poly2))+(RatPolyTest.poly3)
let operation2 = (RatPolyTest.poly3+(RatPolyTest.poly2))+(RatPolyTest.poly1)
XCTAssertEqual(operation1, operation2)
let operation3 = (RatPolyTest.poly1+(RatPolyTest.neg_poly2))+(RatPolyTest.neg_poly3)
let operation4 = (RatPolyTest.neg_poly3+(RatPolyTest.neg_poly2))+(RatPolyTest.poly1)
XCTAssertEqual(operation3, operation4)
}
// Addition Commutative Rule p1 + p2 = p2 + p1
public func testAddCommutativity() -> Void {
XCTAssertEqual(RatPolyTest.poly1+(RatPolyTest.neg_poly2), RatPolyTest.neg_poly2+(RatPolyTest.poly1))
XCTAssertEqual(RatPolyTest.neg_poly3+(RatPolyTest.poly2), RatPolyTest.poly2+(RatPolyTest.neg_poly3))
}
// Zero Polynomial + Zero Polynomial == Zero Polynomial
public func testAddZeroToZero() -> Void {
XCTAssertEqual(RatPoly.zero, RatPoly.zero+(RatPoly.zero))
}
// Additive Identity p + Zero Polynomial == p && Zero Polynomial + p == p
public func testAddZeroToNonZero() -> Void {
XCTAssertEqual(RatPolyTest.poly1, RatPoly.zero+(RatPolyTest.poly1))
XCTAssertEqual(RatPolyTest.poly1, RatPolyTest.poly1+(RatPoly.zero))
}
// Additive Inverse p + (-p) = 0
public func testAddInverse() -> Void {
XCTAssertEqual(RatPoly.zero, RatPolyTest.poly1+(RatPolyTest.neg_poly1))
XCTAssertEqual(RatPoly.zero, RatPolyTest.poly2+(RatPolyTest.neg_poly2))
XCTAssertEqual(RatPoly.zero, RatPolyTest.poly3+(RatPolyTest.neg_poly3))
}
// NaN + NaN == NaN
public func testAddNaNtoNaN() -> Void {
XCTAssertEqual(RatPoly.nan, RatPoly.nan+(RatPoly.nan))
}
// t + NaN == NaN
public func testAddNaNtoNonNaN() -> Void {
XCTAssertEqual(RatPoly.nan, RatPoly.nan+(RatPolyTest.poly1))
XCTAssertEqual(RatPoly.nan, RatPolyTest.poly1+(RatPoly.nan))
}
///////////////////////////////////////////////////////////////////////////////////////
//// Subtraction Test
///////////////////////////////////////////////////////////////////////////////////////
//Also Tests Addition inverse property
// p1 - p2 = p3 , p1 != 0 && p2 != 0, p1.degree == p2.degree
public func testSubtractSameDegree() -> Void {
let temp = RatPoly("3*x^1+4*x^2+5*x^3+6*x^4+7*x^5")!
XCTAssertEqual(RatPoly("2*x^1+2*x^2+2*x^3+2*x^4+2*x^5"), temp-(RatPolyTest.poly1))
let temp2 = RatPoly("-1*x^2-2*x^3-3*x^4")!
XCTAssertEqual(RatPoly("7*x^2+9*x^3+11*x^4"), RatPolyTest.poly2-(temp2))
}
// p1 - p2 = p3 , p1 != 0 && p2 != 0, p1.degree != p2.degree
public func testSubtractDiffDegree() -> Void {
XCTAssertEqual(RatPoly("1*x^1-4*x^2-4*x^3-4*x^4+5*x^5"), RatPolyTest.poly1-(RatPolyTest.poly2))
XCTAssertEqual(RatPoly("-6*x^2-16*x^3-18*x^4"), RatPolyTest.neg_poly2-(RatPolyTest.poly3))
}
// Zero Polynomial - Zero Polynomial == Zero Polynomial
public func testSubtractZeroFromZero() -> Void {
XCTAssertEqual(RatPoly.zero, RatPoly.zero-(RatPoly.zero))
}
//Following test method depends on correctness of negate
// p - ZeroPolynomial == t && ZeroPolynomial - p == -p
public func testSubtractZeroAndNonZero() -> Void {
XCTAssertEqual(RatPolyTest.neg_poly1, RatPoly.zero-(RatPolyTest.poly1))
XCTAssertEqual(RatPolyTest.poly1, RatPolyTest.poly1-(RatPoly.zero))
}
// NaN - NaN == NaN
public func testSubtractNaNtoNaN() -> Void {
XCTAssertEqual(RatPoly.nan, RatPoly.nan-(RatPoly.nan))
}
// p - NaN == NaN && NaN - p == NaN
public func testSubtractNaNtoNonNaN() -> Void {
XCTAssertEqual(RatPoly.nan, RatPoly.nan-(RatPolyTest.poly1))
XCTAssertEqual(RatPoly.nan, RatPolyTest.poly1-(RatPoly.nan))
}
///////////////////////////////////////////////////////////////////////////////////////
//// Remove zero when appropriate test
///////////////////////////////////////////////////////////////////////////////////////
public func testZeroElim() -> Void {
// make sure zeros are removed from poly
eqP("1+0", 0, [ 1 ])
// test zero-elimination from intermediate result of sub
eq(quadPoly(1, 1, 1)-poly(1, 1), "x^2+1")
// test internal cancellation of terms in mul. (x+1)*(x-1)=x^2-1
eq((poly(1, 1)+poly(1, 0))*(poly(1, 1)-poly(1, 0)), "x^2-1")
}
///////////////////////////////////////////////////////////////////////////////////////
//// Small Value Test
///////////////////////////////////////////////////////////////////////////////////////
public func testSmallCoeff() -> Void {
// try to flush out errors when small coefficients are in use.
eq(quadPoly(1, 1, 1)-(poly(999, 1)/(poly(1000, 0))), "x^2+1/1000*x+1")
}
///////////////////////////////////////////////////////////////////////////////////////
//// Multiplication Test
///////////////////////////////////////////////////////////////////////////////////////
// p1 + p2 = p3 , p1 != 0 && p2 != 0, p1.degree == p2.degree
public func testMultiplicationSameDegree() -> Void {
eq(poly(0, 0)*poly(0, 0), "0")
eq(poly(1, 0)*poly(1, 0), "1")
eq(poly(1, 0)*poly(2, 0), "2")
eq(poly(2, 0)*poly(2, 0), "4")
let temp = RatPoly("3*x^4+2")!
XCTAssertEqual(RatPoly("30*x^8+27*x^7+20*x^4+18*x^3"), temp*(RatPolyTest.poly3))
}
// p1 + p2 = p3 , p1 != 0 && p2 != 0, p1.degree != p2.degree
public func testMultiplicationDiffDegree() -> Void {
let temp = RatPoly("3*x^2")!
XCTAssertEqual(RatPoly("18*x^4+21*x^5+24*x^6"), temp*(RatPolyTest.poly2))
XCTAssertEqual(RatPoly("27*x^5+30*x^6"), temp*(RatPolyTest.poly3))
}
// Multiplication Associativity
public func testMultiplicationAssociativity() -> Void {
XCTAssertEqual(RatPolyTest.poly1*(RatPolyTest.poly2)*(RatPolyTest.poly3),
RatPolyTest.poly3*(RatPolyTest.poly2)*(RatPolyTest.poly1))
XCTAssertEqual(RatPolyTest.poly1*(RatPolyTest.neg_poly2)*(RatPolyTest.neg_poly3),
RatPolyTest.neg_poly3*(RatPolyTest.neg_poly2)*(RatPolyTest.poly1))
}
// Multiplication Commutative
public func testMultiplicationCommutativity() -> Void {
XCTAssertEqual(RatPolyTest.poly1*(RatPolyTest.poly2), RatPolyTest.poly2*(RatPolyTest.poly1))
XCTAssertEqual(RatPolyTest.neg_poly3*(RatPolyTest.poly2), RatPolyTest.poly2*(RatPolyTest.neg_poly3))
}
// ZeroPolynomial * ZeroPolynomial == ZeroPolynomial
public func testMultiplicationZeroToZero() -> Void {
XCTAssertEqual(RatPoly.zero, RatPoly.zero*(RatPoly.zero))
}
// p * ZeroPolynomial == ZeroPolynomial && ZeroPolynomial * p == ZeroPolynomial
public func testMultiplicationZeroToNonZero() -> Void {
XCTAssertEqual(RatPoly.zero, RatPoly.zero*(RatPolyTest.poly2))
XCTAssertEqual(RatPoly.zero, RatPolyTest.poly2*(RatPoly.zero))
}
// NaN * NaN == NaN
public func testMultiplicationNaNtoNaN() -> Void {
XCTAssertEqual(RatPoly.nan, RatPoly.nan*(RatPoly.nan))
}
// p * NaN == NaN
public func testMultiplicationNaNtoNonNaN() -> Void {
XCTAssertEqual(RatPoly.nan, RatPoly.nan*(RatPolyTest.poly1))
XCTAssertEqual(RatPoly.nan, RatPolyTest.poly1*(RatPoly.nan))
}
// p * 1 == p
public func testMultiplicationIdentity() -> Void {
XCTAssertEqual(RatPolyTest.poly2, RatPolyTest.poly2*(RatPoly("1")!))
XCTAssertEqual(RatPolyTest.neg_poly3, RatPolyTest.neg_poly3*(RatPoly("1")!))
}
public func testMulMultiplElem() -> Void {
eq((poly(1, 1)-poly(1, 0))*(poly(1, 1)+poly(1, 0)), "x^2-1")
}
///////////////////////////////////////////////////////////////////////////////////////
//// Division Test
///////////////////////////////////////////////////////////////////////////////////////
public func testDivEvaltoSingleCoeff() -> Void {
// 0/x = 0
eq(poly(0, 1)/poly(1, 1), "0")
// 2/1 = 2
eq(poly(2, 0)/poly(1, 0), "2")
// x/x = 1
eq(poly(1, 1)/poly(1, 1), "1")
// -x/x = -1
eq(poly(-1, 1)/poly(1, 1), "-1")
// x/-x = -1
eq(poly(1, 1)/poly(-1, 1), "-1")
// -x/-x = 1
eq(poly(-1, 1)/poly(-1, 1), "1")
// x^100/x^1000 = 0
eq(poly(1, 100)/poly(1, 1000), "0")
}
public func testDivtoSingleTerm() -> Void {
// 5x/5 = x
eq(poly(5, 1)/poly(5, 0), "x")
// -x^2/x = -x
eq(poly(-1, 2)/poly(1, 1), "-x")
// x^100/x = x^99
eq(poly(1, 100)/poly(1, 1), "x^99")
// x^99/x^98 = x
eq(poly(1, 99)/poly(1, 98), "x")
// x^10 / x = x^9 (r: 0)
eq(poly(1, 10)/poly(1, 1), "x^9")
}
public func testDivtoMultipleTerms() -> Void {
// 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)/(poly(1, 3)+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)/(poly(1, 3)+poly(1, 2)+poly(1, 1)),
"x^7-x^6+x^4-x^3+x-1")
// (5x^2+5x)/5 = x^2+x
eq((poly(5, 2)+poly(5, 1))/poly(5, 0), "x^2+x")
// x^10+x^5 / x = x^9+x^4 (r: 0)
eq((poly(1, 10)+poly(1, 5))/poly(1, 1), "x^9+x^4")
// x^10+x^5 / x^3 = x^7+x^2 (r: 0)
eq((poly(1, 10)+(poly(1, 5)))/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
// (with remainder: 29*x^2+14*x-30)
eq((poly(1, 10)+poly(1, 5))/(poly(1, 3)+poly(1, 1)+poly(3, 0)),
"x^7-x^5-3*x^4+x^3+7*x^2+8*x-10")
}
public func testDivComplexI() -> Void {
// (x+1)*(x+1) = x^2+2*x+1
eq((poly(1, 2)+poly(2, 1)+poly(1, 0))/(poly(1, 1)+poly(1, 0)), "x+1")
// (x-1)*(x+1) = x^2-1
eq((poly(1, 2)+poly(-1, 0))/(poly(1, 1)+poly(1, 0)), "x-1")
}
public func testDivComplexII() -> Void {
// 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)
let large = poly(1, 8)+poly(2, 6)+poly(8, 5)+poly(2, 4)+poly(17, 3)+poly(11, 2)+poly(8, 1)+poly(3, 0)
// x^3+2*x+1
let sub1 = poly(1, 3)+poly(2, 1)+poly(1, 0)
// x^5+7*x^2+2*x+3
let sub2 = poly(1, 5)+poly(7, 2)+poly(2, 1)+poly(3, 0)
// just a last minute typo check...
eq(sub1*(sub2), String(describing:large))
eq(sub2*(sub1), String(describing:large))
eq(large/(sub2), "x^3+2*x+1")
eq(large/(sub1), "x^5+7*x^2+2*x+3")
}
public func testDivExamplesFromSpec() -> Void {
// seperated this test case out because it has a dependency on
// both "valueOf" and "div" functioning properly
// example 1 from spec
eq(RatPoly("x^3-2*x+3")!/(RatPoly("3*x^2")!), "1/3*x")
// example 2 from spec
eq(RatPoly("x^2+2*x+15")!/(RatPoly("2*x^3")!), "0")
}
public func testDivExampleFromPset() -> Void {
eq(RatPoly("x^8+x^6+10*x^4+10*x^3+8*x^2+2*x+8")!/(
RatPoly("3*x^6+5*x^4+9*x^2+4*x+8"))!, "1/3*x^2-2/9")
}
public func testBigDiv() -> Void {
// don't "fix" the "infinite loop" in div by simply stopping after
// 50 terms!
eq(
RatPoly("x^102")!/(RatPoly("x+1"))!,
"x^101-x^100+x^99-x^98+x^97-x^96+x^95-x^94+x^93-x^92+x^91-x^90+"
+ "x^89-x^88+x^87-x^86+x^85-x^84+x^83-x^82+x^81-x^80+x^79-x^78+"
+ "x^77-x^76+x^75-x^74+x^73-x^72+x^71-x^70+x^69-x^68+x^67-x^66+"
+ "x^65-x^64+x^63-x^62+x^61-x^60+x^59-x^58+x^57-x^56+x^55-x^54+"
+ "x^53-x^52+x^51-x^50+x^49-x^48+x^47-x^46+x^45-x^44+x^43-x^42+"
+ "x^41-x^40+x^39-x^38+x^37-x^36+x^35-x^34+x^33-x^32+x^31-x^30+"
+ "x^29-x^28+x^27-x^26+x^25-x^24+x^23-x^22+x^21-x^20+x^19-x^18+"
+ "x^17-x^16+x^15-x^14+x^13-x^12+x^11-x^10+x^9-x^8+x^7-x^6+x^5-"
+ "x^4+x^3-x^2+x-1")
}
// p / 0 = NaN
public func testDivByZero() -> Void {
XCTAssertEqual(RatPoly.nan, RatPolyTest.poly2/(RatPoly.zero))
XCTAssertEqual(RatPoly.nan, RatPolyTest.neg_poly1/(RatPoly.zero))
XCTAssertEqual(RatPoly.nan, RatPolyTest.poly1/(RatPoly.zero))
}
// Zero Polynomial / Zero Polynomial == NaN
public func testDivisionZeroFromZero() -> Void {
XCTAssertEqual(RatPoly.nan, RatPoly.zero/(RatPoly.zero))
}
//Following test method depends on correctness of negate
// p / Zero Polynomial == NaN && Zero Polynomial / p == 0
public func testDivisionZeroAndNonZero() -> Void {
XCTAssertEqual(RatPoly.zero, RatPoly.zero/(RatPolyTest.poly1))
}
// NaN / NaN == NaN
public func testDivisionNaNtoNaN() -> Void {
XCTAssertEqual(RatPoly.nan, RatPoly.nan/(RatPoly.nan))
}
// p / NaN == NaN && NaN / p == NaN
public func testDivisionNaNtoNonNaN() -> Void {
XCTAssertEqual(RatPoly.nan, RatPoly.nan/(RatPolyTest.poly1))
XCTAssertEqual(RatPoly.nan, RatPolyTest.poly1/(RatPoly.nan))
}
// p / 1 == p
public func testDivisionByOne() -> Void {
XCTAssertEqual(RatPolyTest.poly2, RatPolyTest.poly2/(RatPoly("1"))!)
}
///////////////////////////////////////////////////////////////////////////////////////
//// Immutable Test
///////////////////////////////////////////////////////////////////////////////////////
public func testImmutabilityOfOperations() -> Void {
// not the most thorough test possible, but hopefully will
// catch the easy cases early on...
let one = poly(1, 0)
let two = poly(2, 0)
let empty = RatPoly()
let _ = one.degree
let _ = two.degree
eq(one, "1", "Degree mutates receiver!")
eq(two, "2", "Degree mutates receiver!")
let _ = one[0]
let _ = two[0]
eq(one, "1", "Coeff mutates receiver!")
eq(two, "2", "Coeff mutates receiver!")
let _ = one.isNaN
let _ = two.isNaN
eq(one, "1", "isNaN mutates receiver!")
eq(two, "2", "isNaN mutates receiver!")
let _ = one.eval(at: 0.0)
let _ = two.eval(at: 0.0)
eq(one, "1", "eval mutates receiver!")
eq(two, "2", "eval mutates receiver!")
let _ = -one
let _ = -two
eq(one, "1", "Negate mutates receiver!")
eq(two, "2", "Negate mutates receiver!")
let _ = one+(two)
eq(one, "1", "Add mutates receiver!")
eq(two, "2", "Add mutates argument!")
let _ = one-(two)
eq(one, "1", "Sub mutates receiver!")
eq(two, "2", "Sub mutates argument!")
let _ = one*(two)
eq(one, "1", "Mul mutates receiver!")
eq(two, "2", "Mul mutates argument!")
let _ = one/(two)
eq(one, "1", "Div mutates receiver!")
eq(two, "2", "Div mutates argument!")
let _ = empty/(RatPoly())
XCTAssertFalse(empty.isNaN, "Div Mutates reciever")
}
///////////////////////////////////////////////////////////////////////////////////////
//// Eval Test
///////////////////////////////////////////////////////////////////////////////////////
public func testEvalZero() -> Void {
let zero = RatPoly()
XCTAssertEqual(0.0, zero.eval(at: 0.0), accuracy: RatPolyTest.accuracy)
XCTAssertEqual(0.0, zero.eval(at: 1.0), accuracy: RatPolyTest.accuracy)
XCTAssertEqual(0.0, zero.eval(at: 2.0), accuracy: RatPolyTest.accuracy)
}
public func testEvalOne() -> Void {
let one = RatPoly(c: 1, e: 0)
XCTAssertEqual(1.0, one.eval(at: 0.0), accuracy: RatPolyTest.accuracy)
XCTAssertEqual(1.0, one.eval(at: 1.0), accuracy: RatPolyTest.accuracy)
XCTAssertEqual(1.0, one.eval(at: 2.0), accuracy: RatPolyTest.accuracy)
}
public func testEvalX() -> Void {
let _X = RatPoly(c: 1, e: 1)
XCTAssertEqual(0.0, _X.eval(at: 0.0), accuracy: RatPolyTest.accuracy)
XCTAssertEqual(1.0, _X.eval(at: 1.0), accuracy: RatPolyTest.accuracy)
XCTAssertEqual(2.0, _X.eval(at: 2.0), accuracy: RatPolyTest.accuracy)
}
public func testEval2X() -> Void {
let _2X = RatPoly(c: 2, e: 1)
XCTAssertEqual(0.0, _2X.eval(at: 0.0), accuracy: RatPolyTest.accuracy)
XCTAssertEqual(2.0, _2X.eval(at: 1.0), accuracy: RatPolyTest.accuracy)
XCTAssertEqual(4.0, _2X.eval(at: 2.0), accuracy: RatPolyTest.accuracy)
}
public func testEvalXsq() -> Void {
let _XSq = RatPoly(c: 1, e: 2)
XCTAssertEqual(0.0, _XSq.eval(at: 0.0), accuracy: RatPolyTest.accuracy)
XCTAssertEqual(1.0, _XSq.eval(at: 1.0), accuracy: RatPolyTest.accuracy)
XCTAssertEqual(4.0, _XSq.eval(at: 2.0), accuracy: RatPolyTest.accuracy)
}
public func testEvalXSq_minus_2X() -> Void {
let _2X = RatPoly(c: 2, e: 1)
let _XSq = RatPoly(c: 1, e: 2)
let _XSq_minus_2X = _XSq-(_2X)
XCTAssertEqual(0.0, _XSq_minus_2X.eval(at: 0.0), accuracy: RatPolyTest.accuracy)
XCTAssertEqual(-1.0, _XSq_minus_2X.eval(at: 1.0), accuracy: RatPolyTest.accuracy)
XCTAssertEqual(0.0, _XSq_minus_2X.eval(at: 2.0), accuracy: RatPolyTest.accuracy)
XCTAssertEqual(3.0, _XSq_minus_2X.eval(at: 3.0), accuracy: RatPolyTest.accuracy)
}
///////////////////////////////////////////////////////////////////////////////////////
//// Get Term Test
///////////////////////////////////////////////////////////////////////////////////////
public func testGetTerm() -> Void {
// getTerm already gets some grunt testing in eqP checking an
// interesting
// input here...
let _XSqPlus2X = poly(1, 2)+poly(1, 1)+poly(1, 1)
let _2XSqPlusX = poly(1, 2)+poly(1, 2)+poly(1, 1)
XCTAssertEqual(RatTerm.zero, _XSqPlus2X[-1])
XCTAssertEqual(RatTerm.zero, _XSqPlus2X[-10])
XCTAssertEqual(RatTerm.zero, _2XSqPlusX[-1])
XCTAssertEqual(RatTerm.zero, _2XSqPlusX[-10])
XCTAssertEqual(RatTerm.zero, zero()[-10])
XCTAssertEqual(RatTerm.zero, zero()[-1])
}
private func assertIsNaNanswer(_ nanAnswer : RatPoly, file: StaticString = #file, line: UInt = #line) {
eq(nanAnswer, "NaN", file: file, line: line)
}
///////////////////////////////////////////////////////////////////////////////////////
//// Differentiate Test
///////////////////////////////////////////////////////////////////////////////////////
// (NaN)' = NaN
public func testDifferentiateNaN(){
XCTAssertEqual(RatPoly.nan, RatPoly.nan.differentiate())
}
// (RatPoly.zero)' = RatPoly.zero
public func testDifferentiateZero(){
XCTAssertEqual(RatPoly.zero, RatPoly.zero.differentiate())
}
// constant a => (a)' = 0
public func testDifferentiateConstantNonZero(){
XCTAssertEqual(RatPoly.zero, RatPoly("1")!.differentiate())
XCTAssertEqual(RatPoly.zero, RatPoly("999")!.differentiate())
}
//f(x) = x => f' = 1
public func testDifferentiatetoOne() -> Void {
eq((RatPoly("x")!.differentiate()), "1")
}
// Constant Multiple Rule (af)' = af'
public func testDifferentiateMultiplicationRule(){
let a_constant = RatPoly("2")
XCTAssertEqual(a_constant!*(RatPolyTest.poly1.differentiate()),
(a_constant!*(RatPolyTest.poly1)).differentiate())
XCTAssertEqual(a_constant!*(RatPolyTest.neg_poly2.differentiate()),
(a_constant!*(RatPolyTest.neg_poly2)).differentiate())
}
// Polynomial Power Rule (ax^b) = (a*b)*x^(b-1)
public func testDifferentiatePowerRule(){
XCTAssertEqual(RatPoly("1+4*x+9*x^2+16*x^3+25*x^4"), RatPolyTest.poly1.differentiate())
XCTAssertEqual(RatPoly("12*x+21*x^2+32*x^3"), RatPolyTest.poly2.differentiate())
}
// Sum rule (f + g)' = f' + g'
public func testDifferentiateSumRule(){
XCTAssertEqual(((RatPolyTest.poly2)+(RatPolyTest.neg_poly3)).differentiate(),
(RatPolyTest.poly2.differentiate())+(RatPolyTest.neg_poly3.differentiate()))
XCTAssertEqual(((RatPolyTest.poly1)+(RatPolyTest.poly3)).differentiate(),
(RatPolyTest.poly1.differentiate())+(RatPolyTest.poly3.differentiate()))
}
// Subtraction rule (f - g)' = f' - g'
public func testDifferentiateSubtractionRule(){
XCTAssertEqual(((RatPolyTest.poly2)-(RatPolyTest.neg_poly3)).differentiate(),
(RatPolyTest.poly2.differentiate())-(RatPolyTest.neg_poly3.differentiate()))
XCTAssertEqual(((RatPolyTest.poly1)-(RatPolyTest.poly3)).differentiate(),
(RatPolyTest.poly1.differentiate())-(RatPolyTest.poly3.differentiate()))
}
// Product Rule h(x) = f(x)*g(x) => h'(x) = f'(x)g(x) + f(x)g'(x)
public func testDifferentiateProductRule(){
// Whole Number Coefficient
let init_product = RatPolyTest.poly1*(RatPolyTest.poly2)
let deriv_pt1 = (RatPolyTest.poly1.differentiate())*(RatPolyTest.poly2)
let deriv_pt2 = RatPolyTest.poly1*(RatPolyTest.poly2.differentiate())
XCTAssertEqual(init_product.differentiate() , deriv_pt1+(deriv_pt2))
// Fractional Number Coefficient
let init_product_b = RatPolyTest.neg_poly2*(RatPolyTest.poly3)
let deriv_pt1_b = (RatPolyTest.neg_poly2.differentiate())*(RatPolyTest.poly3)
let deriv_pt2_b = RatPolyTest.neg_poly2*(RatPolyTest.poly3.differentiate())
XCTAssertEqual(init_product_b.differentiate() , deriv_pt1_b+(deriv_pt2_b))
}
public func testDifferentiatetoMultipleTerms() -> Void {
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")
}
///////////////////////////////////////////////////////////////////////////////////////
//// Anti Differentiate Test
///////////////////////////////////////////////////////////////////////////////////////
//As stated in specification for any term b is assumed >= 0 and Integration Constant is Zero
//Note : AntiDerivative of f(x) = F(x) + c , f = F
//Note : c = Integration Constant
//AntiDifferentiate Basic functionality
public func testAntiDifferentiate() -> Void {
eq(poly(1, 0).antiDifferentiate(withConstant: RatNum(1)), "x+1")
eq(poly(2, 1).antiDifferentiate(withConstant: RatNum(1)), "x^2+1")
}
public func testAntiDifferentiateWithQuadPoly() -> Void {
eq(quadPoly(0, 6, 2).antiDifferentiate(withConstant: RatNum(1)), "3*x^2+2*x+1")
eq(quadPoly(4, 6, 2).antiDifferentiate(withConstant: RatNum(0)),
"4/3*x^3+3*x^2+2*x")
}
// Constant Rule with zero f(x) = 0 => F = c
public func testAntiDifferentiateFromZero() -> Void {
// Zero
XCTAssertEqual(RatPoly.zero, RatPoly.zero.antiDifferentiate(withConstant: RatNum.zero))
// Zero with integration constant 5
XCTAssertEqual(RatPoly("5"), RatPoly.zero.antiDifferentiate(withConstant: RatNum("5")!))
}
// Constant Rule f(x) = c => F = c*x
public func testAntiDifferentiateConstantRule() -> Void {
// Zero Integration Constant
XCTAssertEqual(RatPoly("5*x"), RatPoly("5")!.antiDifferentiate(withConstant: RatNum.zero))
// Non Zero Integration Constant
XCTAssertEqual(RatPoly("5*x+10"), RatPoly("5")!.antiDifferentiate(withConstant: RatNum("10")!))
}
// Constant Multiple Rule f(x) = c*g(x) => F = c*G(x)
public func testAntiDifferentiateConstantMultipleRule() -> Void {
let a_constant = RatPoly("7")
let b_constant = RatPoly("13")
let i_constant = RatNum(11)
XCTAssertEqual(((a_constant)!*(RatPolyTest.poly1)).antiDifferentiate(withConstant: i_constant),
a_constant!*(RatPolyTest.poly1.antiDifferentiate(withConstant: RatNum.zero))+(RatPoly(term: RatTerm(coeff: i_constant , exponent: 0))))
XCTAssertEqual(((b_constant)!*(RatPolyTest.poly3)).antiDifferentiate(withConstant: RatNum.zero),
b_constant!*(RatPolyTest.poly3.antiDifferentiate(withConstant: RatNum.zero)))
}
// Power Rule f(x) = x^a => F = (x^(a+1))/(a+1)
public func testAntiDifferentiatePowerRule() -> Void {
XCTAssertEqual(RatPoly("9/4*x^4+2*x^5"), RatPolyTest.poly3.antiDifferentiate(withConstant: RatNum.zero))
XCTAssertEqual(RatPoly("2*x^3+7/4*x^4+8/5*x^5+1"), RatPolyTest.poly2.antiDifferentiate(withConstant: RatNum(1)))
}
// Sum Rule if h(x) = f(x) + g(x) => H(x) = F(x) + G(x)
public func testAntiDifferentiateSumRule() -> Void {
XCTAssertEqual((RatPolyTest.poly1+(RatPolyTest.poly2)).antiDifferentiate(withConstant: RatNum.zero),
RatPolyTest.poly1.antiDifferentiate(withConstant: RatNum.zero)+(RatPolyTest.poly2.antiDifferentiate(withConstant: RatNum.zero)))
XCTAssertEqual((RatPolyTest.neg_poly3+(RatPolyTest.neg_poly1)).antiDifferentiate(withConstant: RatNum(3)),
RatPolyTest.neg_poly3.antiDifferentiate(withConstant: RatNum.zero)+(RatPolyTest.neg_poly1.antiDifferentiate(withConstant: RatNum.zero))+(RatPoly(term: RatTerm(coeff: RatNum(3) , exponent: 0))))
}
// Difference Rule if h(x) = f(x) - g(x) => H(x) = F(x) - G(x)
public func testAntiDifferentiateDifferenceRule() -> Void {
XCTAssertEqual((RatPolyTest.poly1-(RatPolyTest.poly2)).antiDifferentiate(withConstant: RatNum.zero),
RatPolyTest.poly1.antiDifferentiate(withConstant: RatNum.zero)-(RatPolyTest.poly2.antiDifferentiate(withConstant: RatNum.zero)))
XCTAssertEqual((RatPolyTest.neg_poly3-(RatPolyTest.neg_poly1)).antiDifferentiate(withConstant: RatNum(3)),
RatPolyTest.neg_poly3.antiDifferentiate(withConstant: RatNum.zero)-(RatPolyTest.neg_poly1.antiDifferentiate(withConstant: RatNum.zero))+(RatPoly(term: RatTerm(coeff: RatNum(3) , exponent: 0))))
}
public func testAntiDifferentiateWithNaN() -> Void {
assertIsNaNanswer(RatPoly("NaN")!.antiDifferentiate(withConstant: RatNum(1)))
assertIsNaNanswer(poly(1, 0).antiDifferentiate(withConstant: RatNum(1, 0)))
}
///////////////////////////////////////////////////////////////////////////////////////
//// Integrate Test
///////////////////////////////////////////////////////////////////////////////////////
public func testIntegrateEqualBounds() -> Void {
XCTAssertEqual( 0.0 , RatPolyTest.poly3.integrate(from: 1, to: 1), accuracy: RatPolyTest.accuracy)
XCTAssertEqual( 0.0 , RatPolyTest.poly1.integrate(from: 0,to: 0), accuracy: RatPolyTest.accuracy)
}
public func testIntegrateBoundsDiffBy1() -> Void {
XCTAssertEqual( 17.0 / 4.0 , RatPolyTest.poly3.integrate(from: 0, to: 1), accuracy: RatPolyTest.accuracy)
XCTAssertEqual( 71.0 / 20.0 , RatPolyTest.poly1.integrate(from: 0, to: 1), accuracy: RatPolyTest.accuracy)
}
public func testIntegrateLowBoundGreaterThanHigh() -> Void {
XCTAssertEqual( -19375.0 / 4.0 , RatPolyTest.poly3.integrate(from: 0, to: -5), accuracy: RatPolyTest.accuracy)
XCTAssertEqual( -5683.0 / 60.0 , RatPolyTest.poly1.integrate(from: 2, to: 1), accuracy: RatPolyTest.accuracy)
}
public func testIntegrateLargeBoundDiff() -> Void {
XCTAssertEqual( 20225000000.0, RatPolyTest.poly3.integrate(from: 0, to: 100), accuracy: RatPolyTest.accuracy)
XCTAssertEqual( 841409005000.0 , RatPolyTest.poly1.integrate(from: 0, to: 100), accuracy: RatPolyTest.accuracy)
}
public func testIntegrateZero() -> Void {
XCTAssertEqual(0.0, RatPoly.zero.integrate(from: 0, to: 10), accuracy: RatPolyTest.accuracy)
}
public func testIntegrateOne() -> Void {
XCTAssertEqual(10.0, RatPoly("1")!.integrate(from: 0, to: 10), accuracy: RatPolyTest.accuracy)
}
public func testIntegrateNaN() -> Void {
XCTAssertTrue(RatPoly("NaN")!.integrate(from: 0, to: 1).isNaN)
}
}