//
//  RatNumTest.swift
//  RatNumTests
//

import XCTest
@testable import RatNum

/**
 
 This class contains a set of test cases that can be used to test the
 implementation of the RatNum class.
 
 RatNum is implemented for you, so it should already pass all the tests in
 this suite. This test is provided to give you (1) examples of using the
 RatNum class, albeit in the context of a test driver and (2) an example of a
 test suite.
 
 */
class RatNumTest: XCTestCase {
  
  // Naming convention used throughout class: spell out number in
  // variable as its constructive form. Unary minus is notated with
  // the prefix "neg", and the solidus ("/") is notated with an 'I'
  // character. Thus, "1 + 2/3" becomes "one_plus_two_I_three".
  private let zero =  RatNum(0);
  private let one =  RatNum(1);
  private let negOne =  RatNum(-1);
  private let two =  RatNum(2);
  private let three =  RatNum(3);
  private let one_I_two =  RatNum(1, 2);
  private let one_I_three =  RatNum(1, 3);
  private let one_I_four =  RatNum(1, 4);
  private let two_I_three =  RatNum(2, 3);
  private let three_I_four =  RatNum(3, 4);
  private let negOne_I_two =  RatNum(-1, 2);
  
  // accuracy of double comparision
  private let accuracy = 0.000001
  
  // improper fraction
  private let three_I_two =  RatNum(3, 2);
  
  // NaNs
  private let one_I_zero =  RatNum(1, 0);
  private let negOne_I_zero =  RatNum(-1, 0);
  private let hundred_I_zero =  RatNum(100, 0);
  
  /**
   * ratnums: Set of varied ratnums (includes NaNs) set is { 0, 1, -1, 2, 1/2,
   * 3/2, 1/0, -1/0, 100/0 }
   */
  private var ratNums : [RatNum] = []
  
  /**
   * ratnans: Set of varied NaNs set is { 1/0, -1/0, 100/0 }
   */
  private var ratNaNs : [RatNum] = []
  
  /**
   * ratNonNaNs: Set of varied non-NaN ratNums set is ratNums - ratNaNs
   */
  private var ratNonNaNs : [RatNum] = []
  
  override func setUp() {
    ratNums =  [ zero, one, negOne, two, one_I_two, negOne_I_two, three_I_two,
                 /* NaNs */one_I_zero, negOne_I_zero, hundred_I_zero ];
    
    /**
     * ratnans: Set of varied NaNs set is { 1/0, -1/0, 100/0 }
     */
    ratNaNs = [ one_I_zero, negOne_I_zero, hundred_I_zero ]
    
    /**
     * ratNonNaNs: Set of varied non-NaN ratNums set is ratNums - ratNaNs
     */
    ratNonNaNs =  [ zero, one, negOne, two, one_I_two, three_I_two ]
  }
  
  /**
   * Asserts that description is equal to rep. This method depends on the
   * implementation of RatNum's "description" property and "==" methods Therefore,
   * one should verify (test) those methods before using this method is in
   * tests.
   *
   * - Note: The `file` and `line` parameters are present so that unit test failures can
   *   be attributed to the *call site* of `eq` rather than the body of `eq`.
   *
   */
  private func eq(_ ratNum : RatNum, _ rep: String, file: StaticString = #file, line: UInt = #line) {
    XCTAssertEqual(rep, String(describing: ratNum), file: file, line: line)
  }
  
  // The actual test cases are below.
  //
  // The order of the test cases is important for producing useful
  // output. If a test uses a method of RatNum, it should test that
  // method before hand. For example, suppose one of the test cases
  // for "negate" is:
  //
  // "-RatNum(1) == RatNum(-1)"
  //
  // In this case, the test case relies on RatNum's "==" method
  // in addition to "negate"; therefore, one should test "=="
  // before "negate". Otherwise, it will be unclear if failing the
  // "negate" test is due "negate" having a bug or "==" having a
  // bug. (Furthermore, the test depends on RatNum's constructor,
  // so it should also be tested beforehand.)
  //
  // In general, it is best to have as few dependences in your test
  // cases as possible. Doing so, will reduce the number of methods
  // that could cause a test case to fail, making it easier to find
  // the faulty method. In practice, one will usually need to
  // depend on a few core methods such as the constructors and
  // "==" methods. Also, some of the test cases below depend on
  // the "description" property because it made the cases easier to write.
  //
  // As a secondary concern to above, if one has access to the
  // source code of a class (as under glass box testing) one should
  // order tests such that a method is tested after all the methods
  // it depends on are tested. For example, in RatNum, the "sub"
  // method calls the "negate" method; therefore, one should test
  // "negate" before "sub". Following this methodology will make it
  // more clear that one should fix bugs in "negate" before looking
  // at the results of "sub" test because, "sub" could be correctly
  // written and the "sub" test case fails only be "negate" is
  // broken.
  //
  // If one does not have access to the source code (as is the case
  // of RatTermTest and RatPolyTest, because you are providing the
  // implementations), one can still take an educated guess as to
  // which methods depend on other methods, but don't worry about
  // getting it perfect.
  
  // First, we test the initializers in isolation of (without
  // depending on) all other RatNum methods.
  //
  // Unfortunately, without using any of RatNum's methods, all we
  // can do is call the initializers and ensure that "checkRep"
  // passes. While this is useful, it does not catch many types of
  // errors. For example, the initializers could always return a
  // RatNum, r, where r.numer = 1 and r.denom = 1. Being a valid
  // RatNum, r would pass "checkRep" but would be the wrong RatNum
  // in most cases.
  //
  // Given that we are unable to fully test the initializers, when
  // any other test case fails, it could be due to an error in the
  // constructor instead of an error in method the test case is
  // testing.
  //
  // If RatNum had public properties, this problem would not exist,
  // because we could check if the fields were set to the correct
  // values. This problem is really a case of a more general
  // problem of being unable to test private fields and methods of
  // classes. For example, we are also unable to test the gcd
  // method because it is private. Solutions to this general
  // problem include:
  //
  // (1) Make the private fields and methods either
  // public or internal. (For example, make numer, denom, and gcd
  // public.) This in not done in general because private fields
  // have many benefits as will be discussed in class.
  //
  // (2) Move the test suite code into RatNum and, thus, it would
  // have access to private members. (Maybe as a static inner
  // class [Don't worry if you don't know what this means yet.])
  // This is not done in general because it clutters the class
  // being tested, making it harder to understand.
  //
  // In practice, while testing, you may find it necessary to do (1)
  // or (2) temporarily with a test case that accesses private
  // fields or methods to track down a bug. But after finding the
  // bug, remember to revert your code back. Also for future
  // problem sets where you will be writing your own test suites,
  // make sure that your test suite runs correctly without (1) or
  // (2) being true.
  
  // (Note, all of these objects were already constructed above as
  // fields of this class (RatNumTest); thus, one could argue that
  // this test case is redundant. We included this test case anyhow
  // to give you an example of such a test case and because the
  // implementation of this class could change eliminating the
  // fields above.)
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  Constructor
  ///////////////////////////////////////////////////////////////////////////////////////
  
  public func testOneArgConstructor() {
    let _ = RatNum(0);
    let _ = RatNum(1);
    let _ = RatNum(-1);
    let _ = RatNum(2);
    let _ = RatNum(3);
  }
  
  public func testTwoArgConstructorPos() {
    let _ = RatNum(1, 2);
    let _ = RatNum(1, 3);
    let _ = RatNum(1, 4);
    let _ = RatNum(2, 3);
    let _ = RatNum(3, 4);
  }
  
  public func testTwoArgConstructorNeg() {
    let _ = RatNum(-1, 2);
  }
  
  public func testTwoArgConstructorImproperFract() {
    // improper fraction
    let _ = RatNum(3, 2);
  }
  
  public func testTwoArgConstructorNaN() {
    // NaNs
    let _ = RatNum(1, 0);
    let _ = RatNum(-1, 0);
    let _ = RatNum(100, 0);
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  IsNaN Test
  ///////////////////////////////////////////////////////////////////////////////////////
  
  // Next, we test isNaN because it can be tested in isolation from
  // everything except the constructors. (All instance method tests
  // will depend on a constructor.)
  public func testIsNaN() {
    for ratNum in ratNaNs {
      XCTAssertTrue(ratNum.isNaN)
    }
  }
  
  public func testIsNotNaN() {
    for ratNum in ratNonNaNs {
      XCTAssertFalse(ratNum.isNaN)
    }
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  Test is Polarity, IsPositive, IsNegative
  ///////////////////////////////////////////////////////////////////////////////////////
  
  // Next, we test isPos and isNeg because we can easily test these
  // methods without depending on any other methods (except the
  // constructors).
  private func assertPos(_ n : RatNum, file: StaticString = #file, line: UInt = #line) {
    XCTAssertTrue(n.isPositive, file: file, line: line);
    XCTAssertFalse(n.isNegative, file: file, line: line);
  }
  
  private func assertNeg(_ n : RatNum, file: StaticString = #file, line: UInt = #line) {
    XCTAssertTrue(n.isNegative, file: file, line: line);
    XCTAssertFalse(n.isPositive, file: file, line: line);
  }
  
  //Test Zero is not positive nor negative
  public func testZeroIsNotPosNorNeg() {
    XCTAssertFalse(zero.isPositive);
    XCTAssertFalse(zero.isNegative);
  }
  
  public func testIsNegWholeNum() {
    assertNeg(negOne);
  }
  
  public func testIsNegFraction(){
    assertNeg(negOne_I_two);
  }
  
  
  public func testIsPosWholeNum(){
    assertPos(one);
    assertPos(two);
    assertPos(three);
  }
  
  public func testIsPosFraction(){
    assertPos(one_I_three);
    assertPos(two_I_three);
    assertPos(three_I_two);
  }
  
  //NaN is Special instance and is non-intuitive, See RatNum Specification
  public func testNaNIsPos(){
    assertPos(one_I_zero);
    assertPos(negOne_I_zero);
    assertPos(hundred_I_zero);
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  Double Value
  ///////////////////////////////////////////////////////////////////////////////////////
  
  // Next, we test doubleValue because the test does not require any
  // other RatNum methods (other than constructors).
  
  // asserts that two double's are within .0000001 of one another.
  // It is often impossible to assert that doubles are exactly equal
  // because of the idiosyncrasies of Swift's floating point math.
  private func approxEq(_ expected: Double, _ actual: Double, file: StaticString = #file, line: UInt = #line) {
    XCTAssertEqual(expected, actual, accuracy: accuracy, file: file, line: line)
  }
  
  public func testDoubleValueSmallNum() {
    // use left-shift operator "<<" to create integer for 2^30
    let one_I_twoToThirty =  RatNum(1, (1 << 30))
    let quiteSmall = 1.0 / pow(2.0, 30.0)
    approxEq(quiteSmall, one_I_twoToThirty.asDouble);
  }
  
  // Whole numbers
  public func testDoubleValueWholeNumber() {
    approxEq(0.0, zero.asDouble)
    approxEq(1.0, one.asDouble)
    approxEq(2.0, two.asDouble)
    approxEq(-1.0, negOne.asDouble)
  }
  
  // Fractional numbers
  public func testDoubleValueFracNumber() {
    approxEq(0.5, one_I_two.asDouble);
    approxEq(2.0 / 3.0, two_I_three.asDouble)
    approxEq(0.75, three_I_four.asDouble)
  }
  
  // NaN double Value
  public func testDoubleValueNaN() {
    XCTAssertTrue(one_I_zero.asDouble.isNaN)
  }
  
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  Equals
  ///////////////////////////////////////////////////////////////////////////////////////
  
  // Next, we test the equals method because that can be tested in
  // isolation from everything except the constructor and maybe
  // isNaN, which we just tested.
  // Additionally, this method will be very useful for testing other
  // methods.
  
  /**
   * This test check is equals is reflexive. In other words that x.equals(x)
   * is always true.
   */
  public func testEqualsReflexive() {
    for ratNum in ratNums {
      XCTAssertEqual(ratNum, ratNum)
    }
  }
  
  public func testEqualsSimple() {
    // Some simple cases.
    XCTAssertEqual(one, one);
    XCTAssertEqual(one + one, two);
    // including negitives:
    XCTAssertEqual(negOne, negOne);
  }
  
  public func testEqualsSimpleWithDiffObjects() {
    // Some simple cases where the objects are different but
    // represent the same rational number. That is, x != y but
    // x.equals(y).
    XCTAssertEqual( RatNum(1, 1),  RatNum(1, 1));
    XCTAssertEqual( RatNum(1, 2),  RatNum(1, 2));
  }
  
  public func testEqualsNotReducedFormOne() {
    // Check that equals works on fractions that were not
    // constructed in reduced form.
    XCTAssertEqual(one,  RatNum(2, 2));
    XCTAssertEqual( RatNum(2, 2), one);
  }
  
  public func testEqualsNotReducedFormNegOne() {
    // including negitives:
    XCTAssertEqual(negOne,  RatNum(-9, 9));
    XCTAssertEqual( RatNum(-9, 9), negOne);
  }
  
  public func testEqualsNotReducedFormFraction() {
    // including double negitives:
    XCTAssertEqual(one_I_two,  RatNum(-13, -26));
    XCTAssertEqual( RatNum(-13, -26), one_I_two);
  }
  
  public func testEqualsNaN() {
    // Check that all NaN's are equals to one another.
    XCTAssertEqual(one_I_zero, one_I_zero);
    XCTAssertEqual(one_I_zero, negOne_I_zero);
    XCTAssertEqual(one_I_zero, hundred_I_zero);
  }
  
  public func testEqualsForFalsePos() {
    // Some simple cases checking for false positives.
    XCTAssertNotEqual(one, zero)
    XCTAssertNotEqual(zero, one)
    XCTAssertNotEqual(one, two)
    XCTAssertNotEqual(two, one)
  }
  
  public func testEqualsForSign() {
    // Check that equals does not neglect sign.
    XCTAssertNotEqual(one, negOne)
    XCTAssertNotEqual(negOne, one)
  }
  
  public func testEqualsNotFalsePosWithFracs() {
    // Check that equals does not return false positives on
    // fractions.
    XCTAssertNotEqual(one, one_I_two)
    XCTAssertNotEqual(one_I_two, one)
    XCTAssertNotEqual(one, three_I_two)
    XCTAssertNotEqual(three_I_two, one)
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  String
  ///////////////////////////////////////////////////////////////////////////////////////
  
  // Now that we have verified equals, we will use it in the
  // rest or our tests.
  
  // Next, we test the toString and valueOf methods because we can test
  // them isolation of everything except the constructor and equals,
  // and they will be useful methods to aid with testing other
  // methods. (In some cases, it is easier to use valueOf("1/2") than
  //  RatNum(1, 2) as you will see below.)
  
  // Note that "eq" calls "toString" on its first argument.
  public func testToStringSimple() {
    eq(zero, "0");
    
    eq(one, "1");
    
    let four =  RatNum(4);
    eq(four, "4");
    
    eq(negOne, "-1");
    
    let negFive =  RatNum(-5);
    eq(negFive, "-5");
    
    let negZero =  RatNum(-0);
    eq(negZero, "0");
  }
  
  public func testToStringFractions() {
    let one_I_two =  RatNum(1, 2);
    eq(one_I_two, "1/2");
    
    let three_I_two =  RatNum(3, 2);
    eq(three_I_two, "3/2");
    
    let negOne_I_thirteen =  RatNum(-1, 13);
    eq(negOne_I_thirteen, "-1/13");
    
    let fiftyThree_I_seven =  RatNum(53, 7);
    eq(fiftyThree_I_seven, "53/7");
    
    let twentySeven_I_thirteen =  RatNum(27, 13);
    eq(twentySeven_I_thirteen, "27/13");
  }
  
  public func testToStringNaN() {
    let one_I_zero =  RatNum(1, 0);
    eq(one_I_zero, "NaN");
    
    let two_I_zero =  RatNum(2, 0);
    eq(two_I_zero, "NaN");
    
    let negOne_I_zero =  RatNum(-1, 0);
    eq(negOne_I_zero, "NaN");
    
    let zero_I_zero =  RatNum(0, 0);
    eq(zero_I_zero, "NaN");
    
    let negHundred_I_zero =  RatNum(-100, 0);
    eq(negHundred_I_zero, "NaN");
  }
  
  public func testToStringOneDenom() {
    let two_I_one =  RatNum(2, 1);
    eq(two_I_one, "2");
    
    let zero_I_one =  RatNum(0, 1);
    eq(zero_I_one, "0");
  }
  
  public func testToStringReduction() {
    let negOne_I_negTwo =  RatNum(-1, -2);
    eq(negOne_I_negTwo, "1/2");
    
    let two_I_four =  RatNum(2, 4);
    eq(two_I_four, "1/2");
    
    let six_I_four =  RatNum(6, 4);
    eq(six_I_four, "3/2");
    
    let negHundred_I_negHundred =  RatNum(-100, -100);
    eq(negHundred_I_negHundred, "1");
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  Value Of
  ///////////////////////////////////////////////////////////////////////////////////////
  
  // helper function, "decode-and-check"
  private func decChk(_ s: String, _ expected: RatNum, file: StaticString = #file, line: UInt = #line) {
    XCTAssertEqual(RatNum(s), expected, file: file, line: line);
  }
  
  // Note that decChk calls valueOf.
  public func testValueOf() {
    decChk("0", zero);
    decChk("1", one);
  }
  
  public func testValueOfPosOne() {
    decChk("1/1", one);
    decChk("2/2", one);
    decChk("-1/-1", one);
  }
  
  public func testValueOfNegOne() {
    decChk("-1", negOne);
    decChk("1/-1", negOne);
    decChk("-3/3", negOne);
  }
  
  public func testValueOfPosTwo() {
    decChk("2", two);
    decChk("2/1", two);
    decChk("-4/-2", two);
  }
  
  public func testValueOfOneHalf() {
    decChk("1/2", one_I_two);
    decChk("2/4", one_I_two);
  }
  
  public func testValueOfThreeHalfs() {
    decChk("3/2", three_I_two);
    decChk("-6/-4", three_I_two);
  }
  
  public func testValueOfNaN() {
    decChk("NaN", one_I_zero);
    decChk("NaN", negOne_I_zero);
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  Negate
  ///////////////////////////////////////////////////////////////////////////////////////
  
  // Next, we test the arithmetic operations.
  //
  // We test them in our best guess of increasing difficultly and
  // likelihood of having depend on a previous method. (For
  // example, add could use sub as a subroutine.
  //
  // Note that our tests depend on toString and
  // equals, which we have already tested.
  
  public func testNegateNaN() {
    eq(-one_I_zero, "NaN");
    eq(-negOne_I_zero, "NaN");
    eq(-hundred_I_zero, "NaN");
  }
  
  public func testNegateToPos() {
    eq(-zero, "0");
    eq(-negOne, "1");
  }
  
  public func testNegateToNeg() {
    eq(-one, "-1");
    eq(-two, "-2");
    eq(-one_I_two, "-1/2");
    eq(-one_I_three, "-1/3");
    eq(-three_I_four, "-3/4");
    eq(-three_I_two, "-3/2");
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  Add Test
  ///////////////////////////////////////////////////////////////////////////////////////
  
  public func testAddSimple() {
    eq(zero+(zero), "0");
    eq(zero+(one), "1");
    eq(one+(zero), "1");
    eq(one+(one), "2");
    eq(one+(negOne), "0");
    eq(one+(two), "3");
    eq(two+(two), "4");
  }
  
  public func testAddComplexToOne() {
    eq(one_I_two+(one_I_two), "1");
    eq(one_I_three+(two_I_three), "1");
  }
  
  public func testAddComplex() {
    eq(one_I_two+(zero), "1/2");
    eq(one_I_two+(one), "3/2");
    eq(one_I_two+(one_I_three), "5/6");
    eq(one_I_two+(negOne), "-1/2");
    eq(one_I_two+(two), "5/2");
    eq(one_I_two+(two_I_three), "7/6");
    eq(one_I_three+(three_I_four), "13/12");
  }
  
  public func testAddImproper() {
    eq(three_I_two+(one_I_two), "2");
    eq(three_I_two+(one_I_three), "11/6");
    eq(three_I_four+(three_I_four), "3/2");
    eq(three_I_two+(three_I_two), "3");
  }
  
  public func testAddOnNaN() {
    // each test case (addend, augend) drawn from the set
    // ratNums x ratNaNs
    for ratNum in ratNums {
      for ratNan in ratNaNs {
        eq(ratNum + ratNan, "NaN");
        eq(ratNan + ratNum, "NaN");
      }
    }
  }
  
  // Testing Add Transitivity Property
  
  public func testAddTransitivelyZero() {
    eq(zero+(zero)+(zero), "0");
    eq(zero+(zero+(zero)), "0");
  }
  
  public func testAddTransitivelyOne() {
    eq(one_I_three+(one_I_three)+(one_I_three), "1");
    eq(one_I_three+(one_I_three+(one_I_three)), "1");
  }
  
  public func testAddTransitivelyWholeNum() {
    eq(one+(one)+(one), "3");
    eq(one+(one+(one)), "3");
    
    eq(one+(two)+(three), "6");
    eq(one+(two+(three)), "6");
  }
  
  public func testAddTransitivelyNaN() {
    eq(one_I_zero+(one_I_zero)+(one_I_zero), "NaN");
    eq(one_I_zero+(one_I_zero+(one_I_zero)), "NaN");
  }
  
  public func testAddTransitivelyFractions() {
    eq(one_I_two+(one_I_three)+(one_I_four), "13/12");
    eq(one_I_two+(one_I_three+(one_I_four)), "13/12");
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  Subtraction Test
  ///////////////////////////////////////////////////////////////////////////////////////
  
  public func testSubSimple() {
    eq(zero-(zero), "0");
    eq(one-(zero), "1");
    eq(one-(one), "0");
    eq(two-(one), "1");
    eq(one-(negOne), "2");
  }
  
  public func testSubSimpleToNeg() {
    eq(zero-(one), "-1");
    eq(one-(two), "-1");
    eq(one-(three), "-2");
  }
  
  public func testSubComplex() {
    eq(one-(one_I_two), "1/2");
    eq(one_I_two-(one), "-1/2");
    eq(one_I_two-(zero), "1/2");
    eq(one_I_two-(two_I_three), "-1/6");
    eq(one_I_two-(three_I_four), "-1/4");
  }
  
  public func testSubImproper() {
    eq(three_I_two-(one_I_two), "1");
    eq(three_I_two-(one_I_three), "7/6");
  }
  
  public func testSubOnNaN() {
    // analogous to testAddOnNaN()
    
    for ratNum in ratNums {
      for ratNan in ratNaNs {
        eq(ratNum - ratNan, "NaN");
        eq(ratNan - ratNum, "NaN");
      }
    }
  }
  
  // Subtraction transitivity Tests
  
  public func testSubTransitivetyWholeNumsToNonZero() {
    // subtraction is not transitive; testing that operation is
    // correct when *applied transitivitely*, not that it obeys
    // the transitive property
    
    eq(one-(one)-(one), "-1");
    eq(one-(one-(one)), "1");
    eq(one-(two)-(three), "-4");
    eq(one-(two-(three)), "2");
  }
  
  public func testSubTransitivetyWholeNumsToZero() {
    eq(zero-(zero)-(zero), "0");
    eq(zero-(zero-(zero)), "0");
  }
  
  public func testSubTransitivelyComplex() {
    eq(one_I_three-(one_I_three)-(one_I_three), "-1/3");
    eq(one_I_three-(one_I_three-(one_I_three)), "1/3");
    
    eq(one_I_two-(one_I_three)-(one_I_four), "-1/12");
    eq(one_I_two-(one_I_three-(one_I_four)), "5/12");
  }
  
  public func testSubTransitivelyNaN() {
    eq(one_I_zero-(one_I_zero)-(one_I_zero), "NaN");
    eq(one_I_zero-(one_I_zero-(one_I_zero)), "NaN");
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  Multiplication Test
  ///////////////////////////////////////////////////////////////////////////////////////
  
  //Test multiplication properties
  public func testMulPropertiesZero() {
    // zero property
    for ratNum in ratNonNaNs {
      eq(zero * ratNum, "0");
      eq(ratNum * zero, "0");
    }
  }
  
  public func testMulPropertiesOne() {
    // one property
    for ratNum in ratNonNaNs {
      eq(one * ratNum, String(describing: ratNum));
      eq(ratNum * one, String(describing: ratNum));
    }
  }
  
  public func testMulPropertiesNegOne() {
    // negOne property
    for ratNum in ratNonNaNs {
      eq(-one * ratNum, String(describing: -ratNum));
      eq(ratNum * -one, String(describing: -ratNum));
    }
  }
  
  public func testMulSimple() {
    eq(two*(two), "4");
    eq(two*(three), "6");
    eq(three*(two), "6");
  }
  
  public func testMulComplexToOne() {
    eq(one_I_two*(two), "1");
    eq(two*(one_I_two), "1");
  }
  
  public func testMulComplexToComplex() {
    eq(one_I_two*(one_I_two), "1/4");
    eq(one_I_two*(one_I_three), "1/6");
    eq(one_I_three*(one_I_two), "1/6");
  }
  
  public func testMulImproper() {
    eq(three_I_two*(one_I_two), "3/4");
    eq(three_I_two*(one_I_three), "1/2");
    eq(three_I_two*(three_I_four), "9/8");
    eq(three_I_two*(three_I_two), "9/4");
  }
  
  public func testMulOnNaN() {
    // analogous to testAddOnNaN()
    
    for ratNum in ratNums {
      for ratNan in ratNaNs {
        eq(ratNum * ratNan, "NaN");
        eq(ratNan * ratNum, "NaN");
      }
    }
  }
  
  public func testMulTransitivelyToNonZero() {
    eq(one*(one)*(one), "1");
    eq(one*(one*(one)), "1");
    eq(one*(two)*(three), "6");
    eq(one*(two*(three)), "6");
  }
  
  public func testMulTransitivelyToZero() {
    eq(zero*(zero)*(zero), "0");
    eq(zero*(zero*(zero)), "0");
  }
  
  public func testMulTransitivelyComplex() {
    eq(one_I_three*(one_I_three)*(one_I_three), "1/27");
    eq(one_I_three*(one_I_three*(one_I_three)), "1/27");
    
    eq(one_I_two*(one_I_three)*(one_I_four), "1/24");
    eq(one_I_two*(one_I_three*(one_I_four)), "1/24");
  }
  
  public func testMulTransitivelyNaN() {
    eq(one_I_zero*(one_I_zero)*(one_I_zero), "NaN");
    eq(one_I_zero*(one_I_zero*(one_I_zero)), "NaN");
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  Division Test
  ///////////////////////////////////////////////////////////////////////////////////////
  
  public func testSimpleDivToZero() {
    eq(zero/(one), "0");
    eq(one/(one), "1");
    eq(one/(negOne), "-1");
    eq(one/(two), "1/2");
    eq(two/(two), "1");
  }
  
  public func testDivComplex() {
    eq(one_I_two/(one), "1/2");
    eq(one_I_two/(one_I_two), "1");
    eq(one_I_two/(one_I_three), "3/2");
    eq(one_I_two/(negOne), "-1/2");
    eq(one_I_two/(two), "1/4");
    eq(one_I_two/(two_I_three), "3/4");
    eq(one_I_two/(three_I_four), "2/3");
    eq(one_I_three/(two_I_three), "1/2");
    eq(one_I_three/(three_I_four), "4/9");
  }
  
  public func testDivImproper() {
    eq(three_I_two/(one_I_two), "3");
    eq(three_I_two/(one_I_three), "9/2");
    eq(three_I_two/(three_I_two), "1");
  }
  
  public func testDivNaN() {
    eq(zero/(zero), "NaN");
    eq(one/(zero), "NaN");
    eq(one_I_two/(zero), "NaN");
    eq(one_I_three/(zero), "NaN");
  }
  
  public func testDivOnNaN() {
    // each test case (addend, augend) drawn from the set
    // ratNums x ratNaNs
    
    for ratNum in ratNums {
      for ratNan in ratNaNs {
        eq(ratNum / ratNan, "NaN");
        eq(ratNan / ratNum, "NaN");
      }
    }
  }
  
  public func testDivTransitivelyWholeNum() {
    eq(one/(one)/(one), "1");
    eq(one/(one/(one)), "1");
    eq(one_I_three/(one_I_three)/(one_I_three), "3");
    eq(one_I_two/(one_I_three)/(one_I_four), "6");
  }
  
  public func testDivTransitively() {
    // (same note as in testSubTransitively re: transitivity property)
    eq(one/(two)/(three), "1/6");
    eq(one/(two/(three)), "3/2");
    eq(one_I_three/(one_I_three/(one_I_three)), "1/3");
    eq(one_I_two/(one_I_three/(one_I_four)), "3/8");
  }
  
  public func testDivTransitivelyNaN() {
    eq(zero/(zero)/(zero), "NaN");
    eq(zero/(zero/(zero)), "NaN");
    eq(one_I_zero/(one_I_zero)/(one_I_zero), "NaN");
    eq(one_I_zero/(one_I_zero/(one_I_zero)), "NaN");
    
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////
  ////  Compare Test
  ///////////////////////////////////////////////////////////////////////////////////////
  
  // Finally, we test compare. We do so last, because compare may
  // depend on sub, isNaN, and/or equals, so we want to test those
  // methods first.
  
  private func assertGreater(_ larger: RatNum, _ smaller: RatNum, file: StaticString = #file, line: UInt = #line) {
    XCTAssertTrue(larger > smaller, file: file, line: line)
    XCTAssertTrue(smaller < larger, file: file, line: line)
  }
  
  public func testCompareToReflexive() {
    // reflexivitiy: x.compare(x) == 0.
    for ratNum in ratNums {
      XCTAssertEqual(ratNum, ratNum)
    }
  }
  
  public func testCompareToNonFract() {
    assertGreater(one, zero);
    assertGreater(one, negOne);
    assertGreater(two, one);
    assertGreater(two, zero);
    assertGreater(zero, negOne);
  }
  
  public func testCompareToFract() {
    assertGreater(one, one_I_two);
    assertGreater(two, one_I_three);
    assertGreater(one, two_I_three);
    assertGreater(two, two_I_three);
    assertGreater(one_I_two, zero);
    assertGreater(one_I_two, negOne);
    assertGreater(one_I_two, negOne_I_two);
    assertGreater(zero, negOne_I_two);
  }
  
  public func testCompareToNaNs() {
    for ratNan1 in ratNaNs {
      for ratNan2 in ratNaNs {
        XCTAssertEqual(ratNan1, ratNan2)
      }
      for ratNonNan in ratNonNaNs {
        assertGreater(ratNan1, ratNonNan)
      }
    }
  }
}