integer-types 0.1.3.0 → 0.1.4.0
raw patch · 18 files changed
+769/−355 lines, 18 files
Files
- changelog.md +26/−6
- integer-types.cabal +25/−1
- library/Integer.hs +4/−0
- library/Integer/AbsoluteDifference.hs +90/−0
- test/Main.hs +27/−348
- test/Spec/AbsoluteDifference.hs +55/−0
- test/Spec/AddOne.hs +30/−0
- test/Spec/Addition.hs +28/−0
- test/Spec/Conversion.hs +81/−0
- test/Spec/Deepseq.hs +36/−0
- test/Spec/Enum.hs +74/−0
- test/Spec/FiniteConversion.hs +72/−0
- test/Spec/Increase.hs +53/−0
- test/Spec/Length.hs +14/−0
- test/Spec/Multiplication.hs +32/−0
- test/Spec/SubtractOne.hs +21/−0
- test/Spec/Subtraction.hs +66/−0
- test/Spec/Yolo.hs +35/−0
changelog.md view
@@ -1,5 +1,13 @@-## 0.1.3.0 (2023-07-14)+## 0.1.4.0 +Added module `Integer.AbsoluteDifference`++Added to the `Integer` module: `AbsoluteDifference (absoluteDifference)`++Date: 2023-07-15++## 0.1.3.0+ Added modules `Integer.Increase`, `Integer.StrictlyIncrease` Added classes to the `Integer` module:@@ -14,18 +22,24 @@ Added to the `Integer.Signed` module: `increase`, `strictlyIncrease`, `one`, `addOne`, `subtractOne` -## 0.1.2.0 (2023-06-26)+Date: 2023-07-14 +## 0.1.2.0+ Add `Read` instance for `Positive` -## 0.1.1.0 (2023-04-22)+Date: 2023-06-26 +## 0.1.1.0+ Add `Hashable` instances for `Positive`, `Sign`, and `Signed` Add `Enum` and `Bounded` instances for `Sign` -## 0.1.0.0 (2023-02-09)+Date: 2023-04-22 +## 0.1.0.0+ Change type of `Integer.Natural.addOne` from `Integer -> Integer` to `Natural -> Positive` @@ -36,12 +50,18 @@ Integer.Positive.length :: NonEmpty a -> Positive ``` -## 0.0.0.1 (2023-01-16)+Date: 2023-02-09 +## 0.0.0.1+ Consolidate all the test suites into one Remove `Safe` pragmas -## 0.0.0.0 (2022-11-29)+Date: 2023-01-16 +## 0.0.0.0+ Initial release++Date: 2022-11-29
integer-types.cabal view
@@ -1,7 +1,7 @@ cabal-version: 3.0 name: integer-types-version: 0.1.3.0+version: 0.1.4.0 category: Numeric synopsis: Integer, Natural, and Positive@@ -25,6 +25,7 @@ common base default-language: GHC2021 ghc-options: -Wall+ default-extensions: BlockArguments DerivingStrategies@@ -32,6 +33,7 @@ NoImplicitPrelude PatternSynonyms ViewPatterns+ build-depends: , base ^>= 4.16 || ^>= 4.17 || ^>= 4.18 , deepseq ^>= 1.4.6@@ -41,8 +43,10 @@ library import: base hs-source-dirs: library+ exposed-modules: Integer+ Integer.AbsoluteDifference Integer.BoundedBelow Integer.Conversion Integer.Finite@@ -54,6 +58,7 @@ Integer.Signed Integer.StrictlyIncrease Integer.Subtraction+ other-modules: Integer.Positive.Unsafe @@ -61,15 +66,34 @@ import: base hs-source-dirs: test type: exitcode-stdio-1.0+ default-extensions: AllowAmbiguousTypes BlockArguments+ build-depends: , exceptions ^>= 0.10.4 , integer-types , hedgehog ^>= 1.0.5 || ^>= 1.1 || ^>= 1.2 , hspec ^>= 2.8.5 || ^>= 2.9 || ^>= 2.10 || ^>= 2.11 , hspec-hedgehog ^>= 0.0.1+ main-is: Main.hs+ other-modules: Integer.Gen++ other-modules:+ Spec.AbsoluteDifference+ Spec.Addition+ Spec.AddOne+ Spec.Conversion+ Spec.Deepseq+ Spec.Enum+ Spec.FiniteConversion+ Spec.Increase+ Spec.Length+ Spec.Multiplication+ Spec.SubtractOne+ Spec.Subtraction+ Spec.Yolo
library/Integer.hs view
@@ -13,6 +13,7 @@ -- ** Subtraction Subtraction (subtractInteger, subtractSigned), Subtraction' (subtract),+ AbsoluteDifference (absoluteDifference), -- ** Conversion IntegerNarrow (narrow),@@ -27,6 +28,9 @@ ) where +import Integer.AbsoluteDifference+ ( AbsoluteDifference (absoluteDifference),+ ) import Integer.BoundedBelow (BoundedBelow (minBound)) import Integer.Conversion ( IntegerConvert (convert),
+ library/Integer/AbsoluteDifference.hs view
@@ -0,0 +1,90 @@+module Integer.AbsoluteDifference where++import Essentials+import Integer.Integer (Integer)+import Integer.Integer qualified as Integer+import Integer.Natural (Natural)+import Integer.Positive (Positive)+import Integer.Positive qualified as Positive+import Integer.Signed (Signed (Minus, NonZero, Plus, Zero))+import Integer.Signed qualified as Signed+import Prelude (fromInteger, (+), (-))+import Prelude qualified as Num (abs)++class AbsoluteDifference a b where+ absoluteDifference :: a -> b -> Natural++--++instance AbsoluteDifference Integer Integer where+ absoluteDifference a b = fromInteger $ Num.abs $ a - b++instance AbsoluteDifference Natural Natural where+ absoluteDifference a b = if a >= b then a - b else b - a++instance AbsoluteDifference Positive Positive where+ absoluteDifference a b =+ absoluteDifference+ (Positive.toNatural a)+ (Positive.toNatural b)++instance AbsoluteDifference Signed Signed where+ absoluteDifference Zero Zero = 0+ absoluteDifference Zero (NonZero _ x) = Positive.toNatural x+ absoluteDifference (NonZero _ x) Zero = Positive.toNatural x+ absoluteDifference (NonZero s1 x1) (NonZero s2 x2) =+ if s1 == s2+ then absoluteDifference x1 x2+ else Positive.toNatural (x1 + x2)++--++instance AbsoluteDifference Positive Natural where+ absoluteDifference p n = absoluteDifference (Positive.toNatural p) n++instance AbsoluteDifference Natural Positive where+ absoluteDifference n p = absoluteDifference p n++--++instance AbsoluteDifference Signed Natural where+ absoluteDifference Zero n = n+ absoluteDifference (Plus a) b = absoluteDifference a b+ absoluteDifference (Minus a) b = Positive.toNatural a + b++instance AbsoluteDifference Natural Signed where+ absoluteDifference n s = absoluteDifference s n++--++instance AbsoluteDifference Integer Natural where+ absoluteDifference i n = absoluteDifference (Integer.toSigned i) n++instance AbsoluteDifference Natural Integer where+ absoluteDifference n i = absoluteDifference i n++--++instance AbsoluteDifference Signed Positive where+ absoluteDifference Zero p = Positive.toNatural p+ absoluteDifference (Plus a) b = absoluteDifference a b+ absoluteDifference (Minus a) b = Positive.toNatural (a + b)++instance AbsoluteDifference Positive Signed where+ absoluteDifference p s = absoluteDifference s p++--++instance AbsoluteDifference Signed Integer where+ absoluteDifference s i = absoluteDifference (Signed.toInteger s) i++instance AbsoluteDifference Integer Signed where+ absoluteDifference i s = absoluteDifference s i++--++instance AbsoluteDifference Positive Integer where+ absoluteDifference p i = absoluteDifference (Positive.toInteger p) i++instance AbsoluteDifference Integer Positive where+ absoluteDifference i p = absoluteDifference p i
test/Main.hs view
@@ -1,354 +1,33 @@ module Main (main) where -import Control.DeepSeq (NFData, ($!!))-import Control.Exception (Exception, throw)-import Control.Exception qualified as Exception (ArithException (Underflow))-import Control.Monad.Catch qualified as Exception (MonadCatch, try)-import Data.Bool qualified as Bool-import Data.Either (Either (..))-import Data.Either qualified as Either-import Data.Int (Int)-import Data.List (take)-import Data.List.NonEmpty (NonEmpty ((:|)))-import Data.Ord qualified as Ord-import Data.Word (Word)-import Essentials-import Hedgehog qualified-import Integer-import Integer.Gen (GenFinite, GenIntegral)-import Integer.Gen qualified as Gen-import Integer.Natural qualified as Natural-import Integer.Positive qualified as Positive-import Integer.Signed qualified as Signed+import Spec.AbsoluteDifference qualified+import Spec.AddOne qualified+import Spec.Addition qualified+import Spec.Conversion qualified+import Spec.Deepseq qualified+import Spec.Enum qualified+import Spec.FiniteConversion qualified+import Spec.Increase qualified+import Spec.Length qualified+import Spec.Multiplication qualified+import Spec.SubtractOne qualified+import Spec.Subtraction qualified+import Spec.Yolo qualified import System.IO (IO)-import Test.Hspec (context, hspec, it, shouldBe)-import Test.Hspec.Hedgehog (evalMaybe, hedgehog, modifyMaxSuccess, (===))-import Prelude (Num, fromInteger, toInteger, ($!), (*), (+), (-))-import Prelude qualified as Bounded (Bounded (..))-import Prelude qualified as Num (fromInteger, toInteger)+import Test.Hspec (hspec) main :: IO () main = hspec do- context "addOne in A behaves the same as (+ 1) in Integer" do- modifyMaxSuccess (\_ -> 1000) do- it "A = Natural" $ hedgehog do- x :: Natural <- Hedgehog.forAll Gen.integral- toInteger (Natural.addOne x) === toInteger x + 1- it "A = Positive" $ hedgehog do- x :: Positive <- Hedgehog.forAll Gen.integral- toInteger (Positive.addOne x) === toInteger x + 1- it "A = Signed" $ hedgehog do- x :: Signed <- Hedgehog.forAll Gen.integral- toInteger (Signed.addOne x) === toInteger x + 1-- context "subtractOne in A behaves the same as (- 1) in Integer" do- modifyMaxSuccess (\_ -> 1000) do- it "A = Positive" $ hedgehog do- x :: Positive <- Hedgehog.forAll Gen.integral- toInteger (Positive.subtractOne x) === toInteger x - 1- it "A = Signed" $ hedgehog do- x :: Signed <- Hedgehog.forAll Gen.integral- toInteger (Signed.subtractOne x) === toInteger x - 1-- context "Closed Num operations op behaves the same in A as in Integer" do- modifyMaxSuccess (\_ -> 1000) do- let check ::- forall a m.- GenIntegral a =>- Monad m =>- (forall b. Num b => b -> b -> b) ->- Hedgehog.PropertyT m ()- check o = do- x :: a <- Hedgehog.forAll Gen.integral- y :: a <- Hedgehog.forAll Gen.integral- x `o` y === fromInteger (toInteger x `o` toInteger y)-- it "op = (+), A = Positive" $ hedgehog $ check @Positive (+)- it "op = (+), A = Signed" $ hedgehog $ check @Signed (+)- it "op = (*), A = Positive" $ hedgehog $ check @Positive (*)- it "op = (*), A = Signed" $ hedgehog $ check @Signed (*)-- context "subtract in A behaves the same as (-) in B" do- modifyMaxSuccess (\_ -> 1000) do- let check ::- forall a b m.- (GenIntegral a, Subtraction a, Subtraction' b, Num b) =>- (IntegerConvert a b, IntegerNarrow b a) =>- (Eq b, Show b) =>- Exception.MonadCatch m =>- Hedgehog.PropertyT m ()- check = do- x :: a <- Hedgehog.forAll Gen.integral- y :: a <- Hedgehog.forAll Gen.integral- (subtract x y :: b) === (convert x - convert y :: b)-- it "A = Natural, B = Signed" $ hedgehog $ check @Natural @Signed- it "A = Natural, B = Integer" $ hedgehog $ check @Natural @Integer- it "A = Positive, B = Signed" $ hedgehog $ check @Positive @Signed- it "A = Positive, B = Integer" $ hedgehog $ check @Positive @Integer-- context "(-) in A behaves the same as (-) in Integer if the result is in A, undefined otherwise" do- modifyMaxSuccess (\_ -> 1000) do- let check ::- forall a m.- (GenIntegral a, Subtraction a, IntegerNarrow Integer a) =>- Exception.MonadCatch m =>- Hedgehog.PropertyT m ()- check = do- x :: a <- Hedgehog.forAll Gen.integral- y :: a <- Hedgehog.forAll Gen.integral- case narrow (toInteger x - toInteger y) :: Maybe a of- Just z -> x - y === z- Nothing -> do- z <- Exception.try (pure $! x - y)- z === Either.Left Exception.Underflow-- it "A = Positive" $ hedgehog $ check @Positive-- context "increase in A behaves the same as (+) in Integer" do- modifyMaxSuccess (\_ -> 1000) do- let check ::- forall a m.- (GenIntegral a, Increase a) =>- Exception.MonadCatch m =>- Hedgehog.PropertyT m ()- check = do- x :: Natural <- Hedgehog.forAll Gen.integral- y :: a <- Hedgehog.forAll Gen.integral- toInteger (increase x y) === toInteger x + toInteger y-- it "A = Natural" $ hedgehog $ check @Natural- it "A = Integer" $ hedgehog $ check @Integer- it "A = Positive" $ hedgehog $ check @Positive- it "A = Signed" $ hedgehog $ check @Signed-- context "strictlyIncrease in A behaves the same as (+) in Integer" do- modifyMaxSuccess (\_ -> 1000) do- let check ::- forall a m.- (GenIntegral a, StrictlyIncrease a) =>- Exception.MonadCatch m =>- Hedgehog.PropertyT m ()- check = do- x :: Positive <- Hedgehog.forAll Gen.integral- y :: a <- Hedgehog.forAll Gen.integral- toInteger (strictlyIncrease x y) === toInteger x + toInteger y-- it "A = Natural" $ hedgehog $ check @Natural- it "A = Integer" $ hedgehog $ check @Integer- it "A = Positive" $ hedgehog $ check @Positive- it "A = Signed" $ hedgehog $ check @Signed-- context "convert (convert x) = x" do- let check ::- forall a b m.- (GenIntegral a, IntegerEquiv a b) =>- Monad m =>- Hedgehog.PropertyT m ()- check = do- x :: a <- Hedgehog.forAll Gen.integral- convert (convert x :: b) === x-- it "A = Integer, B = Signed" $ hedgehog $ check @Integer @Signed- it "A = Signed, B = Integer" $ hedgehog $ check @Signed @Integer-- context "narrow (convert x) = Just x" do- modifyMaxSuccess (\_ -> 1000) do- let check ::- forall a b m.- (GenIntegral a, IntegerConvert a b, IntegerNarrow b a) =>- Monad m =>- Hedgehog.PropertyT m ()- check = do- x :: a <- Hedgehog.forAll Gen.integral- narrow (convert x :: b) === Just x-- it "A = Natural, B = Integer" $ hedgehog $ check @Natural @Integer- it "A = Natural, B = Signed" $ hedgehog $ check @Natural @Signed- it "A = Positive, B = Integer" $ hedgehog $ check @Positive @Integer- it "A = Positive, B = Signed" $ hedgehog $ check @Positive @Signed- it "A = Positive, B = Natural" $ hedgehog $ check @Positive @Natural-- context "narrow x = (Just y | convert y = x) or Nothing" do- modifyMaxSuccess (\_ -> 1000) do- let check ::- forall a b m.- (GenIntegral a, BoundedBelow b) =>- (IntegerConvert b a, IntegerNarrow a b) =>- (Show b, Eq b) =>- Monad m =>- Hedgehog.PropertyT m ()- check = do- x :: a <- Hedgehog.forAll Gen.integral- let y :: Maybe b = narrow x- if x Ord.>= convert (minBound @b)- then do- z <- evalMaybe y- convert z === x- else y === Nothing-- it "A = Integer, B = Natural" $ hedgehog $ check @Integer @Natural- it "A = Signed, B = Natural" $ hedgehog $ check @Signed @Natural- it "A = Integer, B = Positive" $ hedgehog $ check @Integer @Positive- it "A = Signed, B = Positive" $ hedgehog $ check @Signed @Positive- it "A = Natural, B = Positive" $ hedgehog $ check @Natural @Positive-- context "yolo (yolo x) = x, if Integer x is in range of A" do- let check ::- forall a m.- (GenIntegral a, BoundedBelow a) =>- Exception.MonadCatch m =>- Hedgehog.PropertyT m ()- check = do- x :: Integer <- Hedgehog.forAll Gen.integral- let y :: a = yolo x- if x Ord.>= Num.toInteger (minBound @a)- then yolo y === x- else do- z <- Exception.try (pure $! y)- z === Either.Left Exception.Underflow-- it "A = Positive" $ hedgehog $ check @Positive- it "A = Natural " $ hedgehog $ check @Natural-- context "toFinite x = (Just y | fromInteger y = x) or Nothing" $- modifyMaxSuccess (\_ -> 1000) do- let check ::- forall a b m.- Monad m =>- (ConvertWithFinite a, GenIntegral a, Show a) =>- (Integer.Finite b, Eq b, Show b) =>- Hedgehog.PropertyT m ()- check = do- x :: a <- Hedgehog.forAll Gen.integral- let x' = Num.toInteger x- let ok =- x' Ord.>= Num.toInteger (Bounded.minBound :: b)- Bool.&& x' Ord.<= Num.toInteger (Bounded.maxBound :: b)- (Integer.toFinite x :: Maybe b)- === if ok then Just (Num.fromInteger x') else Nothing-- it "A = Integer, B = Int " $ hedgehog $ check @Integer @Int- it "A = Integer, B = Word" $ hedgehog $ check @Integer @Word- it "A = Natural, B = Int " $ hedgehog $ check @Natural @Int- it "A = Natural, B = Word" $ hedgehog $ check @Natural @Word- it "A = Positive, B = Int " $ hedgehog $ check @Positive @Int- it "A = Positive, B = Word" $ hedgehog $ check @Positive @Word- it "A = Signed, B = Int " $ hedgehog $ check @Signed @Int- it "A = Signed, B = Word" $ hedgehog $ check @Signed @Word-- context "fromFinite x = narrow (toInteger x)" do- let check ::- forall a b m.- Monad m =>- (ConvertWithFinite a, IntegerNarrow Integer a, Eq a, Show a) =>- (Finite b, GenFinite b, Show b) =>- Hedgehog.PropertyT m ()- check = do- x :: b <- Hedgehog.forAll Gen.finite- (Integer.fromFinite x :: Maybe a) === Integer.narrow (Num.toInteger x)-- it "A = Int, B = Integer " $ hedgehog $ check @Integer @Int- it "A = Word, B = Integer" $ hedgehog $ check @Integer @Word- it "A = Int, B = Natural " $ hedgehog $ check @Natural @Int- it "A = Word, B = Natural" $ hedgehog $ check @Natural @Word- it "A = Int, B = Positive " $ hedgehog $ check @Positive @Int- it "A = Word, B = Positive" $ hedgehog $ check @Positive @Word- it "A = Int, B = Signed " $ hedgehog $ check @Signed @Int- it "A = Word, B = Signed" $ hedgehog $ check @Signed @Word-- context "Enum @Positive" do- let (~>) = shouldBe @[Positive]-- context "[a ..]" do- it "counts upward" do- take 3 [5 ..] ~> [5, 6, 7]- it "can start with 1" do- take 3 [1 ..] ~> [1, 2, 3]-- context "[a .. b]" do- it "counts upward" do- [5 .. 8] ~> [5, 6, 7, 8]- it "can start with 1" do- [1 .. 5] ~> [1, 2, 3, 4, 5]- it "does not count downward" do- [8 .. 5] ~> []- [8 .. 7] ~> []- it "can return 1 item" do- [3 .. 3] ~> [3]- [1 .. 1] ~> [1]-- context "[a, b ..]" do- it "can count upward by 1" do- take 5 [5, 6 ..] ~> [5, 6, 7, 8, 9]- take 5 [1, 2 ..] ~> [1, 2, 3, 4, 5]- it "can count downward by 1" do- [5, 4 ..] ~> [5, 4, 3, 2, 1]- it "can count upward by 2" do- take 5 [5, 7 ..] ~> [5, 7, 9, 11, 13]- take 5 [1, 3 ..] ~> [1, 3, 5, 7, 9]- it "can count downward by 2" do- [9, 7 ..] ~> [9, 7, 5, 3, 1]- it "can count downward by 2 without exactly reaching its lower bound" do- [8, 6 ..] ~> [8, 6, 4, 2]- it "can repeat 1 item indefinitely" do- take 5 [4, 4 ..] ~> [4, 4, 4, 4, 4]-- context "[a, b .. c]" do- it "can count upward by 1" do- [5, 6 .. 9] ~> [5, 6, 7, 8, 9]- [1, 2 .. 5] ~> [1, 2, 3, 4, 5]- it "can count downward by 1" do- [9, 8 .. 5] ~> [9, 8, 7, 6, 5]- it "can count upward by 2" do- [5, 7 .. 11] ~> [5, 7, 9, 11]- [1, 3 .. 7] ~> [1, 3, 5, 7]- it "can count upward without exactly reaching its upper bound" do- [5, 7 .. 12] ~> [5, 7, 9, 11]- it "can count downward by 2" do- [11, 9 .. 5] ~> [11, 9, 7, 5]- it "can count downward by 2 without exactly reaching its lower bound" do- [11, 9 .. 4] ~> [11, 9, 7, 5]- it "can count downward with a lower bound of 1" do- [7, 5 .. 1] ~> [7, 5, 3, 1]- [8, 6 .. 1] ~> [8, 6, 4, 2]- it "can repeat 1 item indefinitely" do- take 5 [4, 4 .. 9] ~> [4, 4, 4, 4, 4]- take 5 [4, 4 .. 4] ~> [4, 4, 4, 4, 4]- it "can return 1 item" do- [4, 5 .. 4] ~> [4]- [4, 3 .. 4] ~> [4]- it "can return an empty list" do- [4, 4 .. 3] ~> []- [4, 5 .. 3] ~> []- [5, 4 .. 6] ~> []-- context "deepseq @Signed" do- let (~>) = shouldBe @(Either X Signed)-- it "can succeed" do- x <- force (NonZero MinusSign 5)- x ~> Right (-5)- it "can force an error" do- x <- force (throw X)- x ~> Left X- it "can force an error in sign" do- x <- force (NonZero (throw X) 5)- x ~> Left X- it "can force an error in magnitude" do- x <- force (NonZero MinusSign (throw X))- x ~> Left X-- context "length" do- it "Natural" do- Natural.length "abc" `shouldBe` 3- it "Positive" do- Positive.length ('a' :| "bc") `shouldBe` 3--data X = X- deriving stock (Eq, Show)--instance Exception X--force :: NFData a => Exception.MonadCatch m => a -> m (Either X a)-force x = Exception.try (pure $!! x)+ Spec.AbsoluteDifference.spec+ Spec.Addition.spec+ Spec.AddOne.spec+ Spec.Conversion.spec+ Spec.Deepseq.spec+ Spec.Enum.spec+ Spec.FiniteConversion.spec+ Spec.Increase.spec+ Spec.Length.spec+ Spec.Multiplication.spec+ Spec.Subtraction.spec+ Spec.SubtractOne.spec+ Spec.Yolo.spec
+ test/Spec/AbsoluteDifference.hs view
@@ -0,0 +1,55 @@+module Spec.AbsoluteDifference where++import Essentials+import Integer+ ( AbsoluteDifference,+ Integer,+ Natural,+ Positive,+ Signed,+ absoluteDifference,+ yolo,+ )+import Integer.Gen (GenIntegral)+import Integer.Gen qualified as Gen+import Test.Hspec (Spec, context, it)+import Test.Hspec.Hedgehog+ ( PropertyT,+ forAll,+ hedgehog,+ modifyMaxSuccess,+ (===),+ )+import Prelude (toInteger, (-))++spec :: Spec+spec =+ context "absoluteDifference @A @B works the same as converting through Integer" do+ modifyMaxSuccess (\_ -> 1000) do+ it "A = Integer, B = Integer" $ hedgehog $ check @Integer @Integer+ it "A = Natural, B = Natural" $ hedgehog $ check @Natural @Natural+ it "A = Positive, B = Positive" $ hedgehog $ check @Positive @Positive+ it "A = Signed, B = Signed" $ hedgehog $ check @Signed @Signed+ it "A = Positive, B = Natural" $ hedgehog $ check @Positive @Natural+ it "A = Natural, B = Positive" $ hedgehog $ check @Natural @Positive+ it "A = Signed, B = Natural" $ hedgehog $ check @Signed @Natural+ it "A = Natural, B = Signed" $ hedgehog $ check @Natural @Signed+ it "A = Integer, B = Natural" $ hedgehog $ check @Integer @Natural+ it "A = Natural, B = Integer" $ hedgehog $ check @Natural @Integer+ it "A = Signed, B = Positive" $ hedgehog $ check @Signed @Positive+ it "A = Positive, B = Signed" $ hedgehog $ check @Positive @Signed+ it "A = Signed, B = Integer" $ hedgehog $ check @Signed @Integer+ it "A = Integer, B = Signed" $ hedgehog $ check @Integer @Signed+ it "A = Positive, B = Integer" $ hedgehog $ check @Positive @Integer+ it "A = Integer, B = Positive" $ hedgehog $ check @Integer @Positive++type AB a b = (GenIntegral a, GenIntegral b, AbsoluteDifference a b)++check :: forall a b m. (AB a b) => Monad m => PropertyT m ()+check = do+ x :: a <- forAll Gen.integral+ y :: b <- forAll Gen.integral+ absoluteDifference x y === reference (toInteger x) (toInteger y)++reference :: Integer -> Integer -> Natural+reference a b = yolo (if a >= b then a - b else b - a)
+ test/Spec/AddOne.hs view
@@ -0,0 +1,30 @@+module Spec.AddOne where++import Essentials+import Integer (Natural, Positive, Signed)+import Integer.Gen qualified as Gen+import Integer.Natural qualified as Natural+import Integer.Positive qualified as Positive+import Integer.Signed qualified as Signed+import Test.Hspec (Spec, context, it)+import Test.Hspec.Hedgehog+ ( forAll,+ hedgehog,+ modifyMaxSuccess,+ (===),+ )+import Prelude (toInteger, (+))++spec :: Spec+spec =+ context "addOne in A behaves the same as (+ 1) in Integer" do+ modifyMaxSuccess (\_ -> 1000) do+ it "A = Natural" $ hedgehog do+ x :: Natural <- forAll Gen.integral+ toInteger (Natural.addOne x) === toInteger x + 1+ it "A = Positive" $ hedgehog do+ x :: Positive <- forAll Gen.integral+ toInteger (Positive.addOne x) === toInteger x + 1+ it "A = Signed" $ hedgehog do+ x :: Signed <- forAll Gen.integral+ toInteger (Signed.addOne x) === toInteger x + 1
+ test/Spec/Addition.hs view
@@ -0,0 +1,28 @@+module Spec.Addition where++import Essentials+import Integer (Positive, Signed)+import Integer.Gen (GenIntegral)+import Integer.Gen qualified as Gen+import Test.Hspec (Spec, context, it)+import Test.Hspec.Hedgehog+ ( PropertyT,+ forAll,+ hedgehog,+ modifyMaxSuccess,+ (===),+ )+import Prelude (fromInteger, toInteger, (+))++spec :: Spec+spec =+ context "(+) behaves the same in A as in Integer" do+ modifyMaxSuccess (\_ -> 1000) do+ it "A = Positive" $ hedgehog $ check @Positive+ it "A = Signed" $ hedgehog $ check @Signed++check :: forall a m. GenIntegral a => Monad m => PropertyT m ()+check = do+ x :: a <- forAll Gen.integral+ y :: a <- forAll Gen.integral+ x + y === fromInteger (toInteger x + toInteger y)
+ test/Spec/Conversion.hs view
@@ -0,0 +1,81 @@+module Spec.Conversion where++import Data.Ord qualified as Ord+import Essentials+import Integer+ ( BoundedBelow (..),+ Integer,+ IntegerConvert (..),+ IntegerEquiv,+ IntegerNarrow (..),+ Natural,+ Positive,+ Signed,+ )+import Integer.Gen (GenIntegral)+import Integer.Gen qualified as Gen+import Test.Hspec (Spec, context, it)+import Test.Hspec.Hedgehog+ ( PropertyT,+ evalMaybe,+ forAll,+ hedgehog,+ modifyMaxSuccess,+ (===),+ )++spec :: Spec+spec = do+ context "convert (convert x) = x" do+ it "A = Integer, B = Signed" $ hedgehog $ checkConvertConvert @Integer @Signed+ it "A = Signed, B = Integer" $ hedgehog $ checkConvertConvert @Signed @Integer++ context "narrow (convert x) = Just x" do+ modifyMaxSuccess (\_ -> 1000) do+ it "A = Natural, B = Integer" $ hedgehog $ checkNarrowConvert @Natural @Integer+ it "A = Natural, B = Signed" $ hedgehog $ checkNarrowConvert @Natural @Signed+ it "A = Positive, B = Integer" $ hedgehog $ checkNarrowConvert @Positive @Integer+ it "A = Positive, B = Signed" $ hedgehog $ checkNarrowConvert @Positive @Signed+ it "A = Positive, B = Natural" $ hedgehog $ checkNarrowConvert @Positive @Natural++ context "narrow x = (Just y | convert y = x) or Nothing" do+ modifyMaxSuccess (\_ -> 1000) do+ it "A = Integer, B = Natural" $ hedgehog $ checkConvertNarrow @Integer @Natural+ it "A = Signed, B = Natural" $ hedgehog $ checkConvertNarrow @Signed @Natural+ it "A = Integer, B = Positive" $ hedgehog $ checkConvertNarrow @Integer @Positive+ it "A = Signed, B = Positive" $ hedgehog $ checkConvertNarrow @Signed @Positive+ it "A = Natural, B = Positive" $ hedgehog $ checkConvertNarrow @Natural @Positive++checkConvertConvert ::+ forall a b m.+ (GenIntegral a, IntegerEquiv a b) =>+ Monad m =>+ PropertyT m ()+checkConvertConvert = do+ x :: a <- forAll Gen.integral+ convert (convert x :: b) === x++checkNarrowConvert ::+ forall a b m.+ (GenIntegral a, IntegerConvert a b, IntegerNarrow b a) =>+ Monad m =>+ PropertyT m ()+checkNarrowConvert = do+ x :: a <- forAll Gen.integral+ narrow (convert x :: b) === Just x++checkConvertNarrow ::+ forall a b m.+ (GenIntegral a, BoundedBelow b) =>+ (IntegerConvert b a, IntegerNarrow a b) =>+ (Show b, Eq b) =>+ Monad m =>+ PropertyT m ()+checkConvertNarrow = do+ x :: a <- forAll Gen.integral+ let y :: Maybe b = narrow x+ if x Ord.>= convert (minBound @b)+ then do+ z <- evalMaybe y+ convert z === x+ else y === Nothing
+ test/Spec/Deepseq.hs view
@@ -0,0 +1,36 @@+module Spec.Deepseq where++import Control.DeepSeq (NFData, ($!!))+import Control.Exception (Exception, throw)+import Control.Monad.Catch qualified as Exception (MonadCatch, try)+import Data.Either (Either (..))+import Essentials+import Integer (Sign (MinusSign), Signed (NonZero))+import Test.Hspec (Expectation, Spec, context, it, shouldBe)++spec :: Spec+spec =+ context "deepseq @Signed" do+ it "can succeed" do+ x <- force (NonZero MinusSign 5)+ x ~> Right (-5)+ it "can force an error" do+ x <- force (throw X)+ x ~> Left X+ it "can force an error in sign" do+ x <- force (NonZero (throw X) 5)+ x ~> Left X+ it "can force an error in magnitude" do+ x <- force (NonZero MinusSign (throw X))+ x ~> Left X++(~>) :: Either X Signed -> Either X Signed -> Expectation+(~>) = shouldBe @(Either X Signed)++data X = X+ deriving stock (Eq, Show)++instance Exception X++force :: NFData a => Exception.MonadCatch m => a -> m (Either X a)+force x = Exception.try (pure $!! x)
+ test/Spec/Enum.hs view
@@ -0,0 +1,74 @@+module Spec.Enum where++import Data.List (take)+import Integer (Positive)+import Test.Hspec (Expectation, Spec, context, it, shouldBe)++(~>) :: [Positive] -> [Positive] -> Expectation+(~>) = shouldBe @[Positive]++spec :: Spec+spec =+ context "Enum @Positive" do+ context "[a ..]" do+ it "counts upward" do+ take 3 [5 ..] ~> [5, 6, 7]+ it "can start with 1" do+ take 3 [1 ..] ~> [1, 2, 3]++ context "[a .. b]" do+ it "counts upward" do+ [5 .. 8] ~> [5, 6, 7, 8]+ it "can start with 1" do+ [1 .. 5] ~> [1, 2, 3, 4, 5]+ it "does not count downward" do+ [8 .. 5] ~> []+ [8 .. 7] ~> []+ it "can return 1 item" do+ [3 .. 3] ~> [3]+ [1 .. 1] ~> [1]++ context "[a, b ..]" do+ it "can count upward by 1" do+ take 5 [5, 6 ..] ~> [5, 6, 7, 8, 9]+ take 5 [1, 2 ..] ~> [1, 2, 3, 4, 5]+ it "can count downward by 1" do+ [5, 4 ..] ~> [5, 4, 3, 2, 1]+ it "can count upward by 2" do+ take 5 [5, 7 ..] ~> [5, 7, 9, 11, 13]+ take 5 [1, 3 ..] ~> [1, 3, 5, 7, 9]+ it "can count downward by 2" do+ [9, 7 ..] ~> [9, 7, 5, 3, 1]+ it "can count downward by 2 without exactly reaching its lower bound" do+ [8, 6 ..] ~> [8, 6, 4, 2]+ it "can repeat 1 item indefinitely" do+ take 5 [4, 4 ..] ~> [4, 4, 4, 4, 4]++ context "[a, b .. c]" do+ it "can count upward by 1" do+ [5, 6 .. 9] ~> [5, 6, 7, 8, 9]+ [1, 2 .. 5] ~> [1, 2, 3, 4, 5]+ it "can count downward by 1" do+ [9, 8 .. 5] ~> [9, 8, 7, 6, 5]+ it "can count upward by 2" do+ [5, 7 .. 11] ~> [5, 7, 9, 11]+ [1, 3 .. 7] ~> [1, 3, 5, 7]+ it "can count upward without exactly reaching its upper bound" do+ [5, 7 .. 12] ~> [5, 7, 9, 11]+ it "can count downward by 2" do+ [11, 9 .. 5] ~> [11, 9, 7, 5]+ it "can count downward by 2 without exactly reaching its lower bound" do+ [11, 9 .. 4] ~> [11, 9, 7, 5]+ it "can count downward with a lower bound of 1" do+ [7, 5 .. 1] ~> [7, 5, 3, 1]+ [8, 6 .. 1] ~> [8, 6, 4, 2]+ it "can repeat 1 item indefinitely" do+ take 5 [4, 4 .. 9] ~> [4, 4, 4, 4, 4]+ take 5 [4, 4 .. 4] ~> [4, 4, 4, 4, 4]+ it "can return 1 item" do+ [4, 5 .. 4] ~> [4]+ [4, 3 .. 4] ~> [4]+ it "can return an empty list" do+ [4, 4 .. 3] ~> []+ [4, 5 .. 3] ~> []+ [5, 4 .. 6] ~> []
+ test/Spec/FiniteConversion.hs view
@@ -0,0 +1,72 @@+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)
+ test/Spec/Increase.hs view
@@ -0,0 +1,53 @@+module Spec.Increase where++import Control.Monad.Catch (MonadCatch)+import Essentials+import Integer+ ( Increase (..),+ Integer,+ Natural,+ Positive,+ Signed,+ StrictlyIncrease (..),+ )+import Integer.Gen (GenIntegral)+import Integer.Gen qualified as Gen+import Test.Hspec (Spec, context, it)+import Test.Hspec.Hedgehog (PropertyT, forAll, hedgehog, modifyMaxSuccess, (===))+import Prelude (toInteger, (+))++spec :: Spec+spec = do+ context "increase in A behaves the same as (+) in Integer" do+ modifyMaxSuccess (\_ -> 1000) do+ it "A = Natural" $ hedgehog $ checkIncrease @Natural+ it "A = Integer" $ hedgehog $ checkIncrease @Integer+ it "A = Positive" $ hedgehog $ checkIncrease @Positive+ it "A = Signed" $ hedgehog $ checkIncrease @Signed++ context "strictlyIncrease in A behaves the same as (+) in Integer" do+ modifyMaxSuccess (\_ -> 1000) do+ it "A = Natural" $ hedgehog $ checkStrictlyIncrease @Natural+ it "A = Integer" $ hedgehog $ checkStrictlyIncrease @Integer+ it "A = Positive" $ hedgehog $ checkStrictlyIncrease @Positive+ it "A = Signed" $ hedgehog $ checkStrictlyIncrease @Signed++checkIncrease ::+ forall a m.+ (GenIntegral a, Increase a) =>+ MonadCatch m =>+ PropertyT m ()+checkIncrease = do+ x :: Natural <- forAll Gen.integral+ y :: a <- forAll Gen.integral+ toInteger (increase x y) === toInteger x + toInteger y++checkStrictlyIncrease ::+ forall a m.+ (GenIntegral a, StrictlyIncrease a) =>+ MonadCatch m =>+ PropertyT m ()+checkStrictlyIncrease = do+ x :: Positive <- forAll Gen.integral+ y :: a <- forAll Gen.integral+ toInteger (strictlyIncrease x y) === toInteger x + toInteger y
+ test/Spec/Length.hs view
@@ -0,0 +1,14 @@+module Spec.Length where++import Data.List.NonEmpty (NonEmpty ((:|)))+import Integer.Natural qualified as Natural+import Integer.Positive qualified as Positive+import Test.Hspec (Spec, context, it, shouldBe)++spec :: Spec+spec =+ context "length" do+ it "Natural" do+ Natural.length "abc" `shouldBe` 3+ it "Positive" do+ Positive.length ('a' :| "bc") `shouldBe` 3
+ test/Spec/Multiplication.hs view
@@ -0,0 +1,32 @@+module Spec.Multiplication where++import Essentials+import Integer (Positive, Signed)+import Integer.Gen (GenIntegral)+import Integer.Gen qualified as Gen+import Test.Hspec (Spec, context, it)+import Test.Hspec.Hedgehog+ ( PropertyT,+ forAll,+ hedgehog,+ modifyMaxSuccess,+ (===),+ )+import Prelude (fromInteger, toInteger, (*))++spec :: Spec+spec =+ context "(*) behaves the same in A as in Integer" do+ modifyMaxSuccess (\_ -> 1000) do+ it "A = Positive" $ hedgehog $ check @Positive+ it "A = Signed" $ hedgehog $ check @Signed++check ::+ forall a m.+ GenIntegral a =>+ Monad m =>+ PropertyT m ()+check = do+ x :: a <- forAll Gen.integral+ y :: a <- forAll Gen.integral+ x * y === fromInteger (toInteger x * toInteger y)
+ test/Spec/SubtractOne.hs view
@@ -0,0 +1,21 @@+module Spec.SubtractOne where++import Essentials+import Integer (Positive, Signed)+import Integer.Gen qualified as Gen+import Integer.Positive qualified as Positive+import Integer.Signed qualified as Signed+import Test.Hspec (Spec, context, it)+import Test.Hspec.Hedgehog (forAll, hedgehog, modifyMaxSuccess, (===))+import Prelude (toInteger, (-))++spec :: Spec+spec =+ context "subtractOne in A behaves the same as (- 1) in Integer" do+ modifyMaxSuccess (\_ -> 1000) do+ it "A = Positive" $ hedgehog do+ x :: Positive <- forAll Gen.integral+ toInteger (Positive.subtractOne x) === toInteger x - 1+ it "A = Signed" $ hedgehog do+ x :: Signed <- forAll Gen.integral+ toInteger (Signed.subtractOne x) === toInteger x - 1
+ test/Spec/Subtraction.hs view
@@ -0,0 +1,66 @@+module Spec.Subtraction where++import Control.Exception (ArithException (Underflow))+import Control.Monad.Catch (MonadCatch, try)+import Data.Either qualified as Either+import Essentials+import Integer+ ( Integer,+ IntegerConvert (..),+ IntegerNarrow (..),+ Natural,+ Positive,+ Signed,+ Subtraction,+ Subtraction' (..),+ )+import Integer.Gen (GenIntegral)+import Integer.Gen qualified as Gen+import Test.Hspec (Spec, context, it)+import Test.Hspec.Hedgehog+ ( PropertyT,+ forAll,+ hedgehog,+ modifyMaxSuccess,+ (===),+ )+import Prelude (Num, toInteger, ($!), (-))++spec :: Spec+spec = do+ context "subtract in A behaves the same as (-) in B" do+ modifyMaxSuccess (\_ -> 1000) do+ it "A = Natural, B = Signed" $ hedgehog $ checkSubtract @Natural @Signed+ it "A = Natural, B = Integer" $ hedgehog $ checkSubtract @Natural @Integer+ it "A = Positive, B = Signed" $ hedgehog $ checkSubtract @Positive @Signed+ it "A = Positive, B = Integer" $ hedgehog $ checkSubtract @Positive @Integer++ context "(-) in A behaves the same as (-) in Integer if the result is in A, undefined otherwise" do+ modifyMaxSuccess (\_ -> 1000) do+ it "A = Positive" $ hedgehog $ checkNumMinus @Positive++checkSubtract ::+ forall a b m.+ (GenIntegral a, Subtraction a, Subtraction' b, Num b) =>+ (IntegerConvert a b, IntegerNarrow b a) =>+ (Eq b, Show b) =>+ MonadCatch m =>+ PropertyT m ()+checkSubtract = do+ x :: a <- forAll Gen.integral+ y :: a <- forAll Gen.integral+ (subtract x y :: b) === (convert x - convert y :: b)++checkNumMinus ::+ forall a m.+ (GenIntegral a, Subtraction a, IntegerNarrow Integer a) =>+ MonadCatch m =>+ PropertyT m ()+checkNumMinus = do+ x :: a <- forAll Gen.integral+ y :: a <- forAll Gen.integral+ case narrow (toInteger x - toInteger y) :: Maybe a of+ Just z -> x - y === z+ Nothing -> do+ z <- try (pure $! x - y)+ z === Either.Left Underflow
+ test/Spec/Yolo.hs view
@@ -0,0 +1,35 @@+module Spec.Yolo where++import Control.Exception qualified as Exception (ArithException (Underflow))+import Control.Monad.Catch qualified as Exception (MonadCatch, try)+import Data.Either qualified as Either+import Data.Ord qualified as Ord+import Essentials+import Hedgehog qualified+import Integer (BoundedBelow (..), Integer, Natural, Positive, yolo)+import Integer.Gen (GenIntegral)+import Integer.Gen qualified as Gen+import Test.Hspec (Spec, context, it)+import Test.Hspec.Hedgehog (hedgehog, (===))+import Prelude (($!))+import Prelude qualified as Num (toInteger)++spec :: Spec+spec =+ context "yolo (yolo x) = x, if Integer x is in range of A" do+ it "A = Positive" $ hedgehog $ check @Positive+ it "A = Natural " $ hedgehog $ check @Natural++check ::+ forall a m.+ (GenIntegral a, BoundedBelow a) =>+ Exception.MonadCatch m =>+ Hedgehog.PropertyT m ()+check = do+ x :: Integer <- Hedgehog.forAll Gen.integral+ let y :: a = yolo x+ if x Ord.>= Num.toInteger (minBound @a)+ then yolo y === x+ else do+ z <- Exception.try (pure $! y)+ z === Either.Left Exception.Underflow