packages feed

i-0.1: test/I/Test/Integer.hs

{-# LANGUAGE AllowAmbiguousTypes #-}

module I.Test.Integer (tt) where

import Control.Monad
import Data.Constraint
import Data.Proxy
import Data.Type.Ord
import Hedgehog (failure, forAll, property, assert, (===), (/==))
import Hedgehog.Gen qualified as Gen
import KindInteger (N, P)
import KindInteger qualified as KI
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (testCase, (@=?))
import Test.Tasty.Hedgehog (testProperty)

import I (I)
import I qualified

import I.Test.Support

--------------------------------------------------------------------------------

tt :: TestTree
tt = testGroup "Integer"
  [ testProperty "wrap" $ property $ do
      x <- forAll genInteger
      x === I.unwrap (I.wrap x)

  , tt'lr @(N 100) @(N 100)
  , tt'lr @(N 100) @(N 10)
  , tt'lr @(N 100) @(N 1)
  , tt'lr @(N 100) @(N 0)
  , tt'lr @(N 1)   @(N 1)
  , tt'lr @(N 1)   @(P 0)
  , tt'lr @(P 0)   @(P 0)
  , tt'lr @(P 0)   @(P 1)
  , tt'lr @(P 1)   @(P 1)
  , tt'lr @(P 0)   @(P 100)
  , tt'lr @(P 1)   @(P 100)
  , tt'lr @(P 10)  @(P 100)
  , tt'lr @(P 100) @(P 100)

  , tt'lu  @(N 100)
  , tt'lu  @(N 1)
  , tt'lu  @(P 0)
  , tt'lu  @(P 1)
  , tt'lu  @(P 100)

  , tt'ur  @(N 100)
  , tt'ur  @(N 1)
  , tt'ur  @(P 0)
  , tt'ur  @(P 1)
  , tt'ur  @(P 100)

  , tt'uu
  ]

tt'lr
  :: forall (l :: KI.Integer) (r :: KI.Integer)
  .  I.Interval Integer ('Just l) ('Just r)
  => TestTree
tt'lr = testGroup ("Interval [" <> show l <> ", " <> show r <> "]")
  $ concat
  [ pure $ testProperty "from" $ property $ do
      x <- forAll genInteger
      case I.from @Integer @('Just l) @('Just r) x of
        Nothing -> assert (x < l' || x > r')
        Just y -> do assert (x >= l' && x <= r')
                     I.unwrap y === x

  , pure $ testProperty "shove" $ property $ do
      x <- forAll genInteger
      let y = I.shove @Integer @('Just l) @('Just r) x
      I.from (I.unwrap y) === Just y
      if x < l' || x > r'
         then I.from @Integer @('Just l) @('Just r) x === Nothing
         else I.from @Integer @('Just l) @('Just r) x /== Nothing

  , pure $ testProperty "plus'" $ property $ do
      a <- forAll $ genIInteger @('Just l) @('Just r)
      b <- forAll $ genIInteger @('Just l) @('Just r)
      let x = toInteger (I.unwrap a) + toInteger (I.unwrap b)
      case I.plus' a b of
        Nothing -> assert (x < l'' || x > r'')
        Just y -> toInteger (I.unwrap y) === x

  , pure $ testProperty "mult'" $ property $ do
      a <- forAll $ genIInteger @('Just l) @('Just r)
      b <- forAll $ genIInteger @('Just l) @('Just r)
      let x = toInteger (I.unwrap a) * toInteger (I.unwrap b)
      case I.mult' a b of
        Nothing -> assert (x < l'' || x > r'')
        Just y -> toInteger (I.unwrap y) === x


  , pure $ testProperty "minus'" $ property $ do
      a <- forAll $ genIInteger @('Just l) @('Just r)
      b <- forAll $ genIInteger @('Just l) @('Just r)
      let x = toInteger (I.unwrap a) - toInteger (I.unwrap b)
      case I.minus' a b of
        Nothing -> assert (x < l'' || x > r'')
        Just y -> toInteger (I.unwrap y) === x

  , if (l' == 0 && r' == 0) then mzero else
    pure $ testProperty "div'" $ property $ do
      a <- forAll $ genIInteger @('Just l) @('Just r)
      b <- forAll $ Gen.filter (\x -> I.unwrap x /= 0)
                               (genIInteger @('Just l) @('Just r))
      let (q, m) = toInteger (I.unwrap a) `divMod` toInteger (I.unwrap b)
      case I.div' a b of
        Nothing -> assert (q < l'' || q > r'' || m /= 0)
        Just y -> do q === toInteger (I.unwrap y)
                     m === 0

  , pure $ testProperty "clamp'" $ property $ do
      x <- forAll $ genInteger
      case I.clamp @Integer @('Just l) @('Just r) x of
        y | x < l' -> I.unwrap y === l'
          | x > r' -> I.unwrap y === r'
          | otherwise -> Just y === I.from x

  , pure $ testProperty "with" $ property $ do
      x <- forAll $ genIInteger @('Just l) @('Just r)
      x === I.with x I.known'


  , case KI.cmpInteger (Proxy @l) (Proxy @r) of
      LTI ->
        [ testProperty "pred'" $ property $ do
            x <- forAll $ genIInteger @('Just l) @('Just r)
            case I.pred' x of
              Nothing -> x === l
              Just y -> do x /== l
                           I.unwrap y === I.unwrap x - 1

        , testProperty "succ'" $ property $ do
            x <- forAll $ genIInteger @('Just l) @('Just r)
            case I.succ' x of
              Nothing -> x === r
              Just y -> do x /== r
                           I.unwrap y === I.unwrap x + 1
        ]
      _ -> mzero

  , case (leInteger @l @(P 0), leInteger @(P 0) @r) of
      (Just Dict, Just Dict) -> pure $ testCase "zero" $ do
        0 @=? I.unwrap (I.zero @Integer @('Just l) @('Just r))
      _ -> mzero


  , case (leInteger @l @(P 1), leInteger @(P 1) @r) of
      (Just Dict, Just Dict) -> pure $ testCase "one" $ do
        1 @=? I.unwrap (I.one @Integer @('Just l) @('Just r))
      _ -> mzero

  , pure $ testProperty "negate'" $ property $ do
      x <- forAll $ genIInteger @('Just l) @('Just r)
      case I.negate' x of
        Just y -> Just x === I.negate' y
        Nothing -> Nothing === I.from @Integer @('Just l) @('Just r)
                                      (negate (toInteger (I.unwrap x)))

  , withDict (negateInteger @r) $
      case (leInteger @l @(P 0), leInteger @(P 0) @r) of
        (Just Dict, Just Dict) ->
          case KI.cmpInteger (Proxy @l) (Proxy @(KI.Negate r)) of
            EQI -> pure $ testProperty "negate" $ property $ do
              x <- forAll $ genIInteger @('Just l) @('Just r)
              Just (I.negate x) === I.negate' x
            _ -> mzero
        _ -> mzero

  , pure $ testProperty "down" $ property $ do
      x <- forAll $ genIInteger @('Just l) @('Just r)
      Just x === I.down x
      case I.down x of
        Nothing -> failure
        Just y -> I.unwrap x
              === I.unwrap (y :: I Integer (I.MinL Integer) (I.MaxR Integer))

  , pure $ testProperty "up" $ property $ do
      x <- forAll $ genIInteger @('Just l) @('Just r)
      x === I.up x
      I.unwrap x ===
        I.unwrap (I.up x :: I Integer (I.MinL Integer) (I.MaxR Integer))

  ]
  where
    l   = I.min        :: I Integer ('Just l) ('Just r)
    l'  = I.unwrap l   :: Integer
    l'' = toInteger l' :: Integer
    r   = I.max        :: I Integer ('Just l) ('Just r)
    r'  = I.unwrap r   :: Integer
    r'' = toInteger r' :: Integer


tt'lu
  :: forall (l :: KI.Integer)
  .  I.Interval Integer ('Just l) 'Nothing
  => TestTree
tt'lu = testGroup ("Interval [" <> show l <> ", infinity)")
  $ concat
  [ pure $ testProperty "from" $ property $ do
      x <- forAll genInteger
      case I.from @Integer @('Just l) @'Nothing x of
        Nothing -> assert (x < l')
        Just y -> do assert (x >= l')
                     I.unwrap y === x

  , pure $ testProperty "shove" $ property $ do
      x <- forAll genInteger
      let y = I.shove @Integer @('Just l) @'Nothing x
      I.from (I.unwrap y) === Just y
      if x < l'
         then I.from @Integer @('Just l) @'Nothing x === Nothing
         else I.from @Integer @('Just l) @'Nothing x /== Nothing

  , pure $ testProperty "plus'" $ property $ do
      a <- forAll $ genIInteger @('Just l) @'Nothing
      b <- forAll $ genIInteger @('Just l) @'Nothing
      let x = toInteger (I.unwrap a) + toInteger (I.unwrap b)
      case I.plus' a b of
        Nothing -> assert (x < l'')
        Just y -> toInteger (I.unwrap y) === x

  , case leInteger @(P 0) @l of
      Nothing -> mzero
      Just Dict -> pure $ testProperty "plus" $ property $ do
        a <- forAll $ genIInteger @('Just l) @'Nothing
        b <- forAll $ genIInteger @('Just l) @'Nothing
        Just (I.plus a b) === I.plus' a b

  , pure $ testProperty "mult'" $ property $ do
      a <- forAll $ genIInteger @('Just l) @'Nothing
      b <- forAll $ genIInteger @('Just l) @'Nothing
      let x = toInteger (I.unwrap a) * toInteger (I.unwrap b)
      case I.mult' a b of
        Nothing -> assert (x < l'')
        Just y -> toInteger (I.unwrap y) === x

  , case leInteger @(P 0) @l of
      Nothing -> mzero
      Just Dict -> pure $ testProperty "mult" $ property $ do
        a <- forAll $ genIInteger @('Just l) @'Nothing
        b <- forAll $ genIInteger @('Just l) @'Nothing
        Just (I.mult a b) === I.mult' a b

  , pure $ testProperty "minus'" $ property $ do
      a <- forAll $ genIInteger @('Just l) @'Nothing
      b <- forAll $ genIInteger @('Just l) @'Nothing
      let x = toInteger (I.unwrap a) - toInteger (I.unwrap b)
      case I.minus' a b of
        Nothing -> assert (x < l'')
        Just y -> toInteger (I.unwrap y) === x

  , if (l' == 0) then mzero else
    pure $ testProperty "div'" $ property $ do
      a <- forAll $ genIInteger @('Just l) @'Nothing
      b <- forAll $ Gen.filter (\x -> I.unwrap x /= 0)
                               (genIInteger @('Just l) @'Nothing)
      let (q, m) = toInteger (I.unwrap a) `divMod` toInteger (I.unwrap b)
      case I.div' a b of
        Nothing -> assert (q < l'' || m /= 0)
        Just y -> do q === toInteger (I.unwrap y)
                     m === 0

  , pure $ testProperty "clamp'" $ property $ do
      x <- forAll $ genInteger
      case I.clamp @Integer @('Just l) @'Nothing x of
        y | x < l' -> I.unwrap y === l'
          | otherwise -> Just y === I.from x

  , pure $ testProperty "with" $ property $ do
      x <- forAll $ genIInteger @('Just l) @'Nothing
      x === I.with x I.known'

  , pure $ testProperty "pred'" $ property $ do
      x <- forAll $ genIInteger @('Just l) @'Nothing
      case I.pred' x of
        Nothing -> x === l
        Just y -> do x /== l
                     I.unwrap y === I.unwrap x - 1

  , pure $ testProperty "succ'" $ property $ do
      x <- forAll $ genIInteger @('Just l) @'Nothing
      case I.succ' x of
        Nothing -> failure
        Just y -> do I.unwrap y === I.unwrap x + 1

  , pure $ testProperty "succ" $ property $ do
      x <- forAll $ genIInteger @('Just l) @'Nothing
      Just (I.succ x) === I.succ' x

  , case leInteger @l @(P 0) of
      Just Dict -> pure $ testCase "zero" $ do
        0 @=? I.unwrap (I.zero @Integer @('Just l) @'Nothing)
      _ -> mzero

  , case leInteger @l @(P 1) of
      Just Dict -> pure $ testCase "one" $ do
        1 @=? I.unwrap (I.one @Integer @('Just l) @'Nothing)
      _ -> mzero

  , pure $ testProperty "down" $ property $ do
      x <- forAll $ genIInteger @('Just l) @'Nothing
      Just x === I.down x
      case I.down x of
        Nothing -> failure
        Just y -> I.unwrap x
              === I.unwrap (y :: I Integer (I.MinL Integer) (I.MaxR Integer))

  , pure $ testProperty "up" $ property $ do
      x <- forAll $ genIInteger @('Just l) @'Nothing
      x === I.up x
      I.unwrap x ===
        I.unwrap (I.up x :: I Integer (I.MinL Integer) (I.MaxR Integer))

  ]
  where
    l   = I.min        :: I Integer ('Just l) 'Nothing
    l'  = I.unwrap l   :: Integer
    l'' = toInteger l' :: Integer

tt'ur
  :: forall (r :: KI.Integer)
  .  I.Interval Integer 'Nothing ('Just r)
  => TestTree
tt'ur = testGroup ("Interval (-infinity, " <> show r <> "]")
  $ concat
  [ pure $ testProperty "from" $ property $ do
      x <- forAll genInteger
      case I.from @Integer @'Nothing @('Just r) x of
        Nothing -> assert (x > r')
        Just y -> do assert (x <= r')
                     I.unwrap y === x

  , pure $ testProperty "shove" $ property $ do
      x <- forAll genInteger
      let y = I.shove @Integer @'Nothing @('Just r) x
      I.from (I.unwrap y) === Just y
      if x > r'
         then I.from @Integer @'Nothing @('Just r) x === Nothing
         else I.from @Integer @'Nothing @('Just r) x /== Nothing

  , pure $ testProperty "plus'" $ property $ do
      a <- forAll $ genIInteger @'Nothing @('Just r)
      b <- forAll $ genIInteger @'Nothing @('Just r)
      let x = toInteger (I.unwrap a) + toInteger (I.unwrap b)
      case I.plus' a b of
        Nothing -> assert (x > r'')
        Just y -> toInteger (I.unwrap y) === x

  , case leInteger @r @(P 0) of
      Nothing -> mzero
      Just Dict -> pure $ testProperty "plus" $ property $ do
        a <- forAll $ genIInteger @'Nothing @('Just r)
        b <- forAll $ genIInteger @'Nothing @('Just r)
        Just (I.plus a b) === I.plus' a b

  , pure $ testProperty "mult'" $ property $ do
      a <- forAll $ genIInteger @'Nothing @('Just r)
      b <- forAll $ genIInteger @'Nothing @('Just r)
      let x = toInteger (I.unwrap a) * toInteger (I.unwrap b)
      case I.mult' a b of
        Nothing -> assert (x > r'')
        Just y -> toInteger (I.unwrap y) === x

  , pure $ testProperty "minus'" $ property $ do
      a <- forAll $ genIInteger @'Nothing @('Just r)
      b <- forAll $ genIInteger @'Nothing @('Just r)
      let x = toInteger (I.unwrap a) - toInteger (I.unwrap b)
      case I.minus' a b of
        Nothing -> assert (x > r'')
        Just y -> toInteger (I.unwrap y) === x

  , if (r' == 0) then mzero else
    pure $ testProperty "div'" $ property $ do
      a <- forAll $ genIInteger @'Nothing @('Just r)
      b <- forAll $ Gen.filter (\x -> I.unwrap x /= 0)
                               (genIInteger @'Nothing @('Just r))
      let (q, m) = toInteger (I.unwrap a) `divMod` toInteger (I.unwrap b)
      case I.div' a b of
        Nothing -> assert (q > r'' || m /= 0)
        Just y -> do q === toInteger (I.unwrap y)
                     m === 0

  , pure $ testProperty "clamp'" $ property $ do
      x <- forAll $ genInteger
      case I.clamp @Integer @'Nothing @('Just r) x of
        y | x > r' -> I.unwrap y === r'
          | otherwise -> Just y === I.from x

  , pure $ testProperty "with" $ property $ do
      x <- forAll $ genIInteger @'Nothing @('Just r)
      x === I.with x I.known'

  , pure $ testProperty "pred'" $ property $ do
      x <- forAll $ genIInteger @'Nothing @('Just r)
      case I.pred' x of
        Nothing -> failure
        Just y -> I.unwrap y === I.unwrap x - 1

  , pure $ testProperty "succ'" $ property $ do
      x <- forAll $ genIInteger @'Nothing @('Just r)
      case I.succ' x of
        Nothing -> x === r
        Just y -> do I.unwrap y === I.unwrap x + 1

  , pure $ testProperty "pred" $ property $ do
      x <- forAll $ genIInteger @'Nothing @('Just r)
      Just (I.pred x) === I.pred' x

  , case leInteger @(P 0) @r of
      Just Dict -> pure $ testCase "zero" $ do
        0 @=? I.unwrap (I.zero @Integer @'Nothing @('Just r))
      _ -> mzero

  , case leInteger @(P 1) @r of
      Just Dict -> pure $ testCase "one" $ do
        1 @=? I.unwrap (I.one @Integer @'Nothing @('Just r))
      _ -> mzero

  , pure $ testProperty "down" $ property $ do
      x <- forAll $ genIInteger @'Nothing @('Just r)
      Just x === I.down x
      case I.down x of
        Nothing -> failure
        Just y -> I.unwrap x
              === I.unwrap (y :: I Integer (I.MinL Integer) (I.MaxR Integer))

  , pure $ testProperty "up" $ property $ do
      x <- forAll $ genIInteger @'Nothing @('Just r)
      x === I.up x
      I.unwrap x ===
        I.unwrap (I.up x :: I Integer (I.MinL Integer) (I.MaxR Integer))

  ]
  where
    r   = I.max        :: I Integer 'Nothing ('Just r)
    r'  = I.unwrap r   :: Integer
    r'' = toInteger r' :: Integer

tt'uu :: TestTree
tt'uu = testGroup "Interval (-infinity, +infinity)"
  $ concat
  [ pure $ testProperty "from" $ property $ do
      x <- forAll genInteger
      case I.from @Integer @'Nothing @'Nothing x of
        Nothing -> failure
        Just y -> I.unwrap y === x

  , pure $ testProperty "shove" $ property $ do
      x <- forAll genInteger
      let y = I.shove @Integer @'Nothing @'Nothing x
      I.from (I.unwrap y) === Just y
      I.from @Integer @'Nothing @'Nothing x /== Nothing

  , pure $ testProperty "plus'" $ property $ do
      a <- forAll $ genIInteger @'Nothing @'Nothing
      b <- forAll $ genIInteger @'Nothing @'Nothing
      let x = toInteger (I.unwrap a) + toInteger (I.unwrap b)
      case I.plus' a b of
        Nothing -> failure
        Just y -> toInteger (I.unwrap y) === x

  , pure $ testProperty "plus" $ property $ do
      a <- forAll $ genIInteger @'Nothing @'Nothing
      b <- forAll $ genIInteger @'Nothing @'Nothing
      Just (I.plus a b) === I.plus' a b

  , pure $ testProperty "mult'" $ property $ do
      a <- forAll $ genIInteger @'Nothing @'Nothing
      b <- forAll $ genIInteger @'Nothing @'Nothing
      let x = toInteger (I.unwrap a) * toInteger (I.unwrap b)
      case I.mult' a b of
        Nothing -> failure
        Just y -> toInteger (I.unwrap y) === x

  , pure $ testProperty "mult" $ property $ do
      a <- forAll $ genIInteger @'Nothing @'Nothing
      b <- forAll $ genIInteger @'Nothing @'Nothing
      Just (I.mult a b) === I.mult' a b

  , pure $ testProperty "minus'" $ property $ do
      a <- forAll $ genIInteger @'Nothing @'Nothing
      b <- forAll $ genIInteger @'Nothing @'Nothing
      let x = toInteger (I.unwrap a) - toInteger (I.unwrap b)
      case I.minus' a b of
        Nothing -> failure
        Just y -> toInteger (I.unwrap y) === x

  , pure $ testProperty "minus" $ property $ do
      a <- forAll $ genIInteger @'Nothing @'Nothing
      b <- forAll $ genIInteger @'Nothing @'Nothing
      Just (I.minus a b) === I.minus' a b

  , pure $ testProperty "div'" $ property $ do
      a <- forAll $ genIInteger @'Nothing @'Nothing
      b <- forAll $ Gen.filter (\x -> I.unwrap x /= 0)
                               (genIInteger @'Nothing @'Nothing)
      let (q, m) = toInteger (I.unwrap a) `divMod` toInteger (I.unwrap b)
      case I.div' a b of
        Nothing -> assert (m /= 0)
        Just y -> do q === toInteger (I.unwrap y)
                     m === 0

  , pure $ testProperty "clamp'" $ property $ do
      x <- forAll $ genInteger
      x === I.unwrap (I.clamp @Integer @'Nothing @'Nothing x)

  , pure $ testProperty "with" $ property $ do
      x <- forAll $ genIInteger @'Nothing @'Nothing
      x === I.with x I.known'

  , pure $ testProperty "pred'" $ property $ do
      x <- forAll $ genIInteger @'Nothing @'Nothing
      case I.pred' x of
        Nothing -> failure
        Just y -> I.unwrap y === I.unwrap x - 1

  , pure $ testProperty "succ'" $ property $ do
      x <- forAll $ genIInteger @'Nothing @'Nothing
      case I.succ' x of
        Nothing -> failure
        Just y -> I.unwrap y === I.unwrap x + 1

  , pure $ testProperty "pred" $ property $ do
      x <- forAll $ genIInteger @'Nothing @'Nothing
      Just (I.pred x) === I.pred' x

  , pure $ testProperty "succ" $ property $ do
      x <- forAll $ genIInteger @'Nothing @'Nothing
      Just (I.succ x) === I.succ' x

  , pure $ testCase "zero" $ do
      0 @=? I.unwrap (I.zero @Integer @'Nothing @'Nothing)

  , pure $ testCase "one" $ do
      1 @=? I.unwrap (I.one @Integer @'Nothing @'Nothing)

  , pure $ testProperty "negate'" $ property $ do
      x <- forAll $ genIInteger @'Nothing @'Nothing
      fmap I.unwrap (I.negate' x)  ===  Just (negate (I.unwrap x))

  , pure $ testProperty "negate" $ property $ do
      x <- forAll $ genIInteger @'Nothing @'Nothing
      Just (I.negate x) === I.negate' x

  , pure $ testProperty "down" $ property $ do
      x <- forAll $ genIInteger @'Nothing @'Nothing
      Just x === I.down x
      case I.down x of
        Nothing -> failure
        Just y -> I.unwrap x
              === I.unwrap (y :: I Integer (I.MinL Integer) (I.MaxR Integer))

  , pure $ testProperty "up" $ property $ do
      x <- forAll $ genIInteger @'Nothing @'Nothing
      x === I.up x
      I.unwrap x ===
        I.unwrap (I.up x :: I Integer (I.MinL Integer) (I.MaxR Integer))

  ]