content: -- !!! Testing the Prelude's Enum instances.
  
  module Main(main) where
  
  import Control.Exception
  import Data.Char
  import Data.Ratio
  
  main = do
     -- Enum Int
    putStrLn "Testing Enum Int: "
    testEnumInt
     -- Enum Integer
    putStrLn "Testing Enum Integer: "
    testEnumInteger
     -- Enum Char
    putStrLn "Testing Enum Char: "
    testEnumChar
     -- Enum ()
    putStrLn "Testing Enum (): "
    testEnumUnit
     -- Enum Ordering
    putStrLn "Testing Enum Ordering (derived): "
    testEnumOrdering
     -- Enum Bool
    putStrLn "Testing Enum Bool: "
    testEnumBool
     -- Enum Rational
    putStrLn "Testing Enum Rational: "
    testEnumRational
     -- Enum (Ratio Int)
    putStrLn "Testing Enum (Ratio Int): "
    testEnumRatioInt
  
  {-
    Here's the properties that's supposed to
    hold for arithmetic sequences over Int:
  
     - [e1..]    = [e1, (e1+1), (e1+2), ..., maxBound]
  
     - [e1,e2..] = [e1, (e1+i), (e1+2*i), ... upper]
                   where
                    i = e2 - e1
                    upper
                     | i >  0 = maxBound
                     | i <  0 = minBound
                     | i == 0 = maxBound -- this really shouldn't matter (I feel.)
     - [e1..e3] = [e1, (e1+i), (e1+2*i),..e3]
                  where
                   i
                    | e3 >= e1 = 1
                    | e3 <  e1 = (-1)
  
     - [e1,e2..e3] = res
                     where
                      i = e2 - e1
  
                      res
                       | i >= 0 && e3 <  e1 = []
                       | i <  0 && e3 >= e1 = []  -- (*)
                       | otherwise          = [e1, (e1+i), (e1 + 2*i), .. e3]
  
     Note:
       (*) - I think this instead should be (i < 0 && e3 > e1), since, as is,
  
              [x,(x+1) ..x] = [x]
              [x,(x-1) ..x] = []
  
             which does not look right, symmetrically speaking.
  
  
     The same properties hold for other Prelude types that
     are instances of Enum as well as being Bounded.
  
     For non-Bounded types (e.g., Float and Double), the properties are similar,
     except that the boundary tests become slightly different, i.e., when an
     element becomes greater than (e3 + i/2) (or less than (e3 + i/2) for negative
     i.)
  
    Q - does [(x::Double)..] have an upper bound? (ditto for Float.)
  
    OK - on with the regression testing.
  -}
  
  
  
  testEnumInt :: IO ()
  testEnumInt = do
       -- succ
    printTest ((succ (0::Int)))
    printTest ((succ (minBound::Int)))
    mayBomb   (printTest ((succ (maxBound::Int))))
  
       -- pred
    printTest (pred (1::Int))
    printTest (pred (maxBound::Int))
    mayBomb   (printTest (pred (minBound::Int)))
  
       -- toEnum
    printTest ((map (toEnum::Int->Int) [1,minBound,maxBound]))
  
       -- fromEnum
    printTest ((map fromEnum [(1::Int),minBound,maxBound]))
  
       -- [x..] aka enumFrom
    printTest ((take 7 [(1::Int)..]))
    printTest ((take 7 [((maxBound::Int)-5)..])) -- just in case it doesn't catch the upper bound..
  
       -- [x,y..] aka enumFromThen
    printTest ((take 7 [(1::Int),2..]))
    printTest ((take 7 [(1::Int),7..]))
    printTest ((take 7 [(1::Int),1..]))
    printTest ((take 7 [(1::Int),0..]))
    printTest ((take 7 [(5::Int),2..]))
    let x = (minBound::Int) + 1
    printTest ((take 7 [x, x-1 ..]))
    let x = (minBound::Int) + 5
    printTest ((take 7 [x, x-1 ..]))
    let x = (maxBound::Int) - 5
    printTest ((take 7 [x, (x+1) ..]))
  
          -- Test overflow conditions
    printTest (([minBound::Int,1..]))
    printTest (([minBound::Int,0..]))
    printTest (([minBound::Int,-1..]))
    printTest (([maxBound::Int,1..]))
    printTest (([maxBound::Int,0..]))
    printTest (([maxBound::Int,-1..]))
  
       -- [x..y] aka enumFromTo
    printTest ((take 7 ([(1::Int) .. 5])))
    printTest ((take 4 ([(1::Int) .. 1])))
    printTest ((take 7 ([(1::Int) .. 0])))
    printTest ((take 7 ([(5::Int) .. 0])))
    printTest ((take 7 ([(maxBound-(5::Int)) .. maxBound])))
    printTest ((take 7 ([(minBound+(5::Int)) .. minBound])))
  
       -- [x,y..z] aka enumFromThenTo
    printTest ((take 7 [(5::Int),4..1]))
    printTest ((take 7 [(5::Int),3..1]))
    printTest ((take 7 [(5::Int),3..2]))
    printTest ((take 7 [(1::Int),2..1]))
    printTest ((take 7 [(2::Int),1..2]))
    printTest ((take 7 [(2::Int),1..1]))
    printTest ((take 7 [(2::Int),3..1]))
  
          -- Test overflow conditions
    printTest (([minBound, 1..maxBound::Int]))
    printTest (([minBound, 0..maxBound::Int]))
    printTest (([minBound,-1..maxBound::Int]))
    printTest (([minBound,-1..maxBound-1::Int]))
    printTest (([minBound,-1..maxBound-2::Int]))
  
    printTest (([maxBound, 1..minBound::Int]))
    printTest (([maxBound, 0..minBound::Int]))
    printTest (([maxBound, 0..minBound+1::Int]))
    printTest (([maxBound, 0..minBound+2::Int]))
    printTest (([maxBound,-1..minBound::Int]))
  
    let x = (maxBound::Int) - 4
    printTest ((take 7 [x,(x+1)..maxBound]))
    let x = (minBound::Int) + 5
    printTest ((take 7 [x,(x-1)..minBound]))
  
  testEnumChar :: IO ()
  testEnumChar = do
       -- succ
    printTest ((succ 'a'))
    printTest ((succ (minBound::Char)))
    mayBomb (printTest ((succ (maxBound::Char))))
  
       -- pred
    printTest ((pred 'b'))
    printTest (pred (maxBound::Char))
    mayBomb (printTest (pred (minBound::Char)))
  
       -- toEnum
    printTest ((map (toEnum::Int->Char) [123,ord (minBound::Char), ord(maxBound::Char)]))
    mayBomb (printTest ((toEnum::Int->Char) (minBound::Int)))
  
       -- fromEnum
    printTest ((map fromEnum ['X',minBound,maxBound]))
  
       -- [x..] aka enumFrom
    -- printTest ((take 7 ['\NUL' .. ]))
    do{ putStr ( "    " ++ "(take 7 ['\\NUL' .. ])" ++ " = " ) ; print (take 7 ['\NUL' .. ]) }
    -- printTest ((take 7 ['\250' .. ]))
    do{ putStr ( "    " ++ "(take 7 ['\\250' .. ])" ++ " = " ) ; print (take 7 ['\250' .. ]) }
  
       -- [x,y..] aka enumFromThen
    printTest ((take 7 ['a','b'..]))
    printTest ((take 7 ['a','e'..]))
    printTest ((take 7 ['a','a'..]))
    printTest ((take 7 ['z','y'..]))
    printTest ((take 7 ['z','v'..]))
    let x = '\1'
    -- printTest ((take 7 ['\1', '\0' ..]))
    do{ putStr ( "    " ++ "(take 7 ['\\1', '\\0' ..])" ++ " = " ) ; print (take 7 ['\1', '\0' ..]) }
    let x = '\5'
    -- printTest ((take 7 ['\5', '\4' ..]))
    do{ putStr ( "    " ++ "(take 7 ['\\5', '\\4' ..])" ++ " = " ) ; print (take 7 ['\5', '\4' ..]) }
    let x = (maxBound::Int) - 5
    -- printTest ((take 7 ['\250', '\251' ..]))
    do{ putStr ( "    " ++ "(take 7 ['\\250', '\\251' ..])" ++ " = " ) ; print (take 7 ['\250', '\251' ..]) }
  
       -- [x..y] aka enumFromTo
    printTest ((take 7 (['a' .. 'e'])))
    printTest ((take 4 (['a' .. 'a'])))
    printTest ((take 7 (['b' .. 'a'])))
    printTest ((take 7 (['e' .. 'a'])))
    -- printTest ((take 7 (['\250' .. '\255'])))
    do{ putStr ( "    " ++ "(take 7 (['\\250' .. '\\255']))" ++ " = " ) ; print (take 7 (['\250' .. '\255'])) }
    -- printTest ((take 7 (['\5' .. '\0'])))
    do{ putStr ( "    " ++ "(take 7 (['\\5' .. '\\0']))" ++ " = " ) ; print (take 7 (['\5' .. '\0'])) }
  
       -- [x,y..z] aka enumFromThenTo
    printTest ((take 7 ['f','e' .. 'b']))
    printTest ((take 7 ['g','e' .. 'b']))
    printTest ((take 7 ['g','d' .. 'c']))
    printTest ((take 7 ['b','c' .. 'b']))
    printTest ((take 7 ['c','b' .. 'c']))
    printTest ((take 7 ['c','b' .. 'b']))
    printTest ((take 7 ['c','d' .. 'b']))
    -- printTest ((take 7 ['\251', '\252' .. maxBound]))
    do{ putStr ( "    " ++ "(take 7 ['\\251', '\\252' .. maxBound])" ++ " = " ) ; print (take 7 ['\251', '\252' .. maxBound]) }
    -- printTest ((take 7 ['\5', '\4' .. minBound]))
    do{ putStr ( "    " ++ "(take 7 ['\\5', '\\4' .. minBound])" ++ " = " ) ; print (take 7 ['\5', '\4' .. minBound]) }
  
  
  testEnumUnit :: IO ()
  testEnumUnit = do
     -- succ:
    mayBomb (printTest ((succ ())))
    mayBomb (printTest ((succ (minBound::()))))
    mayBomb (printTest ((succ (maxBound::()))))
  
     -- pred:
    mayBomb (printTest ((pred ())))
    mayBomb (printTest ((pred (minBound::()))))
    mayBomb (printTest ((pred (maxBound::()))))
  
     -- toEnum:
    printTest ((toEnum 0)::())
    mayBomb   (printTest ((toEnum 1)::()))
  
     -- fromEnum:
    printTest ((fromEnum ()))
  
     -- enumFrom:
    printTest ((take 7 [()..]))
  
     -- enumFromThen:
    printTest ((take 7 [(),()..]))
  
     -- enumFromTo
    printTest ((take 7 [()..()]))
  
     -- enumFromThenTo
    printTest ((take 7 [(),()..()]))
  
  testEnumOrdering :: IO ()
  testEnumOrdering = do
     -- succ:
    printTest ((succ LT))
    printTest ((succ (minBound::Ordering)))
    mayBomb (printTest ((succ (maxBound::Ordering))))
  
     -- pred:
    printTest ((pred GT))
    printTest ((pred (maxBound::Ordering)))
    mayBomb (printTest ((pred (minBound::Ordering))))
  
     -- toEnum:
    printTest ((toEnum 0)::Ordering)
    mayBomb   (printTest ((toEnum 5)::Ordering))
  
     -- fromEnum:
    printTest ((fromEnum LT))
    printTest ((fromEnum EQ))
    printTest ((fromEnum GT))
  
     -- enumFrom:
    printTest (([LT ..]))
    printTest (([EQ ..]))
    printTest (([GT ..]))
  
     -- enumFromThen:
    printTest (([LT,EQ ..]))
    printTest (([EQ,GT ..]))
    printTest (([EQ,LT ..]))
    printTest (([LT,GT ..]))
    printTest (([GT,LT ..]))
    printTest (take 7 (([GT,GT ..])))
    printTest (take 7 (([LT,LT ..])))
  
     -- enumFromTo
    printTest (([LT .. GT]))
    printTest (([LT .. EQ]))
    printTest (([LT .. LT]))
    printTest (([GT .. LT]))
    printTest (([GT .. EQ]))
    printTest (([GT .. GT]))
  
     -- enumFromThenTo
    printTest (([LT,EQ .. GT]))
    printTest (([GT,EQ .. LT]))
    printTest (([GT,EQ .. EQ]))
    printTest (([GT,EQ .. GT]))
    printTest (([GT,EQ .. LT]))
    printTest (([LT,EQ .. LT]))
    printTest (([LT,EQ .. GT]))
    printTest (take 7 (([LT,LT .. GT])))
    printTest (take 7 (([GT,GT .. LT])))
  
  testEnumBool :: IO ()
  testEnumBool = do
     -- succ:
    printTest ((succ False))
    printTest ((succ (minBound::Bool)))
    mayBomb (printTest ((succ (maxBound::Bool))))
  
     -- pred:
    printTest ((pred True))
    printTest ((pred (maxBound::Bool)))
    mayBomb (printTest ((pred (minBound::Bool))))
  
     -- toEnum:
    printTest ((toEnum 0)::Bool)
    mayBomb   (printTest ((toEnum 5)::Bool))
  
     -- fromEnum:
    printTest ((fromEnum False))
    printTest ((fromEnum True))
  
     -- enumFrom:
    printTest (([False ..]))
    printTest (([True ..]))
  
     -- enumFromThen:
    printTest (([False,True ..]))
    printTest (([True,False ..]))
    printTest ((take 7 ([False,False ..])))
    printTest ((take 7 ([True,True ..])))
  
     -- enumFromTo
    printTest (([False .. True]))
    printTest (([True .. False]))
  
     -- enumFromThenTo
    printTest (take 7 ([False,False .. False]))
    printTest (take 7 ([False,False .. True]))
    printTest (take 7 ([False,True .. False]))
    printTest (take 7 ([False,True .. True]))
    printTest (take 7 ([True,False .. False]))
    printTest (take 7 ([True,False .. True]))
    printTest (take 7 ([True,True .. False]))
    printTest (take 7 ([True,True .. True]))
  
  
  testEnumInteger :: IO ()
  testEnumInteger = do
       -- succ
    printTest ((succ (0::Integer)))
    printTest ((succ ((-1)::Integer)))
  
       -- pred
    printTest (pred (1::Integer))
    printTest (pred (0::Integer))
  
       -- toEnum
    printTest ((map (toEnum::Int->Integer) [1,minBound,maxBound]))
  
       -- fromEnum
    printTest ((map fromEnum [(1::Integer),42,45]))
  
       -- [x..] aka enumFrom
    printTest ((take 7 [(1::Integer)..]))
    printTest ((take 7 [(-5::Integer)..]))
  
       -- [x,y..] aka enumFromThen
    printTest ((take 7 [(1::Integer),2..]))
    printTest ((take 7 [(1::Integer),7..]))
    printTest ((take 7 [(1::Integer),1..]))
    printTest ((take 7 [(1::Integer),0..]))
    printTest ((take 7 [(5::Integer),2..]))
  
       -- [x..y] aka enumFromTo
    printTest ((take 7 ([(1::Integer) .. 5])))
    printTest ((take 4 ([(1::Integer) .. 1])))
    printTest ((take 7 ([(1::Integer) .. 0])))
    printTest ((take 7 ([(5::Integer) .. 0])))
  
       -- [x,y..z] aka enumFromThenTo
    printTest ((take 7 [(5::Integer),4..1]))
    printTest ((take 7 [(5::Integer),3..1]))
    printTest ((take 7 [(5::Integer),3..2]))
    printTest ((take 7 [(1::Integer),2..1]))
    printTest ((take 7 [(2::Integer),1..2]))
    printTest ((take 7 [(2::Integer),1..1]))
    printTest ((take 7 [(2::Integer),3..1]))
  
  testEnumRational :: IO ()
  testEnumRational = do
       -- succ
    printTest ((succ (0::Rational)))
    printTest ((succ ((-1)::Rational)))
  
       -- pred
    printTest (pred (1::Rational))
    printTest (pred (0::Rational))
  
       -- toEnum
    printTest ((map (toEnum::Int->Rational) [1,minBound,maxBound]))
  
       -- fromEnum
    printTest ((map fromEnum [(1::Rational),42,45]))
  
       -- [x..] aka enumFrom
    printTest ((take 7 [(1::Rational)..]))
    printTest ((take 7 [(-5::Rational)..]))
  
       -- [x,y..] aka enumFromThen
    printTest ((take 7 [(1::Rational),2..]))
    printTest ((take 7 [(1::Rational),7..]))
    printTest ((take 7 [(1::Rational),1..]))
    printTest ((take 7 [(1::Rational),0..]))
    printTest ((take 7 [(5::Rational),2..]))
  
       -- [x..y] aka enumFromTo
    printTest ((take 7 ([(1::Rational) .. 5])))
    printTest ((take 4 ([(1::Rational) .. 1])))
    printTest ((take 7 ([(1::Rational) .. 0])))
    printTest ((take 7 ([(5::Rational) .. 0])))
  
       -- [x,y..z] aka enumFromThenTo
    printTest ((take 7 [(5::Rational),4..1]))
    printTest ((take 7 [(5::Rational),3..1]))
    printTest ((take 7 [(5::Rational),3..2]))
    printTest ((take 7 [(1::Rational),2..1]))
    printTest ((take 7 [(2::Rational),1..2]))
    printTest ((take 7 [(2::Rational),1..1]))
    printTest ((take 7 [(2::Rational),3..1]))
  
  testEnumRatioInt :: IO ()
  testEnumRatioInt = do
       -- succ
    printTest ((succ (0::Ratio Int)))
    printTest ((succ ((-1)::Ratio Int)))
  
       -- pred
    printTest (pred (1::Ratio Int))
    printTest (pred (0::Ratio Int))
  
       -- toEnum
    printTest ((map (toEnum::Int->Ratio Int) [1,minBound,maxBound]))
  
       -- fromEnum
    printTest ((map fromEnum [(1::Ratio Int),42,45]))
  
       -- [x..] aka enumFrom
    printTest ((take 7 [(1::Ratio Int)..]))
    printTest ((take 7 [(-5::Ratio Int)..]))
    printTest ((take 7 [((toEnum ((maxBound::Int)-5))::Ratio Int)..]))
  
       -- [x,y..] aka enumFromThen
    printTest ((take 7 [(1::Ratio Int),2..]))
    printTest ((take 7 [(1::Ratio Int),7..]))
    printTest ((take 7 [(1::Ratio Int),1..]))
    printTest ((take 7 [(1::Ratio Int),0..]))
    printTest ((take 7 [(5::Ratio Int),2..]))
    let x = (toEnum ((minBound::Int) + 1))::Ratio Int
    printTest ((take 7 [x, x-1 ..]))
    let x = (toEnum ((minBound::Int) + 5))::Ratio Int
    printTest ((take 7 [x, x-1 ..]))
    let x = (toEnum ((maxBound::Int) - 5))::Ratio Int
    printTest ((take 7 [x, (x+1) ..]))
  
       -- [x..y] aka enumFromTo
    printTest ((take 7 ([(1::Ratio Int) .. 5])))
    printTest ((take 4 ([(1::Ratio Int) .. 1])))
    printTest ((take 7 ([(1::Ratio Int) .. 0])))
    printTest ((take 7 ([(5::Ratio Int) .. 0])))
    let x = (toEnum (maxBound - (5::Int))) :: Ratio Int
    let y = (toEnum (maxBound::Int)) :: Ratio Int
    printTest ((take 7 ([x..y])))
    let x = (toEnum (minBound + (5::Int))) :: Ratio Int
    let y = (toEnum (minBound::Int)) :: Ratio Int
    printTest ((take 7 ([x..y])))
  
       -- [x,y..z] aka enumFromThenTo
    printTest ((take 7 [(5::Ratio Int),4..1]))
    printTest ((take 7 [(5::Ratio Int),3..1]))
    printTest ((take 7 [(5::Ratio Int),3..2]))
    printTest ((take 7 [(1::Ratio Int),2..1]))
    printTest ((take 7 [(2::Ratio Int),1..2]))
    printTest ((take 7 [(2::Ratio Int),1..1]))
    printTest ((take 7 [(2::Ratio Int),3..1]))
  
    let x = (toEnum ((maxBound::Int) - 4)) :: Ratio Int
    let y = (toEnum (maxBound::Int)) :: Ratio Int
    printTest ((take 7 [x,(x+1)..y]))
    let x = (toEnum ((minBound::Int) + 5)) :: Ratio Int
    let y = (toEnum (minBound::Int)) :: Ratio Int
    printTest ((take 7 [x,(x-1)..y]))
  
  --
  --
  --  Utils
  --
  --
  
  
  mayBomb x = catch x (\(ErrorCall e) -> putStrLn ("error " ++ show e))
     `catch` (\e -> putStrLn ("Fail: " ++ show (e :: SomeException)))
  
  test :: Show a => String -> String -> a -> IO ()
  test test_nm expected val = do
     putStr test_nm
     if expected == got then
        putStrLn ": SUCCEEDED"
      else do
        putStr   ": FAILED"
        putStrLn ("( expected: " ++ show expected ++ " , got: " ++ show got ++ " )")
    where
     got = show val
  