packages feed

integer-types 0.1.3.0 → 0.1.4.0

raw patch · 18 files changed

+769/−355 lines, 18 files

Files

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