content: {-# LANGUAGE NegativeLiterals #-}
  {-# LANGUAGE TypeFamilies #-}
  
  {-# OPTIONS_GHC -fplugin=GHC.TypeLits.Extra.Solver #-}
  {-# OPTIONS_GHC -fplugin=GHC.TypeLits.Normalise #-}
  {-# OPTIONS_GHC -fplugin=GHC.TypeLits.KnownNat.Solver #-}
  
  module Clash.Tests.FixedExhaustive (tests) where
  
  import Data.Proxy (Proxy(..))
  import Data.Typeable (typeRep)
  
  import Test.Tasty
  import Test.Tasty.HUnit
  
  import Clash.Sized.Fixed (Fixed(..), FracFixedC, SFixed, UFixed)
  
  listsEqual
    :: forall f rep int frac
     . ( FracFixedC rep int frac
       , f ~ Fixed rep int frac
       )
    => String
    -> [f]
    -> [Rational]
    -> Assertion
  listsEqual prefix fs0 rs0 = do
    let limit = 1000
        minVal = toRational $ minBound @f
        maxVal = toRational $ maxBound @f
        fs = take limit (map toRational fs0)
        rs = take limit $ takeWhile (\r -> r >= minVal && r <= maxVal) rs0
    assertBool (prefix ++ "length rs > maxLength") (length rs < limit)
    assertBool (prefix ++ show fs ++ "\n/=\n" ++ show rs) (fs == rs)
  
  forAllEnumFrom
    :: forall f rep int frac
     . ( FracFixedC rep int frac
       , f ~ Fixed rep int frac
       )
    => Proxy f
    -> Assertion
  forAllEnumFrom Proxy = sequence_
    [ listsEqual ("x1 = Fixed " ++ show (toInteger $ unFixed x1))
                 (enumFrom x1)
                 (enumFrom (toRational x1))
    | x1 :: f <- map Fixed [minBound..]]
  
  forAllEnumFromThen
    :: forall f rep int frac
     . ( FracFixedC rep int frac
       , f ~ Fixed rep int frac
       )
    => Proxy f
    -> Assertion
  forAllEnumFromThen Proxy = sequence_
    [ let fs = enumFromThen x1 x2
          rs = enumFromThen (toRational x1) (toRational x2)
          prefix = unlines [ "x1 = Fixed " ++ show (toInteger $ unFixed x1)
                           , "x2 = Fixed " ++ show (toInteger $ unFixed x2)]
      in if (x1 == x2) then
           listsEqual prefix (take 10 fs) (take 10 rs)
         else
           listsEqual prefix fs rs
    | x1 :: f <- map Fixed [minBound..]
    , x2 <- map Fixed [minBound..]]
  
  forAllEnumFromTo
    :: forall f rep int frac
     . ( FracFixedC rep int frac
       , f ~ Fixed rep int frac
       )
    => Proxy f
    -> Assertion
  forAllEnumFromTo Proxy = sequence_
    [ listsEqual (unlines [ "x1 = Fixed " ++ show (toInteger $ unFixed x1)
                          , "y = Fixed " ++ show (toInteger $ unFixed y)])
                 (enumFromTo x1 y)
                 (enumFromTo (toRational x1) (toRational y))
    | x1 :: f <- map Fixed [minBound..]
    , y <- map Fixed [minBound..]]
  
  forAllEnumFromThenTo
    :: forall f rep int frac
     . ( FracFixedC rep int frac
       , f ~ Fixed rep int frac
       )
    => Proxy f
    -> Assertion
  forAllEnumFromThenTo Proxy = sequence_
    [ let fs = enumFromThenTo x1 x2 y
          rs = enumFromThenTo (toRational x1) (toRational x2) (toRational y)
          prefix = unlines [ "x1 = Fixed " ++ show (toInteger $ unFixed x1)
                           , "x2 = Fixed " ++ show (toInteger $ unFixed x2)
                           , "y = Fixed " ++ show (toInteger $ unFixed y)]
      in if (x1 == x2) then
           listsEqual prefix (take 10 fs) (take 10 rs)
         else
           listsEqual prefix fs rs
    | x1 :: f <- map Fixed [minBound..]
    , x2 <- map Fixed [minBound..]
    , y <- map Fixed [minBound..]]
  
  enumTests
    :: forall f rep int frac
     . ( FracFixedC rep int frac
       , f ~ Fixed rep int frac
       )
    => Proxy f
    -> TestTree
  enumTests pf =
    testGroup (show $ typeRep pf)
      [ testCase "enumFrom" $ forAllEnumFrom pf
      , testCase "enumFromThen" $ forAllEnumFromThen pf
      , testCase "enumFromTo" $ forAllEnumFromTo pf
      , testCase "enumFromThenTo" $ forAllEnumFromThenTo pf ]
  
  tests :: TestTree
  tests =
    testGroup "FixedExhaustive"
      [ enumTests (Proxy @(SFixed 0 0))
      , enumTests (Proxy @(SFixed 0 1))
      , enumTests (Proxy @(SFixed 1 0))
      , enumTests (Proxy @(SFixed 0 2))
      , enumTests (Proxy @(SFixed 1 1))
      , enumTests (Proxy @(SFixed 2 0))
      , enumTests (Proxy @(SFixed 0 3))
      , enumTests (Proxy @(SFixed 1 2))
      , enumTests (Proxy @(SFixed 2 1))
      , enumTests (Proxy @(SFixed 3 0))
      , enumTests (Proxy @(SFixed 0 4))
      , enumTests (Proxy @(SFixed 1 3))
      , enumTests (Proxy @(SFixed 2 2))
      , enumTests (Proxy @(SFixed 3 1))
      , enumTests (Proxy @(SFixed 4 0))
      , enumTests (Proxy @(UFixed 0 0))
      , enumTests (Proxy @(UFixed 0 1))
      , enumTests (Proxy @(UFixed 1 0))
      , enumTests (Proxy @(UFixed 0 2))
      , enumTests (Proxy @(UFixed 1 1))
      , enumTests (Proxy @(UFixed 2 0))
      , enumTests (Proxy @(UFixed 0 3))
      , enumTests (Proxy @(UFixed 1 2))
      , enumTests (Proxy @(UFixed 2 1))
      , enumTests (Proxy @(UFixed 3 0))
      , enumTests (Proxy @(UFixed 0 4))
      , enumTests (Proxy @(UFixed 1 3))
      , enumTests (Proxy @(UFixed 2 2))
      , enumTests (Proxy @(UFixed 3 1))
      , enumTests (Proxy @(UFixed 4 0))
      ]
  