{-# LANGUAGE GADTs, PartialTypeSignatures #-}

module Tests_12(runTests, boolTests) where

import Prelude hiding (sequence)
import Template_12(mNumbers, mNum, merge, Stack, isEmpty, empty, pop, push, size,
  runProgram, compile, Instr(..), Expr(..), eval)
import Test.LeanCheck
import Data.List (nub)

-- Generic Setup 
data Test = forall a. Testable a => Test String String a

runTests = flip mapM_ tests
  (\ (Test ex name t) -> putStrLn ("running " ++ ex ++ "(" ++ name ++ ")" ++ "-tests") >> checkFor 1000 t)

-- Tests for this week 
m50 :: [Integer]
m50 = [1,3,7,9,13,21,27,39,49,63,81,91,117,147,169,189,243,273,343,351,441,507,567,637,729,819,1029,1053,1183,1323,1521,1701,1911,2187,2197,2401,2457,3087,3159,3549,3969,4459,4563,5103,5733,6561,6591,7203,7371,8281]

newtype SmallNums = SNum { snum :: Int } deriving (Ord, Eq, Num, Enum)

instance Show SmallNums where
  show = show . snum

instance Listable SmallNums where
  tiers = [map SNum [10..1050]]

-- 1.1

test11a = Test "1.1" "merge [1..n] [1..n] == [1..n]"
  (\ n -> merge [1..snum n] [1.. snum n] == [1..snum n]) 

test11b = Test "1.1" "merge [1,3,5..n] [2,4,6..n] == [1..n]"
  (\ n -> merge [1,3 .. snum n] [2,4 .. snum n] == [1 .. snum n]) 

-- 1.2

test12a = Test "1.2" ("take 50 mNumbers = " ++ show m50) (\ () -> take 50 mNumbers == m50)

-- 1.3
test13a = Test "1.3" ("map mNum [0..49] = " ++ show m50) (\ () -> map mNum [0..49] == m50)
test13b = Test "1.3" ("map mNum [0..79] = take 80 mNumbers") (\ () -> map mNum [0..79] == take 80 mNumbers)

-- 2.1
stackOfList :: [a] -> Stack a
stackOfList = foldl (flip push) empty

instance Listable a => Listable (Stack a) where
  tiers = map (map stackOfList) tiers
  
instance Listable Expr where
  tiers = cons2 Minus \/ cons2 Times \/ [[Var "x", Var "y", Var "z"]] \/ map (map (Num . fromIntegral . snum)) tiers
  
test21a = Test "2.1" "not (isEmpty (push x s))" 
    (\ (x :: Int) s -> not (isEmpty (push x s)))
test21b = Test "2.1" "pop (push x s) = (x, s)"
   (\ (x :: Int) s -> pop (push x s) == (x, s))
test21c = Test "2.1" "isEmpty empty"
   (\ () -> isEmpty empty)
test21d = Test "2.1" "size (push x s) = 1 + size s"
   (\ (x :: Int) s -> size (push x s) == 1 + size s)
   
memoryOf :: [(String,Integer)] -> String -> Integer
memoryOf xs v = case lookup v xs of Just n -> n
   
test22a = Test "2.2" "running example program" ( \ () 
  -> runProgram (memoryOf [("x", 17), ("y", 9)])
       [Const 5, Load "x", Const 3, Subtract, Load "y", Multiply] == -126)

test22b = Test "2.2" "running larger program" ( \ () 
  -> runProgram (memoryOf [("x", 39), ("y", -7), ("z", -8)]) largerProgram == 95040)

largerProgram = [
  Const 30,
  Load "x",
  Subtract,
  Load "y",
  Const 25,
  Multiply,
  Subtract,
  Const 1390,
  Load "x",
  Const 93,
  Subtract,
  Multiply,
  Multiply,
  Const 1,
  Load "z",
  Const 10,
  Subtract,
  Load "y",
  Const 25,
  Subtract,
  Multiply,
  Load "z",
  Const 9,
  Multiply,
  Const 93,
  Subtract,
  Multiply]

test23 = Test "2.3" "runProgram a (compile e) = eval e" 
  (\ x y z e -> let a = memoryOf [("x",x),("y",y),("z",z)]
     in runProgram a (compile e) == eval a e)

tests :: [Test]
tests = [
  test11a,
  test11b,
  test12a,
  test13a,
  test13b,
  test21a,
  test21b,
  test21c,
  test21d,
  test22a,
  test22b,
  test23
  ]

boolTests :: [((String,String), Bool)]
boolTests = map (\ (Test ex n t) -> ((ex,n), holds 1000 t)) tests