//
//  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)
  }
}