packages feed

integer-types-0.1.4.0: test/Spec/FiniteConversion.hs

module Spec.FiniteConversion where

import Data.Bool qualified as Bool
import Data.Int (Int)
import Data.Ord qualified as Ord
import Data.Word (Word)
import Essentials
import Integer
  ( ConvertWithFinite,
    Finite (..),
    Integer,
    IntegerNarrow (..),
    Natural,
    Positive,
    Signed,
  )
import Integer.Gen (GenFinite, GenIntegral)
import Integer.Gen qualified as Gen
import Test.Hspec (Spec, context, it)
import Test.Hspec.Hedgehog (PropertyT, forAll, hedgehog, modifyMaxSuccess, (===))
import Prelude qualified as Bounded (Bounded (..))
import Prelude qualified as Num (fromInteger, toInteger)

spec :: Spec
spec = do
  context "toFinite x = (Just y | fromInteger y = x) or Nothing" $
    modifyMaxSuccess (\_ -> 1000) do
      it "A = Integer,  B = Int " $ hedgehog $ checkToFinite @Integer @Int
      it "A = Integer,  B = Word" $ hedgehog $ checkToFinite @Integer @Word
      it "A = Natural,  B = Int " $ hedgehog $ checkToFinite @Natural @Int
      it "A = Natural,  B = Word" $ hedgehog $ checkToFinite @Natural @Word
      it "A = Positive, B = Int " $ hedgehog $ checkToFinite @Positive @Int
      it "A = Positive, B = Word" $ hedgehog $ checkToFinite @Positive @Word
      it "A = Signed,   B = Int " $ hedgehog $ checkToFinite @Signed @Int
      it "A = Signed,   B = Word" $ hedgehog $ checkToFinite @Signed @Word

  context "fromFinite x = narrow (toInteger x)" do
    it "A = Int,  B = Integer " $ hedgehog $ checkFromFinite @Integer @Int
    it "A = Word, B = Integer" $ hedgehog $ checkFromFinite @Integer @Word
    it "A = Int,  B = Natural " $ hedgehog $ checkFromFinite @Natural @Int
    it "A = Word, B = Natural" $ hedgehog $ checkFromFinite @Natural @Word
    it "A = Int,  B = Positive " $ hedgehog $ checkFromFinite @Positive @Int
    it "A = Word, B = Positive" $ hedgehog $ checkFromFinite @Positive @Word
    it "A = Int,  B = Signed " $ hedgehog $ checkFromFinite @Signed @Int
    it "A = Word, B = Signed" $ hedgehog $ checkFromFinite @Signed @Word

checkToFinite ::
  forall a b m.
  Monad m =>
  (ConvertWithFinite a, GenIntegral a, Show a) =>
  (Integer.Finite b, Eq b, Show b) =>
  PropertyT m ()
checkToFinite = do
  x :: a <- forAll Gen.integral

  let x' = Num.toInteger x
      ok =
        (Bool.&&)
          (x' Ord.>= Num.toInteger (Bounded.minBound :: b))
          (x' Ord.<= Num.toInteger (Bounded.maxBound :: b))

  (Integer.toFinite x :: Maybe b) === if ok then Just (Num.fromInteger x') else Nothing

checkFromFinite ::
  forall a b m.
  Monad m =>
  (ConvertWithFinite a, IntegerNarrow Integer a, Eq a, Show a) =>
  (Finite b, GenFinite b, Show b) =>
  PropertyT m ()
checkFromFinite = do
  x :: b <- forAll Gen.finite
  (Integer.fromFinite x :: Maybe a) === Integer.narrow (Num.toInteger x)