{-# LANGUAGE ScopedTypeVariables #-}
module VersionNum where


import Data.Proxy
import Test.Tasty as Tasty
import Test.Tasty.QuickCheck as Tasty


numTest :: (Num a, Eq a, Arbitrary a, Show a) => Proxy a -> TestTree
numTest p = testGroup "Version's Num instance" $
    ($p) <$> [plusTest, minusTest, multTest, negateTest, absSignumTest]

plusTest :: forall a. (Num a, Eq a, Arbitrary a, Show a) => Proxy a -> TestTree
plusTest _ = testGroup "Plus laws"
    [ plusAssociative
    , plusCommutative
    , plusIdentity
    ]
  where plusAssociative = testProperty "Associative (+)" go
          where go a b c = (a :: a) + (b + c) == (a + b) + c
        plusCommutative = testProperty "Commutative (+)" go
          where go a b = (a :: a) + b == b + a
        plusIdentity = testProperty "Identity (+)" go
          where go a = (a :: a) + fromInteger 0 == a

minusTest :: forall a. (Num a, Eq a, Arbitrary a, Show a) => Proxy a -> TestTree
minusTest _ = testGroup "Minus laws"
  [ testProperty "Minus itself == 0" $ \a -> (a :: a) - a == fromInteger 0
  , testProperty "Minus 0 == itself" $ \a -> (a :: a) - fromInteger 0 == a
  ]

multTest :: forall a. (Num a, Eq a, Arbitrary a, Show a) => Proxy a -> TestTree
multTest _ = testGroup "Multiplication laws"
    [ multAssociative
    , multCommutative
    , multIdentity
    ]
  where multAssociative = testProperty "Associative (*)" go
          where go a b c = (a :: a) * (b * c) == (a * b) * c
        multCommutative = testProperty "Commutative (*)" go
          where go a b = (a :: a) * b == b * a
        multIdentity = testProperty "Identity (*)" go
          where go a = (a :: a) * fromInteger 1 == a

negateTest :: forall a. (Num a, Eq a, Arbitrary a, Show a) => Proxy a -> TestTree
negateTest _ = testGroup "Negate laws"
    [ negateToZero
    , doubleNegate
    ]
  where negateToZero = testProperty "Self added to negated self == identity" $
                            \a -> (a :: a) + negate a == fromInteger 0
        doubleNegate = testProperty "Double negation is original" $
                            \a -> negate (negate a) == (a :: a)

absSignumTest :: forall a. (Num a, Eq a, Arbitrary a, Show a) => Proxy a -> TestTree
absSignumTest _ = testProperty "Absolute * signum is original" $
    \a -> abs a * signum a == (a :: a)
