{-# LANGUAGE ScopedTypeVariables #-}

{-# OPTIONS_GHC -Wall #-}

module Test.QuickCheck.Classes.Enum
  ( enumLaws
  , boundedEnumLaws
  ) where

import Data.Proxy (Proxy)
import Test.QuickCheck hiding ((.&.))
import Test.QuickCheck.Property (Property)

import Test.QuickCheck.Classes.Internal (Laws(..), myForAllShrink)

-- | Tests the following properties:
--
-- [/Succ Pred Identity/]
--   @'succ' ('pred' x) ≡ x@
-- [/Pred Succ Identity/]
--   @'pred' ('succ' x) ≡ x@
--
-- This only works for @Enum@ types that are not bounded, meaning
-- that 'succ' and 'pred' must be total. This means that these property
-- tests work correctly for types like 'Integer' but not for 'Int'.
--
-- Sadly, there is not a good way to test 'fromEnum' and 'toEnum',
-- since many types that have reasonable implementations for 'succ'
-- and 'pred' have more inhabitants than 'Int' does.
enumLaws :: (Enum a, Eq a, Arbitrary a, Show a) => Proxy a -> Laws
enumLaws :: Proxy a -> Laws
enumLaws Proxy a
p = String -> [(String, Property)] -> Laws
Laws String
"Enum"
  [ (String
"Succ Pred Identity", Proxy a -> Property
forall a.
(Enum a, Eq a, Arbitrary a, Show a) =>
Proxy a -> Property
succPredIdentity Proxy a
p)
  , (String
"Pred Succ Identity", Proxy a -> Property
forall a.
(Enum a, Eq a, Arbitrary a, Show a) =>
Proxy a -> Property
predSuccIdentity Proxy a
p)
  ]

-- | Tests the same properties as 'enumLaws' except that it requires
-- the type to have a 'Bounded' instance. These tests avoid taking the
-- successor of the maximum element or the predecessor of the minimal
-- element.
boundedEnumLaws :: (Enum a, Bounded a, Eq a, Arbitrary a, Show a) => Proxy a -> Laws
boundedEnumLaws :: Proxy a -> Laws
boundedEnumLaws Proxy a
p = String -> [(String, Property)] -> Laws
Laws String
"Enum"
  [ (String
"Succ Pred Identity", Proxy a -> Property
forall a.
(Enum a, Bounded a, Eq a, Arbitrary a, Show a) =>
Proxy a -> Property
succPredBoundedIdentity Proxy a
p)
  , (String
"Pred Succ Identity", Proxy a -> Property
forall a.
(Enum a, Bounded a, Eq a, Arbitrary a, Show a) =>
Proxy a -> Property
predSuccBoundedIdentity Proxy a
p)
  ]

succPredIdentity :: forall a. (Enum a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
succPredIdentity :: Proxy a -> Property
succPredIdentity Proxy a
_ = Bool
-> (a -> Bool)
-> (a -> [String])
-> String
-> (a -> a)
-> String
-> (a -> a)
-> Property
forall a b.
(Arbitrary a, Show b, Eq b) =>
Bool
-> (a -> Bool)
-> (a -> [String])
-> String
-> (a -> b)
-> String
-> (a -> b)
-> Property
myForAllShrink Bool
False (Bool -> a -> Bool
forall a b. a -> b -> a
const Bool
True)
  (\(a
a :: a) -> [String
"a = " String -> String -> String
forall a. [a] -> [a] -> [a]
++ a -> String
forall a. Show a => a -> String
show a
a])
  String
"succ (pred x)"
  (\a
a -> a -> a
forall a. Enum a => a -> a
succ (a -> a
forall a. Enum a => a -> a
pred a
a))
  String
"x"
  (\a
a -> a
a)

predSuccIdentity :: forall a. (Enum a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
predSuccIdentity :: Proxy a -> Property
predSuccIdentity Proxy a
_ = Bool
-> (a -> Bool)
-> (a -> [String])
-> String
-> (a -> a)
-> String
-> (a -> a)
-> Property
forall a b.
(Arbitrary a, Show b, Eq b) =>
Bool
-> (a -> Bool)
-> (a -> [String])
-> String
-> (a -> b)
-> String
-> (a -> b)
-> Property
myForAllShrink Bool
False (Bool -> a -> Bool
forall a b. a -> b -> a
const Bool
True)
  (\(a
a :: a) -> [String
"a = " String -> String -> String
forall a. [a] -> [a] -> [a]
++ a -> String
forall a. Show a => a -> String
show a
a])
  String
"pred (succ x)"
  (\a
a -> a -> a
forall a. Enum a => a -> a
pred (a -> a
forall a. Enum a => a -> a
succ a
a))
  String
"x"
  (\a
a -> a
a)

succPredBoundedIdentity :: forall a. (Enum a, Bounded a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
succPredBoundedIdentity :: Proxy a -> Property
succPredBoundedIdentity Proxy a
_ = Bool
-> (a -> Bool)
-> (a -> [String])
-> String
-> (a -> a)
-> String
-> (a -> a)
-> Property
forall a b.
(Arbitrary a, Show b, Eq b) =>
Bool
-> (a -> Bool)
-> (a -> [String])
-> String
-> (a -> b)
-> String
-> (a -> b)
-> Property
myForAllShrink Bool
False (\a
a -> a
a a -> a -> Bool
forall a. Eq a => a -> a -> Bool
/= a
forall a. Bounded a => a
minBound)
  (\(a
a :: a) -> [String
"a = " String -> String -> String
forall a. [a] -> [a] -> [a]
++ a -> String
forall a. Show a => a -> String
show a
a])
  String
"succ (pred x)"
  (\a
a -> a -> a
forall a. Enum a => a -> a
succ (a -> a
forall a. Enum a => a -> a
pred a
a))
  String
"x"
  (\a
a -> a
a)

predSuccBoundedIdentity :: forall a. (Enum a, Bounded a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
predSuccBoundedIdentity :: Proxy a -> Property
predSuccBoundedIdentity Proxy a
_ = Bool
-> (a -> Bool)
-> (a -> [String])
-> String
-> (a -> a)
-> String
-> (a -> a)
-> Property
forall a b.
(Arbitrary a, Show b, Eq b) =>
Bool
-> (a -> Bool)
-> (a -> [String])
-> String
-> (a -> b)
-> String
-> (a -> b)
-> Property
myForAllShrink Bool
False (\a
a -> a
a a -> a -> Bool
forall a. Eq a => a -> a -> Bool
/= a
forall a. Bounded a => a
maxBound)
  (\(a
a :: a) -> [String
"a = " String -> String -> String
forall a. [a] -> [a] -> [a]
++ a -> String
forall a. Show a => a -> String
show a
a])
  String
"pred (succ x)"
  (\a
a -> a -> a
forall a. Enum a => a -> a
pred (a -> a
forall a. Enum a => a -> a
succ a
a))
  String
"x"
  (\a
a -> a
a)