    {-# 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 :: forall a. (Enum a, Eq a, Arbitrary a, Show a) => 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 :: forall a.
    (Enum a, Bounded a, Eq a, Arbitrary a, Show a) =>
    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 :: forall a.
    (Enum a, Eq a, Arbitrary a, Show a) =>
    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 :: forall a.
    (Enum a, Eq a, Arbitrary a, Show a) =>
    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 :: forall a.
    (Enum a, Bounded a, Eq a, Arbitrary a, Show a) =>
    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 :: forall a.
    (Enum a, Bounded a, Eq a, Arbitrary a, Show a) =>
    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)