{-  Exploring Languages with Interpreters and Functional Programming
    Chapters 7 & 12: Rational Module Test (Rational2/RationalDeferGCD)
    Copyright (C) 2018, H. Conrad Cunningham

1234567890123456789012345678901234567890123456789012345678901234567890

2018-07-04: Developed test script along with TestRational1
            to be compatible with 2018 Chapters 7 and 10;
            added copyright notice
2018-07-11: Corrected errors in divRat tests
2018-07-19: Updated for compatibility with Chapter 12

This module is a test script for the Rational module that uses the
data representation module RationalDeferGCD.

This module uses black-box testing methods to conduct functional-type
unit tests on the Rational interface.

Assumptions:

(1) RationalDeferGCD functions makeRat, numer, denom, and showRat
    satisfy their specifications (i.e. have been successfully tested
    using TestRatRepDefer).

(2) This module is tested on a machine with two's complement
    integers. (The machine has 64-bit integers, but the tests are not
    dependent on the length.)

(3) Function eqRat is used in the other tests. If it does not satisfy
    its specification, then the other tests may be misleading.

-}

module TestRational2
where

-- Select needed data representation module to test
-- import Rational1  -- for use of RationalRep RationalCore
import Rational2     -- for use of RationalRep RationalDeferGCD

-- For catching Exceptions
import Prelude hiding (catch)
import Control.Exception

-- To display whether test passed for failed
pass :: Bool -> String
pass True  = "PASS"
pass False = "FAIL"

-- Named constants used in testing
one      = makeRat 1 1       --  1
negone   = makeRat (-1) 1    -- -1
two      = makeRat 2 1       --  2
negtwo   = makeRat (-2) 1    -- -2
half     = makeRat 1 2       -- 1/2
quart1   = makeRat 1 4       -- 1/4
quart3   = makeRat 3 4       -- 3/4
r_32_27  = makeRat 32 27     --  32/27
nr_32_27 = makeRat (-32) 27  -- -32/77
maxInt   = makeRat (maxBound::Int) 1        -- maximum Int
minInt   = makeRat (minBound::Int) 1        -- minimum Int
minIntp1 = makeRat ((minBound::Int) + 1) 1  -- minInt + 1

-- IO program test script for Rational module black-box test
-- Does not depend on specific data abstraction
main :: IO ()
main =
    do
        putStrLn "\nTesting Rational with RationalDeferGCD"

        -- Display the constants
        putStrLn "\nNamed constants used for testing"
        putStrLn ("one      = makeRat 1 1       =>  "
                  ++ showRat one)
        putStrLn ("negone   = makeRat (-1) 1    =>  "
                  ++ showRat negone)
        putStrLn ("two      = makeRat 2 1       =>  "
                  ++ showRat two)
        putStrLn ("negtwo   = makeRat (-2) 1    =>  "
                  ++ showRat negtwo)
        putStrLn ("half     = makeRat 1 2       =>  "
                  ++ showRat half)
        putStrLn ("quart1   = makeRat 1 4       =>  "
                  ++ showRat quart1)
        putStrLn ("quart3   = makeRat 3 4       =>  "
                  ++ showRat quart3)
        putStrLn ("r_32_27  = makeRat 32 27     =>  "
                  ++ showRat r_32_27)
        putStrLn ("nr_32_27 = makeRat (-32) 27  =>  "
                  ++ showRat nr_32_27)
        putStrLn (
                 "maxInt   = makeRat (maxBound::Int) 1       =>  "
                 ++ show (numer maxInt) )
        putStrLn (
                 "minInt   = makeRat (minBound::Int) 1       =>  "
                 ++ show (numer minInt) )
        putStrLn (
                 "minIntp1 = makeRat ((minBound::Int) + 1) 1 =>  "
                 ++ show (numer minIntp1) )

        -- Test eqRat :: Rat -> Rat -> Bool
        -- Boundary values: zeroRat, one, negone, maxInt, minInt
        -- Representative values: quart1, quart3
        -- Equality properties: refexivity, symmetry, transitivity
        -- Interface invariant
        putStrLn "\nTesting eqRat"
        putStrLn ("eqRat zeroRat zeroRat == True:                   "
                  ++ pass (eqRat zeroRat zeroRat == True))
        putStrLn ("eqRat zeroRat one == False:                      "
                  ++ pass (eqRat zeroRat one == False))
        putStrLn ("eqRat one zeroRat == False:                      "
                  ++ pass (eqRat one zeroRat == False))
        putStrLn ("eqRat one one == True:                           "
                  ++ pass (eqRat one one == True))
        putStrLn ("eqRat negone negone == True:                     "
                  ++ pass (eqRat negone negone == True))
        putStrLn ("eqRat one negone == False:                       "
                  ++ pass (eqRat one negone == False)) 
        putStrLn ("eqRat negone one == False:                       "
                  ++ pass (eqRat negone one == False))
        putStrLn ("eqRat quart1 quart3 == False:                    "
                  ++ pass (eqRat quart1 quart3 == False))
        putStrLn ("eqRat quart3 quart1 == False:                    "
                  ++ pass (eqRat quart3 quart1 == False))
        putStrLn ("eqRat maxInt maxInt == True:                     "
                  ++ pass (eqRat maxInt maxInt == True))
        putStrLn ("eqRat maxInt two == False:                       "
                  ++ pass (eqRat maxInt two == False))
        putStrLn ("eqRat two maxInt == False:                       "
                  ++ pass (eqRat two maxInt == False))
        putStrLn ("eqRat minInt minInt == True:                     "
                  ++ pass (eqRat minInt minInt == True))
        putStrLn ("eqRat minInt negone == False:                    "
                  ++ pass (eqRat minInt negone == False))
        putStrLn ("eqRat negone minInt negone == False:             "
                  ++ pass (eqRat negone minInt == False))
        putStrLn ("eqRat minInt maxInt == False:                    "
                  ++ pass (eqRat minInt maxInt == False))
        putStrLn ("eqRat maxInt minInt == False:                    "
                  ++ pass (eqRat maxInt minInt == False))

        -- Test negRat :: Rat -> Rat 
        -- Boundary values: zeroRat, one, negone, maxInt, minInt,
        --                  minIntp1
        -- Representative values: r_32_27, nr_32_27
        -- Properties: idempotence (double negation)
        -- Interface invariant
        putStrLn "\nTesting negRat"
        putStrLn ("eqRat (negRat zeroRat) zeroRat == True:          "
                  ++ pass (eqRat (negRat zeroRat) zeroRat == True))
        putStrLn ("eqRat (negRat one) negone == True:               "
                  ++ pass (eqRat (negRat one) negone == True))
        putStrLn ("eqRat (negRat negone) one == True:               "
                  ++ pass (eqRat (negRat negone) one == True))
        putStrLn ("eqRat (negRat two) negtwo == True:               "
                  ++ pass (eqRat (negRat two) negtwo == True))
        putStrLn ("eqRat (negRat negtwo) two == True:               "
                  ++ pass (eqRat (negRat negtwo) two == True))
        putStrLn ("eqRat (negRat r_32_27) nr_32_27 == True:         "
                  ++ pass (eqRat (negRat r_32_27) nr_32_27 == True))
        putStrLn ("eqRat (negRat nr_32_27) r_32_27 == True:         "
                  ++ pass (eqRat (negRat nr_32_27) r_32_27 == True))
        -- remainder of negRat tests depend on two's complement Ints
        putStrLn ("eqRat (negRat maxInt) minIntp1 == True:          "
                  ++ pass (eqRat (negRat maxInt) minIntp1 == True))
        putStrLn ("eqRat (negRat minIntp1) maxInt == True:          "
                  ++ pass (eqRat (negRat minIntp1) maxInt == True))
        putStrLn ("(negRat minInt) overflows (to minInt):           "
                  ++ show (numer (negRat minInt)))
        putStrLn ("eqRat (negRat minInt) minInt == True:            "
                  ++ pass (eqRat (negRat minInt) minInt == True)
                  ++ " (2's complement overflow)")

        -- Test addRat :: Rat -> Rat -> Rat 
        -- Boundary values: zeroRat, one, negone, maxInt, minInt
        -- Representative values: quart1, half, quart3, two
        -- Properties: associativity, symmetry/commutativity,
        --             identity, inverse
        -- Interface invariant
        putStrLn "\nTesting addRat"
        putStrLn ("eqRat (addRat zeroRat zeroRat) zeroRat == True:  "
                  ++ pass (eqRat (addRat zeroRat zeroRat) zeroRat
                           == True))
        putStrLn ("eqRat (addRat zeroRat one) one == True:          "
                  ++ pass (eqRat (addRat zeroRat one) one == True))
        putStrLn ("eqRat (addRat one zeroRat) one == True:          "
                  ++ pass (eqRat (addRat one zeroRat) one == True))
        putStrLn ("eqRat (addRat one one) two == True:              "
                  ++ pass (eqRat (addRat one one) two == True))
        putStrLn ("eqRat (addRat one negone) zeroRat == True:       "
                  ++ pass (eqRat (addRat one negone) zeroRat == True))
        putStrLn ("eqRat (addRat negone one) zeroRat == True:       "
                  ++ pass (eqRat (addRat negone one) zeroRat == True))
        putStrLn ("eqRat (addRat quart1 quart1) half == True:       "
                  ++ pass (eqRat (addRat quart1 quart1) half == True))
        putStrLn ("eqRat (addRat half quart1) quart3 == True:       "
                  ++ pass (eqRat (addRat half quart1) quart3 == True))
        putStrLn ("eqRat (addRat quart1 half) quart3 == True:       "
                  ++ pass (eqRat (addRat quart1 half) quart3 == True))
        -- remainder of addRat tests depend on two's complement Ints
        putStrLn ("(addRat maxInt one) overflows (to minInt):       "
                  ++ show (numer (addRat maxInt one)))
        putStrLn ("eqRat (addRat maxInt one) minInt == True:        "
                  ++ pass (eqRat (addRat maxInt one) minInt == True)
                  ++ " (2's complement overflow)")
        putStrLn ("(addRat minInt negone) overflows (to maxInt):    "
                  ++ show (numer (addRat minInt negone)))
        putStrLn ("eqRat (addRat minInt negone) maxInt == True:     "
                  ++ pass (eqRat (addRat minInt negone) maxInt
                           == True)
                  ++ " (2's complement overflow)")

        -- Test subRat :: Rat -> Rat -> Rat 
        -- Boundary values: zeroRat, one, negone, maxInt, minInt
        -- Representative values: quart1, half, quart3, two
        -- Properties: right identity, right inverse
        -- Interface invariant
        putStrLn "\nTesting subRat"
        putStrLn ("eqRat (subRat one zeroRat) one == True:          "
                  ++ pass (eqRat (subRat one zeroRat) one == True))
        putStrLn ("eqRat (subRat one one) zeroRat == True:          "
                  ++ pass (eqRat (subRat one one) zeroRat == True))
        putStrLn ("eqRat (subRat zeroRat one) negone == True:       "
                  ++ pass (eqRat (subRat one negone) two == True))
        putStrLn ("eqRat (subRat one negone) two == True:           "
                  ++ pass (eqRat (subRat one negone) two == True))
        putStrLn ("eqRat (subRat one two) negone == True:           "
                  ++ pass (eqRat (subRat one two) negone == True))
        putStrLn ("eqRat (subRat quart3 half) quart1 == True:       "
                  ++ pass (eqRat (subRat quart3 half) quart1 == True))
        putStrLn ("eqRat (subRat quart3 quart1) half == True:       "
                  ++ pass (eqRat (subRat quart3 quart1) half == True))
        -- remainder of subRat tests depend on two's complement Ints
        putStrLn ("(subRat maxInt negone) overflows (to minInt):    "
                  ++ show (numer (subRat maxInt negone)))
        putStrLn ("eqRat (subRat maxInt negone) minInt == True:     "
                  ++ pass (eqRat (subRat maxInt negone) minInt))
        putStrLn ("(subRat minInt one) overflows (to maxInt):       "
                  ++ show (numer (subRat minInt one)))
        putStrLn ("eqRat (subRat minInt one) maxInt == True:        "
                  ++ pass (eqRat (subRat minInt one) maxInt))

        -- Test mulRat :: Rat -> Rat -> Rat
        -- Boundary values: zeroRat, one, negone, maxInt, minInt
        -- Representative values: quart1, half, quart3, two
        -- Properties: associativity, symmetry/commutativity,
        --     identity, inverse, zero element
        -- Interface invariant
        putStrLn "\nTesting mulRat"
        putStrLn ("eqRat (mulRat zeroRat zeroRat) zeroRat == True:  "
                  ++ pass (eqRat (mulRat zeroRat zeroRat) zeroRat
                           == True))
        putStrLn ("eqRat (mulRat zeroRat one) zeroRat == True:      "
                  ++ pass (eqRat (mulRat zeroRat one) zeroRat
                           == True))
        putStrLn ("eqRat (mulRat one zeroRat) zeroRat == True:      "
                  ++ pass (eqRat (mulRat one zeroRat) zeroRat
                           == True))
        putStrLn ("eqRat (mulRat one one) one == True:              "
                  ++ pass (eqRat (mulRat one one) one == True))
        putStrLn ("eqRat (mulRat one negone) negone == True:        "
                  ++ pass (eqRat (mulRat one negone) negone == True))
        putStrLn ("eqRat (mulRat negone one) negone == True:        "
                  ++ pass (eqRat (mulRat negone one) negone == True))
        putStrLn ("eqRat (mulRat one two) two == True:              "
                  ++ pass (eqRat (mulRat one two) two == True))
        putStrLn ("eqRat (mulRat two one) two == True:              "
                  ++ pass (eqRat (mulRat two one) two == True))
        putStrLn ("eqRat (mulRat half two) one == True:             "
                  ++ pass (eqRat (mulRat half two) one == True))
        putStrLn ("eqRat (mulRat two half) one == True:             "
                  ++ pass (eqRat (mulRat two half) one == True))
        putStrLn ("eqRat (mulRat two quart1) half = True:           "
                  ++ pass (eqRat (mulRat two quart1) half == True))
        putStrLn ("eqRat (mulRat quart1 two) half == True:          "
                  ++ pass (eqRat (mulRat quart1 two) half == True))
        -- remainder of mulRat tests depend on two's complement Ints
        putStrLn ("(mulRat two maxInt) overflows (to -2):           "
                  ++ show (numer (mulRat two maxInt)))
        putStrLn ("eqRat (mulRat two maxInt) negtwo == True:        "
                  ++ pass (eqRat (mulRat two maxInt) negtwo == True)
                  ++ " (2's complement overflow)")
        putStrLn ("(mulRat two minInt) overflows (to 0):            "
                  ++ show (numer (mulRat two minInt)))
        putStrLn ("eqRat (mulRat two minInt) zeroRat == True:       "
                  ++ pass (eqRat (mulRat two minInt) zeroRat == True)
                  ++ " (2's complement overflow)")
 
        -- Test divRat :: Rat -> Rat -> Rat
        -- Division by zero is an error!
        -- Boundary values: zeroRat, one, negone, maxInt, minInt
        -- Representative values: half, two, negtwo
        -- Properties: left zero, right identity, inverse
        -- Interface invariant
        putStrLn "\nTesting divRat"
        putStrLn ("eqRat (divRat zeroRat one) zeroRat == True:      "
                  ++ pass (eqRat (divRat zeroRat one) zeroRat
                           == True))
        putStrLn ("eqRat (divRat one one) one == True:              "
                  ++ pass (eqRat (divRat one one) one == True))
        putStrLn ("eqRat (divRat one negone) negone == True:        "
                  ++ pass (eqRat (divRat one negone) negone == True))
        putStrLn ("eqRat (divRat negone one) negone == True:        "
                  ++ pass (eqRat (divRat negone one) negone == True))
        putStrLn ("eqRat (divRat one two) half = True:              "
                  ++ pass (eqRat (divRat one two) half == True))
        putStrLn ("eqRat (divRat one half) two == True:             "
                  ++ pass (eqRat (divRat one half) two == True))
        putStrLn ("eqRat (divRat half half) one == True:            "
                  ++ pass (eqRat (divRat half half) one == True))
        putStrLn ("eqRat (divRat one zeroRat) maxInt == True:       "
                   ++ pass (eqRat (divRat one zeroRat) maxInt
                            == True))
                 `catch` (\(ErrorCall msg)
                     -> putStrLn ("[Error Call] (EXPECTED)\n...."
                                     ++ msg))
        -- remainder of mulRat tests depend on two's complement Ints
        putStrLn ("(divRat maxInt half) overflows (to -2):          "
                  ++ show (numer (divRat maxInt half)))
        putStrLn ("eqRat (divRat maxInt half) negtwo == True:       "
                  ++ pass (eqRat (divRat maxInt half) negtwo == True)
                  ++ " (2's complement overflow)")
        putStrLn ("(divRat minInt half) overflows (to 0):           "
                  ++ show (numer (divRat minInt half)))
        putStrLn ("eqRat (divRat minInt half) zeroRat == True:      "
                  ++ pass (eqRat (divRat minInt half) zeroRat == True)
                  ++ " (2's complement overflow)")
