//
//  RatTermTest.swift
//  RatNumTests
//

import XCTest
@testable import RatNum

class RatTermTest: XCTestCase {
  
  // accuracy of double comparisons
  private let accuracy = 0.000001
  
  private static let nanNum = RatNum(1) / RatNum(0)
  private static let nanTerm = RatTerm(coeff: nanNum, exponent: 3)
  private static let zeroTerm = RatTerm.zero
  private static let oneTerm = RatTerm(coeff: RatNum(1),exponent: 0)
  
  // Convenient way to make a RatTerm equals to coeff*x^expt.
  private func term(_ coeff: Int, _ expt : Int) -> RatTerm {
    return RatTerm(coeff: RatNum(coeff), exponent: expt)
  }
  
  // Convenient way to make a RatTerm equals to num/denom*x^expt.
  private func term(_ numer : Int, _ denom : Int, _ expt: Int) -> RatTerm {
    return RatTerm(coeff: RatNum(numer, denom), exponent: expt)
  }
  
  
  // Is it good style to include these constructor tests? Explicit constructor
  // tests are often not needed. Generally we advise you to have at
  // least one assertion in every test unless testing that an exception IS
  // thrown (via the "expected" test parameter). The constructor tests below
  // violate this rule, but we chose to include them in this assignment in
  // case they help you isolate constructor errors as you write your code.
  public func testInitializer() -> Void {
    XCTAssertEqual(RatTerm("1"), term(1, 0))
    XCTAssertEqual(RatTerm("2*x^3"), term(2, 3))
    XCTAssertEqual(RatTerm("4/3*x^6"), term(4, 3, 6))
    XCTAssertEqual(RatTerm("-2/7*x^3"), term(-2, 7, 3))
  }
  
  public func testInitializerZeroCoeff() -> Void {
    XCTAssertEqual(RatTerm.zero, term(0, 0))
    XCTAssertEqual(RatTerm.zero, term(0, 1))
  }
  
  public func testInitializerNaN() -> Void {
    XCTAssertEqual(RatTerm.nan, term(3, 0, 0))
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  Get Coefficient
  ///////////////////////////////////////////////////////////////////////////////////////
  
  public func testCoeffRegular() -> Void {
    // Simple cases
    XCTAssertEqual(RatNum(3), term(3, 1).coeff)
    XCTAssertEqual(RatNum(2, 5), term(2, 5, 2).coeff)
  }
  
  public func testCoeffZero() -> Void {
    // Check zero
    XCTAssertEqual(RatNum(0), term(0, 0).coeff)
  }
  
  public func testCoeffNegative() -> Void {
    // Check negative coeff
    XCTAssertEqual(RatNum(-2, 3), term(-2, 3, 2).coeff)
  }
  
  public func testCoeffNaN() -> Void {
    // Check NaN
    XCTAssertEqual(RatTerm.nan.coeff, term(3, 0, 4).coeff)
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  Get Coefficient
  ///////////////////////////////////////////////////////////////////////////////////////
  
  public func testExptRegular() -> Void {
    // Simple
    XCTAssertEqual(4, term(2, 4).exponent)
  }
  
  public func testExptZero() -> Void {
    // Zero always have zero expt
    XCTAssertEqual(0, term(0, 0).exponent)
    XCTAssertEqual(0, term(0, 1).exponent)
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  isNaN Test
  ///////////////////////////////////////////////////////////////////////////////////////
  
  public func testIsNaNZeroDenomRegNumer() -> Void {
    // Check that 5/0*x^0 isNaN instead of zero
    XCTAssertTrue(term(5, 0, 0).isNaN)
  }
  
  public func testIsNaNZeroDenomZeroNumer() -> Void {
    // Check that 0/0*x^4 isNaN instead of zero
    XCTAssertTrue(term(0, 0, 4).isNaN)
  }
  
  public func testIsNaNFalse() -> Void {
    // Check for false positives
    XCTAssertFalse(term(2, 3, 2).isNaN)
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  isZero Test
  ///////////////////////////////////////////////////////////////////////////////////////
  
  public func testIsZeroNumAndDen() -> Void {
    XCTAssertTrue(term(0, 0).isZero)
  }
  
  public func testIsZeroRatNum() -> Void {
    XCTAssertTrue(term(0, 1).isZero)
    XCTAssertTrue(term(0, 4, 3).isZero)
    XCTAssertTrue(term(0, -2, 2).isZero)
  }
  
  public func testIsZeroFalsoPos() -> Void {
    // Check for false positives
    XCTAssertFalse(term(1, 3, 0).isZero)
  }
  
  public func testIsNaNNotTerm() -> Void {
    // Check that 0/0*x^4 is not zero
    XCTAssertFalse(term(0, 0, 4).isZero)
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  eval Test
  ///////////////////////////////////////////////////////////////////////////////////////
  
  public func testEvalZero() -> Void {
    XCTAssertEqual(0.0, term(0, 0).eval( 5.0), accuracy: accuracy)
    XCTAssertEqual(0.0, term(0, 5).eval( 1.2), accuracy: accuracy)
  }
  
  public func testEvalSmallFrac() -> Void {
    XCTAssertEqual(0.125, term(1, 2, 2).eval( 0.5), accuracy: accuracy)
  }
  
  public func testEvalSmallWhole() -> Void {
    XCTAssertEqual(2.0, term(2, 0).eval( 3.1) , accuracy: accuracy)
    XCTAssertEqual(1.0, term(1, 0).eval( 100.0) , accuracy: accuracy)
    XCTAssertEqual(1.0, term(-1, 1).eval( -1.0) , accuracy: accuracy)
    XCTAssertEqual(2.0, term(1, 2, 2).eval( 2.0) , accuracy: accuracy)
  }
  
  public func testEvalLarge() -> Void {
    XCTAssertEqual(35.0, term(5, 1).eval( 7.0) , accuracy: accuracy)
    XCTAssertEqual(12.0, term(3, 2).eval( 2.0) , accuracy: accuracy)
  }
  
  public func testEvalNegative() -> Void {
    XCTAssertEqual(-16.0, term(-2, 3).eval( 2.0) , accuracy: accuracy)
    XCTAssertEqual(-3.0, term(3, 3).eval( -1.0) , accuracy: accuracy)
  }
  
  public func testEvalNaN() -> Void {
    XCTAssertTrue(term(3, 0, 2).eval( 1.0).isNaN)
  }
  
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  Equals Test
  ///////////////////////////////////////////////////////////////////////////////////////
  
  public func testEquals() -> Void {
    XCTAssertEqual(term(3, 5), term(3, 5))
    XCTAssertEqual(term(1, 2, 4), term(1, 2, 4))
    XCTAssertEqual(term(-2, 4, 2), term(1, -2, 2))
  }
  public func testNotEqualsReg() -> Void {
    XCTAssertNotEqual(term(4, 6), term(7, 8))
  }
  
  public func testEqualsZeroCoeff() -> Void {
    XCTAssertEqual(term(0, 0), term(0, 0))
    XCTAssertEqual(term(0, 1), term(0, 0))
  }
  
  public func testEqualsNotZeroCoeff() -> Void {
    XCTAssertNotEqual(term(0, 0), term(3, 5))
  }
  
  public func testEqualsNaNCoeff() -> Void {
    XCTAssertEqual(RatTermTest.nanTerm, term(19, 0, 0))
    XCTAssertEqual(RatTermTest.nanTerm, term(0, 0, 0))
  }
  
  public func testEqualsNotNaNCoeff() -> Void {
    XCTAssertNotEqual(RatTermTest.nanTerm, term(3, 5))
    XCTAssertNotEqual(term(0, 3), RatTermTest.nanTerm)
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  ValueOf Test
  ///////////////////////////////////////////////////////////////////////////////////////
  
  // All tests below depend on constructor and equals.
  
  // Note: the extra file/line parameters are to enable XCode to report assertion failures
  // at the *call site* of testValueOf.
  private func testValueOf(_ actual: String, _ target: RatTerm, file: StaticString = #file, line: UInt = #line) -> Void {
    XCTAssertEqual(target, RatTerm(actual), file: file, line: line)
  }
  
  public func testValueOfSimple() -> Void {
    testValueOf("x", term(1, 1))
    testValueOf("-x", term(-1, 1))
  }
  
  public func testValueOfConst() -> Void {
    testValueOf("2", term(2, 0))
    testValueOf("3/4", term(3, 4, 0))
    testValueOf("-4", term(-4, 0))
    testValueOf("-7/5", term(-7, 5, 0))
  }
  
  public func testValueOfLeadingCoeff() -> Void {
    testValueOf("2*x", term(2, 1))
    testValueOf("3/7*x", term(3, 7, 1))
    testValueOf("-4/3*x", term(-4, 3, 1))
  }
  
  public func testValueOfPow() -> Void {
    testValueOf("x^3", term(1, 3))
    testValueOf("-x^4", term(-1, 4))
  }
  
  public func testValueOfFull() -> Void {
    testValueOf("4*x^2", term(4, 2))
    testValueOf("2/5*x^6", term(2, 5, 6))
    testValueOf("-3/2*x^2", term(-3, 2, 2))
  }
  
  public func testValueOfNaN() -> Void {
    testValueOf("NaN", term(1, 0, 0))
  }
  
  public func testValueOfZero() -> Void {
    testValueOf("0", term(0, 0))
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  toString Test
  ///////////////////////////////////////////////////////////////////////////////////////
  
  private func testToString(_ term: String, _ actual: RatTerm, file: StaticString = #file, line: UInt = #line) {
    XCTAssertEqual(term, String(describing: actual), file: file, line: line)
  }
  
  public func testToStringSimple() -> Void {
    testToString("x", term(1, 1))
    testToString("-x", term(-1, 1))
  }
  
  public func testToStringConst() -> Void {
    testToString("2", term(2, 0))
    testToString("3/4", term(3, 4, 0))
    testToString("-4", term(-4, 0))
    testToString("-7/5", term(-7, 5, 0))
  }
  
  public func testToStringLeadingCoeff() -> Void {
    testToString("2*x", term(2, 1))
    testToString("3/7*x", term(3, 7, 1))
    testToString("-4/3*x", term(-4, 3, 1))
  }
  
  public func testToStringPow() -> Void {
    testToString("x^3", term(1, 3))
    testToString("-x^4", term(-1, 4))
  }
  
  public func testToStringFull() -> Void {
    testToString("4*x^2", term(4, 2))
    testToString("2/5*x^6", term(2, 5, 6))
    testToString("-3/2*x^2", term(-3, 2, 2))
  }
  
  public func testToStringNaN() -> Void {
    testToString("NaN", term(1, 0, 0))
  }
  
  public func testToStringZero() -> Void {
    testToString("0", term(0, 0))
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  Add Test
  ///////////////////////////////////////////////////////////////////////////////////////
  //Relies on RatTerm equals method and RatNum add method is correct
  
  // Addition with Whole Number Coefficient
  public func testAddWholeNumCoeff() -> Void {
    XCTAssertEqual(term(3, 0), term(1, 0)+(term(2, 0))) //Zero Exponent Value
    XCTAssertEqual(term(4, 2), term(3, 2)+(term(1, 2))) //Non Zero Exponent
  }
  
  // Addition with Fractional Number Coefficient
  public func testAddFracNumCoeff() -> Void {
    XCTAssertEqual(term(1, 2, 3), term(1, 6, 3)+(term(1, 3, 3)))
    XCTAssertEqual(term(1, 8, 1), term(1, 4, 1)+(term(-1, 8, 1)))
    XCTAssertEqual(term(-1, 8, 1), term(-1, 4, 1)+(term(1, 8, 1)))
  }
  
  // Addition Associativity
  public func testAddAssociativity() -> Void {
    //Whole Number Coefficient
    XCTAssertEqual(term(6, 0), (term(1, 0)+(term(2, 0)))!+(term(3,0)))
    XCTAssertEqual(term(6, 0), (term(3, 0)+(term(2, 0)))!+(term(1,0)))
    
    //Fractional Number Coefficient
    XCTAssertEqual(term(7, 8, 3), (term(1, 8, 3)+(term(1, 4, 3)))!+(term(1, 2, 3)))
    XCTAssertEqual(term(7, 8, 3), (term(1, 2, 3)+(term(1, 4, 3)))!+(term(1, 8, 3)))
  }
  
  // Addition Commutative
  public func testAddCommutativity() -> Void {
    XCTAssertEqual(term(1, 2, 3), term(1, 3, 3)+(term(1, 6, 3)))
    XCTAssertEqual(term(3, 0), term(2, 0)+(term(1, 0)))
  }
  
  // Zero Term + Zero Term == Zero Term
  public func testAddZeroToZero() -> Void {
    XCTAssertEqual(RatTermTest.zeroTerm, RatTermTest.zeroTerm+(RatTermTest.zeroTerm))
  }
  
  // t + Zero Term == t && Zero Term + t == t
  public func testAddZeroToNonZero() -> Void {
    let t = term(-2, 3)
    XCTAssertEqual(t, RatTermTest.zeroTerm+(t))
    XCTAssertEqual(t, t+(RatTermTest.zeroTerm))
  }
  
  // NaN + NaN == NaN
  public func testAddNaNtoNaN() -> Void {
    XCTAssertEqual(RatTermTest.nanTerm, RatTermTest.nanTerm+(RatTermTest.nanTerm))
  }
  
  // t + NaN == NaN
  public func testAddNaNtoNonNaN() -> Void {
    XCTAssertEqual(RatTermTest.nanTerm, RatTermTest.nanTerm+(term(3, 4)))
    XCTAssertEqual(RatTermTest.nanTerm, term(3, 4)+(RatTermTest.nanTerm))
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  Subtract Test
  ///////////////////////////////////////////////////////////////////////////////////////
  
  //Also Tests Addition inverse property
  
  // Subtraction with Whole Number Coefficient
  public func testSubtractWholeNumCoeff() -> Void {
    XCTAssertEqual(term(1, 0), term(2, 0)-(term(1, 0)))
    XCTAssertEqual(term(-1, 0), term(1, 0)-(term(2, 0)))
    XCTAssertEqual(term(2, 2), term(3, 2)-(term(1, 2)))
  }
  
  // Subtraction with Fractional Number Coefficient
  public func testSubtractFractionalNumCoeff() -> Void {
    XCTAssertEqual(term(-1, 6, 3), term(1, 6, 3)-(term(1, 3, 3)))
    XCTAssertEqual(term(3, 8, 1), term(1, 4, 1)-(term(-1, 8, 1)))
    XCTAssertEqual(term(-3, 8, 1), term(-1, 4, 1)-(term(1, 8, 1)))
  }
  
  // Zero Term - Zero Term == Zero Term
  public func testSubtractZeroFromZero() -> Void {
    XCTAssertEqual(RatTermTest.zeroTerm, RatTermTest.zeroTerm-(RatTermTest.zeroTerm))
  }
  
  //Following test method depends on correctness of negate
  // t - Zero Term == t && Zero Term - t == -t
  public func testSubtractZeroAndNonZero() -> Void {
    let t = term(-2, 3)
    XCTAssertEqual(-t, RatTermTest.zeroTerm-(t))
    XCTAssertEqual(t, t-(RatTermTest.zeroTerm))
  }
  
  // NaN - NaN == NaN
  public func testSubtractNaNtoNaN() -> Void {
    XCTAssertEqual(RatTermTest.nanTerm, RatTermTest.nanTerm-(RatTermTest.nanTerm))
  }
  
  // t - NaN == NaN && NaN - t == NaN
  public func testSubtractNaNtoNonNaN() -> Void {
    XCTAssertEqual(RatTermTest.nanTerm, RatTermTest.nanTerm-(term(3, 4)))
    XCTAssertEqual(RatTermTest.nanTerm, term(3, 4)-(RatTermTest.nanTerm))
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  Multiplication Test
  ///////////////////////////////////////////////////////////////////////////////////////
  
  // Multiplication with Whole Number Coefficient
  public func testMultiplicationWholeNumCoeff() -> Void {
    XCTAssertEqual(term(2, 0), term(1, 0)*(term(2, 0))) //Zero Exponent Value
    XCTAssertEqual(term(30, 4), term(3, 2)*(term(10, 2))) //Non Zero Exponent
  }
  
  // Multiplication with Fractional Number Coefficient
  public func testMultiplicationFracNumCoeff() -> Void {
    XCTAssertEqual(term(1, 18, 6), term(1, 6, 3)*(term(1, 3, 3)))
    XCTAssertEqual(term(-1, 32, 2), term(1, 4, 1)*(term(-1, 8, 1)))
  }
  
  // Multiplication with different Exponent Values
  public func testMultiplicationDifferentExpVal() -> Void {
    XCTAssertEqual(term(3*2, 6*3, 9+3), term(3, 6, 9)*(term(2, 3, 3)))
    XCTAssertEqual(term(1 * -1, 8*4, -1+1), term(1, 4, -1)*(term(-1, 8, 1)))
    XCTAssertEqual(term(-1*1, 4*8, 100+10), term(-1, 4, 100)*(term(1, 8, 10)))
  }
  
  // Multiplication Associativity
  public func testMultiplicationAssociativity() -> Void {
    //Whole Number Coefficient
    XCTAssertEqual((term(12,5)*(term(11, 4)))*(term(10, 3)),
                   (term(10, 3)*(term(11, 4)))*(term(12,5)))
    
    //Fractional Number Coefficient
    XCTAssertEqual((term(4, 9, 3)*(term(2, 9, 2)))*(term(1, 3, 1)),
                   (term(1, 3, 1)*(term(2, 9, 2)))*(term(4, 9, 3)))
  }
  
  // Multiplication Commutative
  public func testMultiplicationCommutativity() -> Void {
    //Whole Number Coefficient
    XCTAssertEqual(term(7, 1)*(term(3, 9)), term(3, 9)*(term(7, 1)))
    
    //Fractional Number Coefficient
    XCTAssertEqual(term(1, 6, 3)*(term(1, 3, 3)), term(1, 3, 3)*(term(1, 6, 3)))
  }
  
  // Zero Term * Zero Term == Zero Term
  public func testMultiplicationZeroToZero() -> Void {
    XCTAssertEqual(RatTermTest.zeroTerm, RatTermTest.zeroTerm*(RatTermTest.zeroTerm))
  }
  
  // t * Zero Term == Zero Term && Zero Term * t == Zero Term
  public func testMultiplicationZeroToNonZero() -> Void {
    let t = term(-2, 3)
    XCTAssertEqual(RatTermTest.zeroTerm, RatTermTest.zeroTerm*(t))
    XCTAssertEqual(RatTermTest.zeroTerm, t*(RatTermTest.zeroTerm))
  }
  
  // NaN * NaN == NaN
  public func testMultiplicationNaNtoNaN() -> Void {
    XCTAssertEqual(RatTermTest.nanTerm, RatTermTest.nanTerm*(RatTermTest.nanTerm))
  }
  
  // t * NaN == NaN
  public func testMultiplicationNaNtoNonNaN() -> Void {
    XCTAssertEqual(RatTermTest.nanTerm, RatTermTest.nanTerm*(term(3, 4)))
    XCTAssertEqual(RatTermTest.nanTerm, term(3, 4)*(RatTermTest.nanTerm))
  }
  
  // a * 1 == a
  public func testMultiplicationIdentity() -> Void {
    XCTAssertEqual(term(4, 3), term(4, 3)*RatTerm(coeff: RatNum.one, exponent: 0))
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  Division Test
  ///////////////////////////////////////////////////////////////////////////////////////
  
  // Division with Whole Number Coefficient
  public func testDivisionWholeNumCoeff() -> Void {
    XCTAssertEqual(term(3, 0), term(3, 2)/(term(1, 2)))
    XCTAssertEqual(term(2, 1), term(2, 1)/(term(1, 0)))
    XCTAssertEqual(term(8, 3), term(-16, 5)/(term(-2, 2)))
  }
  
  // Division with Fractional Number Coefficient
  public func testDivisionFractionalNumCoeff() -> Void {
    XCTAssertEqual(term(1, 2, 0), term(1, 0)/(term(2, 0)))
    XCTAssertEqual(term(1, 2, 0), term(1, 6, 3)/(term(1, 3, 3)))
    XCTAssertEqual(term(-2, 0), term(1, 4, 1)/(term(-1, 8, 1)))
  }
  
  // Zero Term / Zero Term == NaN
  public func testDivisionZeroFromZero() -> Void {
    XCTAssertEqual(RatTermTest.nanTerm, RatTermTest.zeroTerm/(RatTermTest.zeroTerm))
  }
  
  //Following test method depends on correctness of negate
  // t / Zero Term == NaN && Zero Term / t == 0
  public func testDivisionZeroAndNonZero() -> Void {
    let t = term(-2, 3)
    XCTAssertEqual(RatTermTest.zeroTerm, RatTermTest.zeroTerm/(t))
    XCTAssertEqual(RatTermTest.nanTerm, t/(RatTermTest.zeroTerm))
  }
  
  // NaN / NaN == NaN
  public func testDivisionNaNtoNaN() -> Void {
    XCTAssertEqual(RatTermTest.nanTerm, RatTermTest.nanTerm/(RatTermTest.nanTerm))
  }
  
  // t / NaN == NaN && NaN / t == NaN
  public func testDivisionNaNtoNonNaN() -> Void {
    XCTAssertEqual(RatTermTest.nanTerm, RatTermTest.nanTerm/(term(3, 4)))
    XCTAssertEqual(RatTermTest.nanTerm, term(3, 4)/(RatTermTest.nanTerm))
  }
  
  // a / 1 == a
  public func testDivisionByOne() -> Void {
    XCTAssertEqual(term(4, 3), term(4, 3)/(RatTerm(coeff: RatNum.one, exponent: 0)))
  }
  
  
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  Differentiation Test
  ///////////////////////////////////////////////////////////////////////////////////////
  //As stated in specification for any term b is assumed >= 0
  //Note : Derivative of f = f'
  
  // (NaN)' = NaN
  public func testDifferentiateNaN(){
    XCTAssertEqual(RatTermTest.nanTerm, RatTermTest.nanTerm.differentiate())
    XCTAssertEqual(RatTermTest.nanTerm, (RatTerm(coeff: RatNum.nan, exponent: 0)).differentiate())
  }
  
  // (RatTerm.ZERO)' = RatTerm.ZERO
  public func testDifferentiateZero(){
    XCTAssertEqual(term(0, 0), term(0, 0).differentiate())
  }
  
  // constant a => (a)' = 0
  public func testDifferentiateConstantNonZero(){
    XCTAssertEqual(term(0, 0), term(99, 0).differentiate())
    XCTAssertEqual(term(0, 0), term(1, 0).differentiate())
  }
  
  // Constant Multiple Rule (af)' = af'
  public func testDifferentiateMultiplicationRule(){
    // Whole Number Coefficient
    XCTAssertEqual(term(5, 0)*(term(1,3).differentiate()),
                   (term(5, 0)*(term(1,3))).differentiate())
    
    // Fractional Number Coefficient
    XCTAssertEqual(term(2,3,0)*(term(1,4,3).differentiate()),
                   (term(2,3,0)*(term(1,4,3))).differentiate())
  }
  
  // Polynomial Power Rule (ax^b) = (a*b)*x^(b-1)
  public func testDifferentiatePowerRule(){
    XCTAssertEqual(term(1, 0), term(1, 1).differentiate())
    XCTAssertEqual(term(5, 0), term(5, 1).differentiate())
    XCTAssertEqual(term(14, 1), term(7, 2).differentiate())
    XCTAssertEqual(term(-2, 3), term(-1, 2, 4).differentiate())
  }
  
  // Sum rule (f + g)' = f' + g'
  public func testDifferentiateSumRule(){
    // Whole Number Coefficient
    XCTAssertEqual(((term(5, 2))+(term(1,2)))!.differentiate(),
                   (term(5, 2).differentiate())+(term(1,2).differentiate()))
    
    // Fractional Number Coefficient
    XCTAssertEqual(((term(5, 2, 7))+(term(1,3, 7)))!.differentiate(),
                   (term(5, 2, 7).differentiate())+(term(1,3, 7).differentiate()))
  }
  
  // Subtraction rule (f - g)' = f' - g'
  public func testDifferentiateSubtractionRule(){
    // Whole Number Coefficient
    XCTAssertEqual(((term(5, 2))-(term(1,2)))!.differentiate(),
                   (term(5, 2).differentiate())-(term(1,2).differentiate()))
    
    // Fractional Number Coefficient
    XCTAssertEqual(((term(5, 2, 7))-(term(1,3, 7)))!.differentiate(),
                   (term(5, 2, 7).differentiate())-(term(1,3, 7).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
    var init_product = term(12, 4)*(term(5,4))
    var deriv_pt1 = (term(12, 4).differentiate())*(term(5,4))
    var deriv_pt2 = term(12, 4)*(term(5,4).differentiate())
    
    XCTAssertEqual(init_product.differentiate() , deriv_pt1+(deriv_pt2))
    
    // Fractional Number Coefficient
    init_product = term(1,2, 4)*(term(2,3,4))
    deriv_pt1 = (term(1,2, 4).differentiate())*(term(2,3,4))
    deriv_pt2 = term(1,2, 4)*(term(2,3,4).differentiate())
    
    XCTAssertEqual(init_product.differentiate() , deriv_pt1+(deriv_pt2))
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  AntiDifferentiation Test
  ///////////////////////////////////////////////////////////////////////////////////////
  //As stated in specification for any term b is assumed >= 0 and Integration Constant is Zero
  //Note : AntiDerivative of f(x) = F(x) , f = F
  //Note : c = Arbitrary Constant
  
  // Constant Rule f(x) = c => F = c*x
  public func testAntiDifferentiateConstantRule() -> Void {
    // Zero
    XCTAssertEqual(RatTermTest.zeroTerm, RatTermTest.zeroTerm.antiDerivative())
    
    // Non Zero constant
    XCTAssertEqual(term(3,1), term(3,0).antiDerivative())
  }
  
  // Constant Multiple Rule f(x) = c*g(x) => F = c*G(x)
  public func testAntiDifferentiateConstantMultipleRule() -> Void {
    // Whole Number Coefficient
    XCTAssertEqual(((term(5, 0))*(term(1,2))).antiDerivative(),
                   term(5, 0)*(term(1,2).antiDerivative()))
    
    // Fraction Coefficient
    XCTAssertEqual(((term(1,500, 0))*(term(1,2,3))).antiDerivative(),
                   term(1,500, 0)*(term(1,2,3).antiDerivative()))
  }
  
  // Power Rule f(x) = x^a => F = (x^(a+1))/(a+1)
  public func testAntiDifferentiatePowerRule() -> Void {
    XCTAssertEqual(term(1, 2), term(2, 1).antiDerivative())
    XCTAssertEqual(term(4, 3, 3), term(4, 2).antiDerivative())
  }
  
  // Sum Rule if h(x) = f(x) + g(x) => H(x) = F(x) + G(x)
  public func testAntiDifferentiateSumRule() -> Void {
    // Whole Number Coefficient
    XCTAssertEqual((term(1, 2)+(term(3,2)))!.antiDerivative(),
                   term(1, 2).antiDerivative()+(term(3,2).antiDerivative()))
    
    // Fraction Coefficient
    XCTAssertEqual((term(3,4,5)+(term(1,2,5)))!.antiDerivative(),
                   term(3,4,5).antiDerivative()+(term(1,2,5).antiDerivative()))
  }
  
  // Difference Rule if h(x) = f(x) - g(x) => H(x) = F(x) - G(x)
  public func testAntiDifferentiateDifferenceRule() -> Void {
    // Whole Number Coefficient
    XCTAssertEqual((term(1, 2)-(term(3,2)))!.antiDerivative(),
                   term(1, 2).antiDerivative()-(term(3,2).antiDerivative()))
    
    // Fraction Coefficient
    XCTAssertEqual((term(3,4,5)-(term(1,2,5)))!.antiDerivative(),
                   term(3,4,5).antiDerivative()-(term(1,2,5).antiDerivative()))
  }
}