packages feed

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

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE OverloadedStrings #-}

{-# OPTIONS_GHC -Wno-unused-top-binds -Wno-unused-imports #-}

module I.Test.Rational (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)
import KindRational (type (/))
import KindRational qualified as KR
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 "Rational"
  [ testProperty "wrap" $ property $ do
      x <- forAll genRational
      x === I.unwrap (I.wrap x)

  , tt'cc @(N 100/1) @(N 100/1)
  , tt'cc @(N 100/1) @(N 10/1)
  , tt'cc @(N 100/1) @(N 1/1)
  , tt'cc @(N 100/1) @(N 0/1)
  , tt'cc @(N 1/1)   @(N 1/1)
  , tt'cc @(N 1/1)   @(0/1)
  , tt'cc @(0/1)     @(0/1)
  , tt'cc @(0/1)     @(1/1)
  , tt'cc @(1/1)     @(1/1)
  , tt'cc @(0/1)     @(100/1)
  , tt'cc @(1/1)     @(100/1)
  , tt'cc @(10/1)    @(100/1)
  , tt'cc @(100/1)   @(100/1)

  , tt'co @(N 100/1) @(N 10/1)
  , tt'co @(N 100/1) @(N 1/1)
  , tt'co @(N 100/1) @(N 0/1)
  , tt'co @(N 1/1)   @(0/1)
  , tt'co @(0/1)     @(1/1)
  , tt'co @(0/1)     @(100/1)
  , tt'co @(1/1)     @(100/1)
  , tt'co @(10/1)    @(100/1)

  , tt'cu @(N 100/1)
  , tt'cu @(N 1/1)
  , tt'cu @(0/1)
  , tt'cu @(1/1)
  , tt'cu @(100/1)

  , tt'oc @(N 100/1) @(N 10/1)
  , tt'oc @(N 100/1) @(N 1/1)
  , tt'oc @(N 100/1) @(N 0/1)
  , tt'oc @(N 1/1)   @(0/1)
  , tt'oc @(0/1)     @(1/1)
  , tt'oc @(0/1)     @(100/1)
  , tt'oc @(1/1)     @(100/1)
  , tt'oc @(10/1)    @(100/1)

  , tt'oo @(N 100/1) @(N 10/1)
  , tt'oo @(N 100/1) @(N 1/1)
  , tt'oo @(N 100/1) @(N 0/1)
  , tt'oo @(N 1/1)   @(0/1)
  , tt'oo @(0/1)     @(1/1)
  , tt'oo @(0/1)     @(100/1)
  , tt'oo @(1/1)     @(100/1)
  , tt'oo @(10/1)    @(100/1)

  , tt'ou @(N 100/1)
  , tt'ou @(N 1/1)
  , tt'ou @(0/1)
  , tt'ou @(1/1)
  , tt'ou @(100/1)

  , tt'uc @(N 100/1)
  , tt'uc @(N 1/1)
  , tt'uc @(0/1)
  , tt'uc @(1/1)
  , tt'uc @(100/1)

  , tt'uo @(N 100/1)
  , tt'uo @(N 1/1)
  , tt'uo @(0/1)
  , tt'uo @(1/1)
  , tt'uo @(100/1)

  , tt'uu
  ]

tt'cc
  :: forall (l :: KR.Rational) (r :: KR.Rational)
  .  I.Interval Rational ('Just '( 'True, l)) ('Just '( 'True, r))
  => TestTree
tt'cc = testGroup ("Interval [" <> show l <> ", " <> show r <> "]")
  $ concat
  [ pure $ testProperty "from" $ property $ do
      x <- forAll genRational
      case I.from @Rational @('Just '( 'True, l)) @('Just '( 'True, 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 genRational
      let y = I.shove @Rational @('Just '( 'True, l)) @('Just '( 'True, r)) x
      I.from (I.unwrap y) === Just y
      if x < l' || x > r'
         then I.from @Rational @('Just '( 'True, l)) @('Just '( 'True, r)) x === Nothing
         else I.from @Rational @('Just '( 'True, l)) @('Just '( 'True, r)) x /== Nothing

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

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

  , case (leRational @(0/1) @l, leRational @r @(1/1)) of
      (Just Dict, Just Dict) ->
        pure $ testProperty "mult" $ property $ do
          a <- forAll $ genIRational @('Just '( 'True, l)) @('Just '( 'True, r))
          b <- forAll $ genIRational
          Just (I.mult a b) === I.mult' a b
      _ -> mzero

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

  , if (l' == 0 && r' == 0) then mzero else
    pure $ testProperty "div'" $ property $ do
      a <- forAll $ genIRational @('Just '( 'True, l)) @('Just '( 'True, r))
      b <- forAll $ Gen.filter (\x -> I.unwrap x /= 0) genIRational
      I.div' a b === I.from (I.unwrap a / I.unwrap b)

  , case (ltRational @(0/1) @l, leRational @r @(1/1)) of
      (Just Dict, Just Dict) -> pure $ testProperty "div" $ property $ do
        a <- forAll $ genIRational @('Just '( 'True, l)) @('Just '( 'True, r))
        b <- forAll $ Gen.filter (\x -> I.unwrap x /= 0)
                                 (genIRational @('Just '( 'True, l))
                                               @('Just '( 'True, r)))
        I.div' a b === Just (I.div a b)
      _ -> mzero

  , pure $ testProperty "clamp'" $ property $ do
      x <- forAll $ genRational
      case I.clamp @Rational @('Just '( 'True, l)) @('Just '( 'True, 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 $ genIRational @('Just '( 'True, l)) @('Just '( 'True, r))
      x === I.with x I.known'

  , case (leRational @l @(0/1), leRational @(0/1) @r) of
      (Just Dict, Just Dict) -> pure $ testCase "zero" $ do
        0 @=? I.unwrap (I.zero @Rational @('Just '( 'True, l))
                                         @('Just '( 'True, r)))
      _ -> mzero

  , case (leRational @l @(1/1), leRational @(1/1) @r) of
      (Just Dict, Just Dict) -> pure $ testCase "one" $ do
        1 @=? I.unwrap (I.one @Rational @('Just '( 'True, l))
                                        @('Just '( 'True, r)))
      _ -> mzero

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

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

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

  ]
  where
    l   = I.min         :: I Rational ('Just '( 'True, l)) ('Just '( 'True, r))
    l'  = I.unwrap l    :: Rational
    r   = I.max         :: I Rational ('Just '( 'True, l)) ('Just '( 'True, r))
    r'  = I.unwrap r    :: Rational

tt'co
  :: forall (l :: KR.Rational) (r :: KR.Rational)
  .  I.Interval Rational ('Just '( 'True, l)) ('Just '( 'False, r))
  => TestTree
tt'co = testGroup ("Interval [" <> show l <> ", " <> show r' <> ")")
  $ concat
  [ pure $ testProperty "from" $ property $ do
      x <- forAll genRational
      case I.from @Rational @('Just '( 'True, l)) @('Just '( 'False, 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 genRational
      let y = I.shove @Rational @('Just '( 'True, l)) @('Just '( 'False, r)) x
      I.from (I.unwrap y) === Just y
      if x < l' || x >= r'
         then I.from @Rational @('Just '( 'True, l)) @('Just '( 'False, r)) x === Nothing
         else I.from @Rational @('Just '( 'True, l)) @('Just '( 'False, r)) x /== Nothing

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

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


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

  , if (l' == 0 && r' == 0) then mzero else
    pure $ testProperty "div'" $ property $ do
      a <- forAll $ genIRational @('Just '( 'True, l)) @('Just '( 'False, r))
      b <- forAll $ Gen.filter (\x -> I.unwrap x /= 0) genIRational
      I.div' a b === I.from (I.unwrap a / I.unwrap b)

  , case (ltRational @(0/1) @l, leRational @r @(1/1)) of
      (Just Dict, Just Dict) -> pure $ testProperty "div" $ property $ do
        a <- forAll $ genIRational @('Just '( 'False, l)) @('Just '( 'True, r))
        b <- forAll $ Gen.filter (\x -> I.unwrap x /= 0) genIRational
        I.div' a b === Just (I.div a b)
      _ -> mzero

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

  , case (leRational @l @(0/1), ltRational @(0/1) @r) of
      (Just Dict, Just Dict) -> pure $ testCase "zero" $ do
        0 @=? I.unwrap (I.zero @Rational @('Just '( 'True, l))
                                        @('Just '( 'False, r)))
      _ -> mzero


  , case (leRational @l @(1/1), ltRational @(1/1) @r) of
      (Just Dict, Just Dict) -> pure $ testCase "one" $ do
        1 @=? I.unwrap (I.one @Rational @('Just '( 'True, l))
                                        @('Just '( 'False, r)))
      _ -> mzero

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

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

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

  ]
  where
    l  = I.min :: I Rational ('Just '( 'True, l)) ('Just '( 'False, r))
    l' = I.unwrap l :: Rational
    r' = KR.rationalVal (Proxy @r) :: Rational

tt'cu
  :: forall (l :: KR.Rational)
  .  I.Interval Rational ('Just '( 'True, l)) 'Nothing
  => TestTree
tt'cu = testGroup ("Interval [" <> show l <> ", infinity)")
  $ concat
  [ pure $ testProperty "from" $ property $ do
      x <- forAll genRational
      case I.from @Rational @('Just '( 'True, 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 genRational
      let y = I.shove @Rational @('Just '( 'True, l)) @'Nothing x
      I.from (I.unwrap y) === Just y
      if x < l'
         then I.from @Rational @('Just '( 'True, l)) @'Nothing x === Nothing
         else I.from @Rational @('Just '( 'True, l)) @'Nothing x /== Nothing

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

  , case leRational @(0/1) @l of
      Nothing -> mzero
      Just Dict -> pure $ testProperty "plus" $ property $ do
        a <- forAll $ genIRational @('Just '( 'True, l)) @'Nothing
        b <- forAll $ genIRational @('Just '( 'True, l)) @'Nothing
        Just (I.plus a b) === I.plus' a b

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

  , case leRational @(1/1) @l of
      Nothing -> mzero
      Just Dict -> pure $ testProperty "mult" $ property $ do
        a <- forAll $ genIRational @('Just '( 'True, l)) @'Nothing
        b <- forAll $ genIRational @('Just '( 'True, l)) @'Nothing
        Just (I.mult a b) === I.mult' a b

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

  , if (l' == 0) then mzero else
    pure $ testProperty "div'" $ property $ do
      a <- forAll $ genIRational @('Just '( 'True, l)) @'Nothing
      b <- forAll $ Gen.filter (\x -> I.unwrap x /= 0) genIRational
      I.div' a b === I.from (I.unwrap a / I.unwrap b)

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

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

  , case leRational @l @(0/1) of
      Just Dict -> pure $ testCase "zero" $ do
        0 @=? I.unwrap (I.zero @Rational @('Just '( 'True, l)) @'Nothing)
      _ -> mzero

  , case leRational @l @(1/1) of
      Just Dict -> pure $ testCase "one" $ do
        1 @=? I.unwrap (I.one @Rational @('Just '( 'True, l)) @'Nothing)
      _ -> mzero

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

  , pure $ testProperty "up" $ property $ do
      x <- forAll $ genIRational @('Just '( 'True, l)) @'Nothing
      x === I.up x
      I.unwrap x ===
        I.unwrap (I.up x :: I Rational (I.MinL Rational) (I.MaxR Rational))
  ]
  where
    l   = I.min        :: I Rational ('Just '( 'True, l)) 'Nothing
    l'  = I.unwrap l   :: Rational

tt'oc
  :: forall (l :: KR.Rational) (r :: KR.Rational)
  .  I.Interval Rational ('Just '( 'False, l)) ('Just '( 'True, r))
  => TestTree
tt'oc = testGroup ("Interval (" <> show l' <> ", " <> show r <> "]")
  $ concat
  [ pure $ testProperty "from" $ property $ do
      x <- forAll genRational
      case I.from @Rational @('Just '( 'False, l)) @('Just '( 'True, 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 genRational
      let y = I.shove @Rational @('Just '( 'False, l)) @('Just '( 'True, r)) x
      I.from (I.unwrap y) === Just y
      if x <= l' || x > r'
         then I.from @Rational @('Just '( 'False, l)) @('Just '( 'True, r)) x === Nothing
         else I.from @Rational @('Just '( 'False, l)) @('Just '( 'True, r)) x /== Nothing

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

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


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

  , if (r' == 0) then mzero else
    pure $ testProperty "div'" $ property $ do
      a <- forAll $ genIRational @('Just '( 'False, l)) @('Just '( 'True, r))
      b <- forAll $ Gen.filter (\x -> I.unwrap x /= 0) genIRational
      I.div' a b === I.from (I.unwrap a / I.unwrap b)

  , case (ltRational @(0/1) @l, leRational @r @(1/1)) of
      (Just Dict, Just Dict) -> pure $ testProperty "div" $ property $ do
        a <- forAll $ genIRational @('Just '( 'False, l)) @('Just '( 'True, r))
        b <- forAll $ Gen.filter (\x -> I.unwrap x /= 0) genIRational
        I.div' a b === Just (I.div a b)
      _ -> mzero

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

  , case (ltRational @l @(0/1), leRational @(0/1) @r) of
      (Just Dict, Just Dict) ->
        pure $ testCase "zero" $
          0 @=? I.unwrap (I.zero @Rational @('Just '( 'False, l))
                                           @('Just '( 'True, r)))
      _ -> mzero

  , case (ltRational @l @(1/1), leRational @(1/1) @r) of
      (Just Dict, Just Dict) -> pure $ testCase "one" $ do
        1 @=? I.unwrap (I.one @Rational @('Just '( 'False, l))
                                        @('Just '( 'True, r)))
      _ -> mzero

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

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

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

  ]
  where
    l' = KR.rationalVal (Proxy @l) :: Rational
    r  = I.max :: I Rational ('Just '( 'False, l)) ('Just '( 'True, r))
    r' = I.unwrap r :: Rational

tt'oo
  :: forall (l :: KR.Rational) (r :: KR.Rational)
  .  I.Interval Rational ('Just '( 'False, l)) ('Just '( 'False, r))
  => TestTree
tt'oo = testGroup ("Interval (" <> show l' <> ", " <> show r' <> ")")
  $ concat
  [ pure $ testProperty "from" $ property $ do
      x <- forAll genRational
      case I.from @Rational @('Just '( 'False, l)) @('Just '( 'False, 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 genRational
      let y = I.shove @Rational @('Just '( 'False, l)) @('Just '( 'False, r)) x
      I.from (I.unwrap y) === Just y
      if x <= l' || x >= r'
         then I.from @Rational @('Just '( 'False, l)) @('Just '( 'False, r)) x === Nothing
         else I.from @Rational @('Just '( 'False, l)) @('Just '( 'False, r)) x /== Nothing

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

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

  , case (leRational @(0/1) @l, leRational @r @(1/1)) of
      (Just Dict, Just Dict) ->
        pure $ testProperty "mult" $ property $ do
          a <- forAll $ genIRational @('Just '( 'False, l)) @('Just '( 'False, r))
          b <- forAll $ genIRational
          Just (I.mult a b) === I.mult' a b
      _ -> mzero

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

  , pure $ testProperty "div'" $ property $ do
      a <- forAll $ genIRational @('Just '( 'False, l)) @('Just '( 'False, r))
      b <- forAll $ Gen.filter (\x -> I.unwrap x /= 0) genIRational
      I.div' a b === I.from (I.unwrap a / I.unwrap b)

  , case (ltRational @(0/1) @l, leRational @r @(1/1)) of
      (Just Dict, Just Dict) -> pure $ testProperty "div" $ property $ do
        a <- forAll $ genIRational @('Just '( 'False, l)) @('Just '( 'False, r))
        b <- forAll $ Gen.filter (\x -> I.unwrap x /= 0)
                                 (genIRational @('Just '( 'False, l))
                                               @('Just '( 'False, r)))
        I.div' a b === Just (I.div a b)
      _ -> mzero

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

  , case (ltRational @l @(0/1), ltRational @(0/1) @r) of
      (Just Dict, Just Dict) -> pure $ testCase "one" $ do
        0 @=? I.unwrap (I.zero @Rational @('Just '( 'False, l))
                                         @('Just '( 'False, r)))
      _ -> mzero

  , case (ltRational @l @(1/1), ltRational @(1/1) @r) of
      (Just Dict, Just Dict) -> pure $ testCase "one" $ do
        1 @=? I.unwrap (I.one @Rational @('Just '( 'False, l))
                                        @('Just '( 'False, r)))
      _ -> mzero

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

  , withDict (negateRational @r) $
    case (ltRational @l @(0/1), ltRational @(0/1) @r) of
      (Just Dict, Just Dict) ->
        case KR.cmpRational (Proxy @l) (Proxy @(KR.Negate r)) of
          EQI -> pure $ testProperty "negate" $ property $ do
            x <- forAll $ genIRational @('Just '( 'False, l))
                                       @('Just '( 'False, r))
            Just (I.negate x) === I.negate' x
          _ -> mzero
      _ -> mzero

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

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

  ]
  where
    l' = KR.rationalVal (Proxy @l) :: Rational
    r' = KR.rationalVal (Proxy @r) :: Rational

tt'ou
  :: forall (l :: KR.Rational)
  .  I.Interval Rational ('Just '( 'False, l)) 'Nothing
  => TestTree
tt'ou = testGroup ("Interval (" <> show l' <> ", infinity)")
  $ concat
  [ pure $ testProperty "from" $ property $ do
      x <- forAll genRational
      case I.from @Rational @('Just '( 'False, 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 genRational
      let y = I.shove @Rational @('Just '( 'False, l)) @'Nothing x
      I.from (I.unwrap y) === Just y
      if x <= l'
         then I.from @Rational @('Just '( 'False, l)) @'Nothing x === Nothing
         else I.from @Rational @('Just '( 'False, l)) @'Nothing x /== Nothing

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

  , case leRational @(0/1) @l of
      Nothing -> mzero
      Just Dict -> pure $ testProperty "plus" $ property $ do
        a <- forAll $ genIRational @('Just '( 'False, l)) @'Nothing
        b <- forAll $ genIRational @('Just '( 'False, l)) @'Nothing
        Just (I.plus a b) === I.plus' a b

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

  , case leRational @(1/1) @l of
      Nothing -> mzero
      Just Dict -> pure $ testProperty "mult" $ property $ do
        a <- forAll $ genIRational @('Just '( 'False, l)) @'Nothing
        b <- forAll $ genIRational @('Just '( 'False, l)) @'Nothing
        Just (I.mult a b) === I.mult' a b

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

  , if (l' == 0) then mzero else
    pure $ testProperty "div'" $ property $ do
      a <- forAll $ genIRational @('Just '( 'False, l)) @'Nothing
      b <- forAll $ Gen.filter (\x -> I.unwrap x /= 0) genIRational
      I.div' a b === I.from (I.unwrap a / I.unwrap b)

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

  , case ltRational @l @(0/1) of
      Just Dict -> pure $ testCase "zero" $ do
        0 @=? I.unwrap (I.zero @Rational @('Just '( 'False, l)) @'Nothing)
      _ -> mzero

  , case ltRational @l @(1/1) of
      Just Dict -> pure $ testCase "one" $ do
        1 @=? I.unwrap (I.one @Rational @('Just '( 'False, l)) @'Nothing)
      _ -> mzero

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

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

  ]
  where
    l' = KR.rationalVal (Proxy @l) :: Rational

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

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

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

  , case leRational @r @(0/1) of
      Nothing -> mzero
      Just Dict -> pure $ testProperty "plus" $ property $ do
        a <- forAll $ genIRational @'Nothing @('Just '( 'True, r))
        b <- forAll $ genIRational @'Nothing @('Just '( 'True, r))
        Just (I.plus a b) === I.plus' a b

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

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

  , if (r' == 0) then mzero else
    pure $ testProperty "div'" $ property $ do
      a <- forAll $ genIRational @'Nothing @('Just '( 'True, r))
      b <- forAll $ Gen.filter (\x -> I.unwrap x /= 0) genIRational
      I.div' a b === I.from (I.unwrap a / I.unwrap b)

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

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

  , case leRational @(0/1) @r of
      Just Dict -> pure $ testCase "zero" $ do
        0 @=? I.unwrap (I.zero @Rational @'Nothing @('Just '( 'True, r)))
      _ -> mzero

  , case leRational @(1/1) @r of
      Just Dict -> pure $ testCase "one" $ do
        1 @=? I.unwrap (I.one @Rational @'Nothing @('Just '( 'True, r)))
      _ -> mzero

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

  , pure $ testProperty "up" $ property $ do
      x <- forAll $ genIRational @'Nothing @('Just '( 'True, r))
      x === I.up x
      I.unwrap x ===
        I.unwrap (I.up x :: I Rational (I.MinL Rational) (I.MaxR Rational))
  ]
  where
    r   = I.max        :: I Rational 'Nothing ('Just '( 'True, r))
    r'  = I.unwrap r   :: Rational

tt'uo
  :: forall (r :: KR.Rational)
  .  I.Interval Rational 'Nothing ('Just '( 'False, r))
  => TestTree
tt'uo = testGroup ("Interval (-infinity, " <> show r' <> ")")
  $ concat
  [ pure $ testProperty "from" $ property $ do
      x <- forAll genRational
      case I.from @Rational @'Nothing @('Just '( 'False, r)) x of
        Nothing -> assert (x >= r')
        Just y -> do assert (x < r')
                     I.unwrap y === x

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

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

  , case leRational @r @(0/1) of
      Nothing -> mzero
      Just Dict -> pure $ testProperty "plus" $ property $ do
        a <- forAll $ genIRational @'Nothing @('Just '( 'False, r))
        b <- forAll $ genIRational @'Nothing @('Just '( 'False, r))
        Just (I.plus a b) === I.plus' a b

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

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

  , pure $ testProperty "div'" $ property $ do
      a <- forAll $ genIRational @'Nothing @('Just '( 'False, r))
      b <- forAll $ Gen.filter (\x -> I.unwrap x /= 0) genIRational
      I.div' a b === I.from (I.unwrap a / I.unwrap b)

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

  , case ltRational @(0/1) @r of
      Just Dict -> pure $ testCase "zero" $ do
        0 @=? I.unwrap (I.zero @Rational @'Nothing @('Just '( 'False, r)))
      _ -> mzero

  , case ltRational @(1/1) @r of
      Just Dict -> pure $ testCase "one" $ do
        1 @=? I.unwrap (I.one @Rational @'Nothing @('Just '( 'False, r)))
      _ -> mzero

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

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

  ]
  where
    r' = KR.rationalVal (Proxy @r) :: Rational

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

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

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

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

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

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

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

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

  , pure $ testProperty "div'" $ property $ do
      a <- forAll $ genIRational @'Nothing @'Nothing
      b <- forAll $ Gen.filter (\x -> I.unwrap x /= 0) genIRational
      I.div' a b === I.from (I.unwrap a / I.unwrap b)

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

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

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

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

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

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

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

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

  ]