main :: IO ()
main = defaultMain
  [ testProperty "trySIntVal in bounds" $ forAll naturals $
      \x -> x <= maxInt ==> case someNatVal x of
        SomeNat (Proxy :: Proxy n) ->
          fmap (fromIntegral . unSInt) (trySIntVal @n) === Just x

  , testProperty "trySIntVal out of bounds" $ forAll naturals $
      \x -> case someNatVal (maxInt + x) of
        SomeNat (Proxy :: Proxy n) ->
          void (trySIntVal @n) === Nothing

  , testProperty "withSInt negative raises" $ \ (Negative x) ->
      raisesError $ withSInt x $ const ()

  , testProperty "withSInt positive" $ \ (Positive x) ->
      withSInt x unSInt === x

  , testProperty "addSInt" $
      \ (Positive x) (Positive y) ->
        withSInt x $ \x' ->
        withSInt y $ \y' ->
          let xy = fromIntegral x + fromIntegral y :: Natural
          in  if xy > maxInt
                then raisesError $ addSInt x' y'
                else fromIntegral (unSInt (addSInt x' y')) === xy

  , testProperty "addSInt out of range" $
      raisesError $ withSInt maxBound $ \x -> withSInt 1 (unSInt . addSInt x)

  , testProperty "subSInt" $
      \ (Positive x) (Positive y) ->
        withSInt x $ \x' ->
        withSInt y $ \y' ->
          if x < y
            then raisesError $ subSInt x' y'
            else unSInt (subSInt x' y') === x - y

  , testProperty "mulSInt" $
      forAll (scale (*1000000000) arbitrary) $ \ (Positive x) ->
      forAll (scale (*1000000000) arbitrary) $ \ (Positive y) ->
        withSInt x $ \x' ->
        withSInt y $ \y' ->
          let xy = fromIntegral x * fromIntegral y :: Natural
          in  if xy > maxInt
                then raisesError $ mulSInt x' y'
                else fromIntegral (unSInt (mulSInt x' y')) === xy

  , testProperty "reifySInt" $
      \ (Positive x) ->
        withSInt x $ \ (x' :: SInt n) ->
          fromIntegral x === reifySInt x' (natVal @n Proxy)
  ]
main :: IO ()
main = defaultMain
  [ testProperty "trySIntVal in bounds" $ forAll naturals $
      \x -> x <= maxInt ==> case someNatVal x of
        SomeNat (Proxy :: Proxy n) ->
          fmap (fromIntegral . unSInt) (trySIntVal @n) === Just x

  , testProperty "trySIntVal out of bounds" $ forAll naturals $
      \x -> case someNatVal (maxInt + x) of
        SomeNat (Proxy :: Proxy n) ->
          void (trySIntVal @n) === Nothing

  , testProperty "withSInt negative raises" $ \ (Negative x) ->
      raisesError $ withSInt x $ const ()

  , testProperty "withSInt positive" $ \ (Positive x) ->
      withSInt x unSInt === x

  , testProperty "addSInt" $
      \ (Positive x) (Positive y) ->
        withSInt x $ \x' ->
        withSInt y $ \y' ->
          let xy = fromIntegral x + fromIntegral y :: Natural
          in  if xy > maxInt
                then raisesError $ addSInt x' y'
                else fromIntegral (unSInt (addSInt x' y')) === xy

  , testProperty "addSInt out of range" $
      raisesError $ withSInt maxBound $ \x -> withSInt 1 (unSInt . addSInt x)

  , testProperty "subSInt" $
      \ (Positive x) (Positive y) ->
        withSInt x $ \x' ->
        withSInt y $ \y' ->
          if x < y
            then raisesError $ subSInt x' y'
            else unSInt (subSInt x' y') === x - y

  , testProperty "mulSInt" $
      forAll (scale (*1000000000) arbitrary) $ \ (Positive x) ->
      forAll (scale (*1000000000) arbitrary) $ \ (Positive y) ->
        withSInt x $ \x' ->
        withSInt y $ \y' ->
          let xy = fromIntegral x * fromIntegral y :: Natural
          in  if xy > maxInt
                then raisesError $ mulSInt x' y'
                else fromIntegral (unSInt (mulSInt x' y')) === xy

  , testProperty "reifySInt" $
      \ (Positive x) ->
        withSInt x $ \ (x' :: SInt n) ->
          fromIntegral x === reifySInt x' (natVal @n Proxy)
  ]
main = defaultMain
  [ testProperty "trySIntVal in bounds" $ forAll naturals $
      \x -> x <= maxInt ==> case someNatVal x of
        SomeNat (Proxy :: Proxy n) ->
          fmap (fromIntegral . unSInt) (trySIntVal @n) === Just x

  , testProperty "trySIntVal out of bounds" $ forAll naturals $
      \x -> case someNatVal (maxInt + x) of
        SomeNat (Proxy :: Proxy n) ->
          void (trySIntVal @n) === Nothing

  , testProperty "withSInt negative raises" $ \ (Negative x) ->
      raisesError $ withSInt x $ const ()

  , testProperty "withSInt positive" $ \ (Positive x) ->
      withSInt x unSInt === x

  , testProperty "addSInt" $
      \ (Positive x) (Positive y) ->
        withSInt x $ \x' ->
        withSInt y $ \y' ->
          let xy = fromIntegral x + fromIntegral y :: Natural
          in  if xy > maxInt
                then raisesError $ addSInt x' y'
                else fromIntegral (unSInt (addSInt x' y')) === xy

  , testProperty "addSInt out of range" $
      raisesError $ withSInt maxBound $ \x -> withSInt 1 (unSInt . addSInt x)

  , testProperty "subSInt" $
      \ (Positive x) (Positive y) ->
        withSInt x $ \x' ->
        withSInt y $ \y' ->
          if x < y
            then raisesError $ subSInt x' y'
            else unSInt (subSInt x' y') === x - y

  , testProperty "mulSInt" $
      forAll (scale (*1000000000) arbitrary) $ \ (Positive x) ->
      forAll (scale (*1000000000) arbitrary) $ \ (Positive y) ->
        withSInt x $ \x' ->
        withSInt y $ \y' ->
          let xy = fromIntegral x * fromIntegral y :: Natural
          in  if xy > maxInt
                then raisesError $ mulSInt x' y'
                else fromIntegral (unSInt (mulSInt x' y')) === xy

  , testProperty "reifySInt" $
      \ (Positive x) ->
        withSInt x $ \ (x' :: SInt n) ->
          fromIntegral x === reifySInt x' (natVal @n Proxy)
  ]