integer-types (empty) → 0.0.0.0
raw patch · 21 files changed
+1590/−0 lines, 21 filesdep +basedep +deepseqdep +exceptions
Dependencies added: base, deepseq, exceptions, hedgehog, hspec, integer-types
Files
- changelog.md +3/−0
- integer-generators/Integer/Gen.hs +122/−0
- integer-types.cabal +109/−0
- integer-types/Integer.hs +24/−0
- integer-types/Integer/BoundedBelow.hs +11/−0
- integer-types/Integer/Conversion.hs +93/−0
- integer-types/Integer/Finite.hs +59/−0
- integer-types/Integer/Integer.hs +65/−0
- integer-types/Integer/Natural.hs +86/−0
- integer-types/Integer/Positive.hs +75/−0
- integer-types/Integer/Positive/Unsafe.hs +160/−0
- integer-types/Integer/Sign.hs +24/−0
- integer-types/Integer/Signed.hs +213/−0
- integer-types/Integer/Subtraction.hs +47/−0
- license.txt +13/−0
- readme.md +135/−0
- test-integer-arithmetic/Main.hs +78/−0
- test-integer-conversions/Main.hs +91/−0
- test-integer-deepseq/Main.hs +40/−0
- test-integer-enum/Main.hs +71/−0
- test-integer-finite/Main.hs +71/−0
+ changelog.md view
@@ -0,0 +1,3 @@+# 0.0.0.0 (2022-11-29)++* Initial release
+ integer-generators/Integer/Gen.hs view
@@ -0,0 +1,122 @@+module Integer.Gen+ (+ GenIntegral (integral),+ GenFinite (finite),+ astronomical,+ )+ where++import Control.Applicative (pure, (<*>))+import Data.Function (id, ($))+import Data.Functor (fmap)+import Data.Int (Int)+import Data.Word (Word)+import Integer (BoundedBelow (..), Integer, Natural, Positive, Sign (..),+ Signed (..))+import Text.Show (Show)++import qualified Hedgehog+import qualified Hedgehog.Gen as Gen+import qualified Hedgehog.Range as Range+import qualified Prelude as Bounded (Bounded (..))+import qualified Prelude as Num (Integral (..), Num (..), (+), (^))++---++class (Num.Integral a, Show a) => GenIntegral a+ where+ -- | Generators for 'Integer', 'Natural', 'Positive',+ -- or 'Signed' selected from one of three methods:+ --+ -- * small numbers (magnitude less than ten)+ -- * large numbers (well in excess of 64-bit)+ -- * numbers at or around a bound of 'Int' or 'Word'+ integral :: Hedgehog.Gen a++instance GenIntegral Integer where integral = integer+instance GenIntegral Natural where integral = boundedBelow+instance GenIntegral Positive where integral = boundedBelow+instance GenIntegral Signed where integral = signed++---++class (Num.Integral a, Bounded.Bounded a, Show a) => GenFinite a+ where+ finite :: Hedgehog.Gen a++instance GenFinite Int where finite = defaultFinite++instance GenFinite Word where finite = defaultFinite++defaultFinite :: (Num.Integral a, Bounded.Bounded a) => Hedgehog.Gen a+defaultFinite = Gen.choice+ [ Gen.integral $ Range.linear Bounded.minBound Bounded.maxBound+ , Gen.integral $ Range.linear Bounded.maxBound Bounded.minBound+ ]++---++smol :: Num.Integral a => a+smol = 10++astronomical :: Num.Integral a => a+astronomical = 2 Num.^ (99 :: Integer)++bigRange :: Num.Integral a => Range.Range a+bigRange = Range.exponential smol astronomical++---++integer :: Hedgehog.Gen Integer+integer = Gen.choice [smolInteger, nearFiniteBoundInteger, bigInteger]++smolInteger :: Hedgehog.Gen Integer+smolInteger = Gen.integral $ Range.linearFrom 0 (Num.negate smol) smol++bigInteger :: Hedgehog.Gen Integer+bigInteger = Gen.element [id, Num.negate] <*> Gen.integral bigRange++nearFiniteBoundInteger :: Hedgehog.Gen Integer+nearFiniteBoundInteger = Gen.element [id, Num.negate] <*> nearPositiveFiniteBound++---++boundedBelow :: forall a. (BoundedBelow a, Num.Integral a) => Hedgehog.Gen a+boundedBelow = Gen.choice [smolBoundedBelow, nearPositiveFiniteBound, bigBoundedBelow]++smolBoundedBelow :: forall a. (BoundedBelow a, Num.Integral a) => Hedgehog.Gen a+smolBoundedBelow = fmap Num.fromInteger $ Gen.integral $ Range.linear (Num.toInteger $ minBound @a) smol++bigBoundedBelow :: forall a. (BoundedBelow a, Num.Integral a) => Hedgehog.Gen a+bigBoundedBelow = fmap Num.fromInteger $ Gen.integral bigRange++nearPositiveFiniteBound :: forall a. Num.Integral a => Hedgehog.Gen a+nearPositiveFiniteBound = fmap Num.fromInteger $+ pure (Num.+)+ <*> Gen.element+ [ Num.toInteger (Bounded.maxBound :: Int)+ , Num.toInteger (Bounded.maxBound :: Word)+ ]+ <*> smolInteger++---++signed :: Hedgehog.Gen Signed+signed = Gen.choice [smolSigned, nearFiniteBoundSigned, bigSigned]++smolSigned :: Hedgehog.Gen Signed+smolSigned = Gen.frequency+ [ (,) 1 $ pure Zero+ , (,) 9 $ pure NonZero <*> sign <*> smolBoundedBelow+ ]++bigSigned :: Hedgehog.Gen Signed+bigSigned = pure NonZero <*> sign <*> bigBoundedBelow++nearFiniteBoundSigned :: Hedgehog.Gen Signed+nearFiniteBoundSigned = pure NonZero <*> sign <*> nearPositiveFiniteBound++---++sign :: Hedgehog.Gen Sign+sign = Gen.element [PlusSign, MinusSign]
+ integer-types.cabal view
@@ -0,0 +1,109 @@+cabal-version: 3.0++name: integer-types+version: 0.0.0.0++category: Numeric+synopsis: Integer, Natural, and Positive++description:+ The base package contains two unbounded integral types:+ Integer (-∞, ∞) and Natural (0, ∞). This package expands that+ integral repertoire with the addition of Positive (1, ∞).++author: Chris Martin+maintainer: Chris Martin, Julie Moronuki++homepage: https://github.com/typeclasses/integer-types+bug-reports: https://github.com/typeclasses/integer-types/issues++license: Apache-2.0+license-file: license.txt++extra-doc-files: readme.md changelog.md++common base+ default-language: GHC2021+ ghc-options: -Wall+ default-extensions:+ MultiParamTypeClasses+ NoGeneralizedNewtypeDeriving+ NoImplicitPrelude+ PatternSynonyms+ ViewPatterns+ build-depends:+ base ^>= 4.16 || ^>= 4.17+ , deepseq ^>= 1.4.6++common test+ import: base+ default-extensions:+ AllowAmbiguousTypes+ build-depends:+ exceptions+ , integer-types++common test-with-hedgehog+ import: test+ build-depends:+ hedgehog ^>= 1.1 || ^>= 1.2++common test-with-hspec+ import: test+ build-depends:+ hspec++library+ import: base+ hs-source-dirs: integer-types+ exposed-modules:+ Integer+ Integer.BoundedBelow+ Integer.Conversion+ Integer.Finite+ Integer.Integer+ Integer.Natural+ Integer.Positive+ Integer.Sign+ Integer.Signed+ Integer.Subtraction+ other-modules:+ Integer.Positive.Unsafe++library integer-generators+ import: test-with-hedgehog+ hs-source-dirs: integer-generators+ exposed-modules: Integer.Gen++test-suite test-integer-arithmetic+ import: test-with-hedgehog+ type: exitcode-stdio-1.0+ main-is: Main.hs+ hs-source-dirs: test-integer-arithmetic+ build-depends: integer-generators++test-suite test-integer-conversions+ import: test-with-hedgehog+ type: exitcode-stdio-1.0+ main-is: Main.hs+ hs-source-dirs: test-integer-conversions+ build-depends: integer-generators++test-suite test-integer-enum+ import: test-with-hspec+ type: exitcode-stdio-1.0+ main-is: Main.hs+ hs-source-dirs: test-integer-enum++test-suite test-integer-deepseq+ import: test-with-hspec+ type: exitcode-stdio-1.0+ main-is: Main.hs+ hs-source-dirs: test-integer-deepseq++test-suite test-integer-finite+ import: test-with-hedgehog+ type: exitcode-stdio-1.0+ main-is: Main.hs+ hs-source-dirs: test-integer-finite+ build-depends: integer-generators
+ integer-types/Integer.hs view
@@ -0,0 +1,24 @@+{-# language Safe #-}++module Integer+ (+ {- ** Types -} Integer, Natural, Positive,+ Signed (Zero, NonZero, Minus, Plus), Sign (MinusSign, PlusSign),+ {- ** Subtraction -} Subtraction (subtractInteger, subtractSigned), Subtraction' (subtract),+ {- ** Conversion -} IntegerNarrow (narrow), IntegerConvert (convert), IntegerEquiv, yolo, ConvertWithFinite (toInt, fromInt, toWord, fromWord), Finite (..),+ {- ** Lower bound -} BoundedBelow (minBound),+ )+ where++import Integer.BoundedBelow (BoundedBelow (minBound))+import Integer.Conversion (IntegerConvert (convert), IntegerEquiv,+ IntegerNarrow (narrow), yolo)+import Integer.Finite (ConvertWithFinite (fromInt, fromWord, toInt, toWord),+ Finite (..))+import Integer.Integer (Integer)+import Integer.Natural (Natural)+import Integer.Positive (Positive)+import Integer.Sign (Sign (MinusSign, PlusSign))+import Integer.Signed (Signed (Minus, NonZero, Plus, Zero))+import Integer.Subtraction (Subtraction (subtractInteger, subtractSigned),+ Subtraction' (subtract))
+ integer-types/Integer/BoundedBelow.hs view
@@ -0,0 +1,11 @@+module Integer.BoundedBelow where++import Numeric.Natural (Natural)++class BoundedBelow a where+ minBound :: a++instance BoundedBelow Natural where+ minBound = 0++
+ integer-types/Integer/Conversion.hs view
@@ -0,0 +1,93 @@+{-# language Safe #-}++module Integer.Conversion+ (+ IntegerNarrow (narrow),+ IntegerConvert (convert),+ IntegerEquiv,+ yolo,+ )+ where++import Data.Function (id, (.))+import Data.Maybe (Maybe (..))+import Integer.Integer (Integer)+import Integer.Natural (Natural)+import Integer.Positive (Positive)+import Integer.Signed (Signed)++import qualified Integer.Integer as Integer+import qualified Integer.Natural as Natural+import qualified Integer.Positive as Positive+import qualified Integer.Signed as Signed+import qualified Prelude as Num (Integral (..), Num (..))++class IntegerNarrow a b => IntegerConvert a b where+ convert :: a -> b++class IntegerNarrow a b where+ narrow :: a -> Maybe b++class (IntegerConvert a b, IntegerConvert b a) => IntegerEquiv a b+++--- Isomorphisms ---++instance IntegerEquiv Integer Integer+instance IntegerConvert Integer Integer where convert = id+instance IntegerNarrow Integer Integer where narrow = Just++instance IntegerEquiv Natural Natural+instance IntegerConvert Natural Natural where convert = id+instance IntegerNarrow Natural Natural where narrow = Just++instance IntegerEquiv Positive Positive+instance IntegerConvert Positive Positive where convert = id+instance IntegerNarrow Positive Positive where narrow = Just++instance IntegerEquiv Signed Signed+instance IntegerConvert Signed Signed where convert = id+instance IntegerNarrow Signed Signed where narrow = Just++instance IntegerEquiv Integer Signed+instance IntegerConvert Integer Signed where convert = Integer.toSigned+instance IntegerNarrow Integer Signed where narrow = Just . convert++instance IntegerEquiv Signed Integer+instance IntegerConvert Signed Integer where convert = Signed.toInteger+instance IntegerNarrow Signed Integer where narrow = Just . convert+++--- Prisms ---++instance IntegerNarrow Integer Natural where narrow = Integer.toNatural+instance IntegerNarrow Natural Integer where narrow = Just . convert+instance IntegerConvert Natural Integer where convert = Natural.toInteger++instance IntegerNarrow Signed Natural where narrow = Signed.toNatural+instance IntegerNarrow Natural Signed where narrow = Just . convert+instance IntegerConvert Natural Signed where convert = Natural.toSigned++instance IntegerNarrow Integer Positive where narrow = Integer.toPositive+instance IntegerNarrow Positive Integer where narrow = Just . convert+instance IntegerConvert Positive Integer where convert = Positive.toInteger++instance IntegerNarrow Natural Positive where narrow = Natural.toPositive+instance IntegerNarrow Positive Natural where narrow = Just . convert+instance IntegerConvert Positive Natural where convert = Positive.toNatural++instance IntegerNarrow Signed Positive where narrow = Signed.toPositive+instance IntegerNarrow Positive Signed where narrow = Just . convert+instance IntegerConvert Positive Signed where convert = Positive.toSigned+++--- lol ---++-- | Partial conversion between 'Num.Integral' types via 'Integer'+--+-- @+-- yolo = 'Num.fromInteger' . 'Num.toInteger'+-- @+--+yolo :: (Num.Integral a, Num.Num b) => a -> b+yolo = Num.fromInteger . Num.toInteger
+ integer-types/Integer/Finite.hs view
@@ -0,0 +1,59 @@+module Integer.Finite where++import Data.Function ((.))+import Data.Int (Int)+import Data.Maybe (Maybe)+import Data.Word (Word)+import Integer.Integer (Integer)+import Integer.Natural (Natural)+import Integer.Positive (Positive)+import Integer.Signed (Signed)+import Prelude (Bounded, Integral)++import qualified Data.Maybe as Maybe+import qualified Integer.Integer as Integer+import qualified Integer.Natural as Natural+import qualified Integer.Positive as Positive+import qualified Integer.Signed as Signed++class ConvertWithFinite a where+ toWord :: a -> Maybe Word+ fromWord :: Word -> Maybe a+ toInt :: a -> Maybe Int+ fromInt :: Int -> Maybe a++instance ConvertWithFinite Natural where+ toWord = Natural.toWord+ fromWord = Maybe.Just . Natural.fromWord+ toInt = Natural.toInt+ fromInt = Natural.fromInt++instance ConvertWithFinite Positive where+ toWord = Positive.toWord+ fromWord = Positive.fromWord+ toInt = Positive.toInt+ fromInt = Positive.fromInt++instance ConvertWithFinite Integer where+ toWord = Integer.toWord+ fromWord = Maybe.Just . Integer.fromWord+ toInt = Integer.toInt+ fromInt = Maybe.Just . Integer.fromInt++instance ConvertWithFinite Signed where+ toWord = Signed.toWord+ fromWord = Maybe.Just . Signed.fromWord+ toInt = Signed.toInt+ fromInt = Maybe.Just . Signed.fromInt++class (Bounded b, Integral b) => Finite b where+ toFinite :: ConvertWithFinite a => a -> Maybe b+ fromFinite :: ConvertWithFinite a => b -> Maybe a++instance Finite Int where+ toFinite = toInt+ fromFinite = fromInt++instance Finite Word where+ toFinite = toWord+ fromFinite = fromWord
+ integer-types/Integer/Integer.hs view
@@ -0,0 +1,65 @@+{-# language Safe #-}++module Integer.Integer+ (+ {- * Type -} Integer,+ {- * Conversion -}+ {- ** Positive -} toPositive, fromPositive,+ {- ** Natural -} toNatural, fromNatural,+ {- ** Signed -} toSigned, fromSigned,+ {- ** Int -} toInt, fromInt,+ {- ** Word -} toWord, fromWord,+ )+ where++import Data.Int (Int)+import Data.Maybe (Maybe (..))+import Data.Word (Word)+import Integer.Positive (Positive)+import Integer.Signed (Signed (..))+import Numeric.Natural (Natural)+import Prelude (Integer)++import qualified Data.Bool as Bool+import qualified Data.Ord as Ord+import qualified Integer.Natural as Natural+import qualified Integer.Positive as Positive+import qualified Integer.Signed as Signed+import qualified Prelude as Bounded (Bounded (..))+import qualified Prelude as Num (Integral (..), Num (..))++toPositive :: Integer -> Maybe Positive+toPositive = Positive.fromInteger++fromPositive :: Positive -> Integer+fromPositive = Positive.toInteger++toNatural :: Integer -> Maybe Natural+toNatural = Natural.fromInteger++fromNatural :: Natural -> Integer+fromNatural = Natural.toInteger++toSigned :: Integer -> Signed+toSigned = Signed.fromInteger++fromSigned :: Signed -> Integer+fromSigned = Signed.toInteger++toInt :: Integer -> Maybe Int+toInt x = if ok then Just (Num.fromInteger x) else Nothing+ where+ ok = x Ord.>= Num.toInteger (Bounded.minBound :: Int) Bool.&&+ x Ord.<= Num.toInteger (Bounded.maxBound :: Int)++fromInt :: Int -> Integer+fromInt = Num.toInteger++toWord :: Integer -> Maybe Word+toWord x = if ok then Just (Num.fromInteger x) else Nothing+ where+ ok = x Ord.>= Num.toInteger (Bounded.minBound :: Word) Bool.&&+ x Ord.<= Num.toInteger (Bounded.maxBound :: Word)++fromWord :: Word -> Integer+fromWord = Num.toInteger
+ integer-types/Integer/Natural.hs view
@@ -0,0 +1,86 @@+{-# language Trustworthy #-}++module Integer.Natural+ (+ {- * Type -} Natural,+ {- * Subtraction -} subtract,+ {- * Conversion -}+ {- ** Positive -} toPositive, fromPositive,+ {- ** Integer -} toInteger, fromInteger,+ {- ** Signed -} toSigned, fromSigned,+ {- ** Int -} toInt, fromInt,+ {- ** Word -} toWord, fromWord,+ {- * One (1) -} one, addOne, subtractOne,+ )+ where++import Data.Function (($))+import Data.Int (Int)+import Data.Maybe (Maybe (..))+import Data.Word (Word)+import Integer.Signed (Signed (..))+import Numeric.Natural (Natural)+import Prelude (Integer)++import qualified Data.Ord as Ord+import qualified Integer.Positive as Positive+import qualified Integer.Positive.Unsafe as Positive.Unsafe+import qualified Integer.Signed as Signed+import qualified Prelude as Bounded (Bounded (..))+import qualified Prelude as Num (Integral (..), Num (..))++toPositive :: Natural -> Maybe Positive.Unsafe.Positive+toPositive = Positive.fromNatural++fromPositive :: Positive.Unsafe.Positive -> Natural+fromPositive = Positive.toNatural++fromInteger :: Integer -> Maybe Natural+fromInteger x = if x Ord.>= 0 then Just (Num.fromInteger x) else Nothing++toInteger :: Natural -> Integer+toInteger = Num.toInteger++toSigned :: Natural -> Signed+toSigned = Signed.fromNatural++fromSigned :: Signed -> Maybe Natural+fromSigned = Signed.toNatural++toInt :: Natural -> Maybe Int+toInt x = if ok then Just (Num.fromInteger x') else Nothing+ where+ ok = x' Ord.<= Num.toInteger (Bounded.maxBound :: Int)+ x' = Num.toInteger x++fromInt :: Int -> Maybe Natural+fromInt x = if ok then Just (Num.fromInteger x') else Nothing+ where+ ok = x Ord.>= 0+ x' = Num.toInteger x++toWord :: Natural -> Maybe Word+toWord x = if ok then Just (Num.fromInteger x') else Nothing+ where+ ok = x' Ord.<= Num.toInteger (Bounded.maxBound :: Word)+ x' = Num.toInteger x++fromWord :: Word -> Natural+fromWord x = Num.fromInteger (Num.toInteger x)++subtract :: Natural -> Natural -> Signed+subtract a b = case Ord.compare a b of+ Ord.EQ -> Zero+ Ord.GT -> Plus $ Positive.Unsafe.fromNatural $ (Num.-) a b+ Ord.LT -> Minus $ Positive.Unsafe.fromNatural $ (Num.-) b a++one :: Natural+one = 1++addOne :: Integer -> Integer+addOne = (Num.+ 1)++subtractOne :: Natural -> Maybe Signed+subtractOne x = case x of+ 0 -> Nothing+ p -> Just (subtract p 1)
+ integer-types/Integer/Positive.hs view
@@ -0,0 +1,75 @@+{-# language Trustworthy #-}++module Integer.Positive+ (+ {- * Type -} Positive,+ {- * Subtraction -} subtract,+ {- * Conversion -}+ {- ** Natural -} toNatural, fromNatural,+ {- ** Integer -} toInteger, fromInteger,+ {- ** Signed -} toSigned, fromSigned,+ {- ** Int -} toInt, fromInt,+ {- ** Word -} toWord, fromWord,+ {- * One (1) -} one, addOne, subtractOne,+ )+ where++import Data.Function (($))+import Data.Int (Int)+import Data.Maybe (Maybe (..))+import Data.Word (Word)+import Integer.Positive.Unsafe (Positive, addOne, one, toInteger, toNatural)+import Integer.Signed (Signed (..))+import Numeric.Natural (Natural)+import Prelude (Integer)++import qualified Data.Ord as Ord+import qualified Integer.Positive.Unsafe as Unsafe+import qualified Prelude as Bounded (Bounded (..))+import qualified Prelude as Num (Integral (..), Num (..))++fromInteger :: Integer -> Maybe Positive+fromInteger x = if x Ord.> 0 then Just (Unsafe.fromInteger x) else Nothing++fromNatural :: Natural -> Maybe Positive+fromNatural x = case x of 0 -> Nothing; _ -> Just (Unsafe.fromNatural x)++toInt :: Positive -> Maybe Int+toInt x = if ok then Just (Num.fromInteger x') else Nothing+ where+ ok = x' Ord.<= Num.toInteger (Bounded.maxBound :: Int)+ x' = Num.toInteger x++fromInt :: Int -> Maybe Positive+fromInt x = if ok then Just (Num.fromInteger x') else Nothing+ where+ ok = x' Ord.>= 1+ x' = Num.toInteger x++toWord :: Positive -> Maybe Word+toWord x = if ok then Just (Num.fromInteger x') else Nothing+ where+ ok = x' Ord.<= Num.toInteger (Bounded.maxBound :: Word)+ x' = Num.toInteger x++fromWord :: Word -> Maybe Positive+fromWord x = if ok then Just (Num.fromInteger x') else Nothing+ where+ ok = x' Ord.>= 1+ x' = Num.toInteger x++subtract :: Positive -> Positive -> Signed+subtract a b = case Ord.compare a b of+ Ord.EQ -> Zero+ Ord.GT -> Plus $ Unsafe.subtract a b+ Ord.LT -> Minus $ Unsafe.subtract b a++subtractOne :: Positive -> Natural+subtractOne x = toNatural x Num.- 1++toSigned :: Positive -> Signed+toSigned = Plus++fromSigned :: Signed -> Maybe Positive+fromSigned (Plus x) = Just x+fromSigned _ = Nothing
+ integer-types/Integer/Positive/Unsafe.hs view
@@ -0,0 +1,160 @@+{-# language Unsafe #-}++{- | This module is unsafe not merely in the sense that it contains partial+functions, but moreover than it is capable of constructing the invalid+'Positive' value @'FromNatural' 0@ representing zero, which is not positive.+When a function has "checked" in its name, this indicates that it is partial but+will never construct an invalid 'Positive'. -}++module Integer.Positive.Unsafe+ (+ {- * Type -} Positive (FromNatural),+ {- * Conversion -}+ {- ** Natural -} toNatural, fromNatural, fromNaturalChecked,+ {- ** Integer -} toInteger, fromInteger, fromIntegerChecked,+ {- ** Int -} toInt, fromInt, fromIntChecked,+ {- * Arithmetic -} subtract, subtractChecked,+ {- * One (1) -} one, addOne, subtractOne, subtractOneChecked,+ )+ where++import Data.Function (const, id, ($), (.))+import Integer.BoundedBelow (BoundedBelow)+import Numeric.Natural (Natural)+import Prelude (Enum, Eq, Int, Integer, Integral, Num, Ord, Real, Show)++import qualified Control.DeepSeq as DeepSeq+import qualified Control.Exception as Exception+import qualified Data.Bits as Bits+import qualified Data.List as List+import qualified Data.Maybe as Maybe+import qualified Data.Ord as Ord+import qualified Integer.BoundedBelow as BoundedBelow+import qualified Prelude as Enum (Enum (..))+import qualified Prelude as Num (Integral (..), Num (..), Real (..),+ fromIntegral)+import qualified Text.Show as Show++newtype Positive = FromNatural{ toNatural :: Natural } deriving (Eq, Ord)++instance DeepSeq.NFData Positive where rnf (FromNatural x) = DeepSeq.rnf x++fromNatural :: Natural -> Positive+fromNatural = FromNatural++fromNaturalChecked :: Natural -> Positive+fromNaturalChecked x = case x of 0 -> Exception.throw Exception.Underflow; _ -> fromNatural x++toInteger :: Positive -> Integer+toInteger = Num.toInteger . toNatural++fromInteger :: Integer -> Positive+fromInteger = fromNatural . Num.fromInteger++fromIntegerChecked :: Integer -> Positive+fromIntegerChecked x = if x Ord.>= 1 then fromInteger x else Exception.throw Exception.Underflow++add :: Positive -> Positive -> Positive+add a b = fromNatural (toNatural a Num.+ toNatural b)++subtract :: Positive -> Positive -> Positive+subtract a b = fromNatural (toNatural a Num.- toNatural b)++subtractChecked :: Positive -> Positive -> Positive+subtractChecked a b = if a Ord.> b then subtract a b else Exception.throw Exception.Underflow++multiply :: Positive -> Positive -> Positive+multiply a b = fromNatural (toNatural a Num.* toNatural b)++one :: Positive+one = fromNatural 1++addOne :: Positive -> Positive+addOne = fromNatural . (Num.+ 1) . toNatural++subtractOne :: Positive -> Positive+subtractOne = fromNatural . (Num.- 1) . toNatural++subtractOneChecked :: Positive -> Positive+subtractOneChecked x = case x of { 1 -> Exception.throw Exception.Underflow; _ -> subtractOne x }++toInt :: Positive -> Int+toInt = Num.fromIntegral . toNatural++toIntChecked :: Positive -> Int+toIntChecked = Maybe.fromMaybe (Exception.throw Exception.Overflow) . Bits.toIntegralSized . toNatural++fromInt :: Int -> Positive+fromInt = fromNatural . Num.fromIntegral++fromIntChecked :: Int -> Positive+fromIntChecked x = case Num.signum x of { 1 -> fromInt x; _ -> Exception.throw Exception.Underflow }++enumFrom :: Positive -> [Positive]+enumFrom = List.map fromNatural . Enum.enumFrom . toNatural++enumFromTo :: Positive -> Positive -> [Positive]+enumFromTo a b = List.map fromNatural $ Enum.enumFromTo (toNatural a) (toNatural b)++enumFromThen :: Positive -> Positive -> [Positive]+enumFromThen a b = if a Ord.< b then ascending else descending+ where+ ascending = List.map fromNatural $ Enum.enumFromThen (toNatural a) (toNatural b)+ descending = List.map fromInteger $ List.takeWhile (Ord.>= 1) $+ Enum.enumFromThen (toInteger a) (toInteger b)++enumFromThenTo :: Positive -> Positive -> Positive -> [Positive]+enumFromThenTo a b c = if a Ord.< b then ascending else descending+ where+ ascending = List.map fromNatural $ Enum.enumFromThenTo (toNatural a) (toNatural b) (toNatural c)+ descending = List.map fromInteger $ List.takeWhile (Ord.>= 1) $+ Enum.enumFromThenTo (toInteger a) (toInteger b) (toInteger c)++type Div a = a -> a -> (a, a)++divisionOp :: Div Natural -> Div Positive+divisionOp o a b =+ let (q, r) = o (toNatural a) (toNatural b)+ in (fromNaturalChecked q, fromNaturalChecked r)++instance BoundedBelow Positive+ where+ minBound = 1++instance Num Positive+ where+ abs = id+ negate = const (Exception.throw Exception.Underflow)+ signum = const (fromNatural 1)+ fromInteger = fromIntegerChecked+ (+) = add+ (*) = multiply+ (-) = subtractChecked++instance Enum Positive+ where+ succ = addOne+ pred = subtractOneChecked++ fromEnum = toIntChecked+ toEnum = fromIntChecked++ enumFrom = enumFrom+ enumFromTo = enumFromTo+ enumFromThen = enumFromThen+ enumFromThenTo = enumFromThenTo++instance Real Positive+ where+ toRational = Num.toRational . toInteger++instance Integral Positive+ where+ toInteger = toInteger+ quotRem = divisionOp Num.quotRem+ divMod = divisionOp Num.divMod++instance Show Positive+ where+ show = Show.show . toNatural+ showsPrec i = Show.showsPrec i . toNatural
+ integer-types/Integer/Sign.hs view
@@ -0,0 +1,24 @@+{-# language Safe #-}++module Integer.Sign+ (+ {- * Type -} Sign (..),+ {- * Operations -} negate, multiply,+ )+ where++import Prelude (Eq, Ord, Show, seq, (==))++import qualified Control.DeepSeq as DeepSeq++data Sign = MinusSign | PlusSign+ deriving (Eq, Ord, Show)++instance DeepSeq.NFData Sign where rnf x = seq x ()++negate :: Sign -> Sign+negate PlusSign = MinusSign+negate MinusSign = PlusSign++multiply :: Sign -> Sign -> Sign+multiply a b = if a == b then PlusSign else MinusSign
+ integer-types/Integer/Signed.hs view
@@ -0,0 +1,213 @@+{-# language Trustworthy #-}++module Integer.Signed+ (+ {- * Type -} Signed (Zero, NonZero, Plus, Minus, NotPlus, NotMinus),+ {- * Conversion -}+ {- ** Integer -} toInteger, fromInteger,+ {- ** Natural -} toNatural, fromNatural,+ {- ** Positive -} toPositive, fromPositive,+ {- ** Int -} toInt, fromInt,+ {- ** Word -} toWord, fromWord,+ )+ where++import Data.Function (($), (.))+import Data.Int (Int)+import Data.Maybe (Maybe (..))+import Data.Word (Word)+import Integer.Positive.Unsafe (Positive)+import Integer.Sign (Sign (..))+import Numeric.Natural (Natural)+import Prelude (Enum, Eq, Integer, Integral, Num, Ord, Real, Show, seq)++import qualified Control.DeepSeq as DeepSeq+import qualified Data.List as List+import qualified Data.Ord as Ord+import qualified Integer.Positive.Unsafe as Positive.Unsafe+import qualified Integer.Sign as Sign+import qualified Prelude as Bounded (Bounded (..))+import qualified Prelude as Enum (Enum (..))+import qualified Prelude as Num (Integral (..), Num (..), Real (..))+import qualified Text.Show as Show++data Signed = Zero | NonZero Sign Positive+ deriving (Eq)++instance Ord Signed where+ compare Zero Zero = Ord.EQ++ compare Zero (Minus _) = Ord.GT+ compare Zero (Plus _ ) = Ord.LT+ compare (Minus _) Zero = Ord.LT+ compare (Plus _) Zero = Ord.GT++ compare (Plus _) (Minus _) = Ord.GT+ compare (Minus _) (Plus _) = Ord.LT+ compare (Plus a) (Plus b) = Ord.compare a b+ compare (Minus a) (Minus b) = Ord.compare b a++instance DeepSeq.NFData Signed where+ rnf Zero = ()+ rnf (NonZero a b) = a `seq` b `seq` ()++pattern Minus :: Positive -> Signed+pattern Minus x = NonZero MinusSign x+pattern Plus :: Positive -> Signed++pattern Plus x = NonZero PlusSign x++-- | A 'Signed' that is either zero or positive+pattern NotMinus :: Natural -> Signed+pattern NotMinus x <- (toNatural -> Just x)+ where NotMinus = fromNatural++-- | A 'Signed' that is either zero or negative;+-- the 'Natural' gives the magnitude of the negative+pattern NotPlus :: Natural -> Signed+pattern NotPlus x <- ((toNatural . negate) -> Just x)+ where NotPlus = negate . fromNatural++{-# complete Zero, Minus, Plus #-}+{-# complete Plus, NotPlus #-}+{-# complete Minus, NotMinus #-}++fromPositive :: Positive -> Signed+fromPositive = Plus++toPositive :: Signed -> Maybe Positive+toPositive (Plus x) = Just x+toPositive _ = Nothing++fromNatural :: Natural -> Signed+fromNatural 0 = Zero+fromNatural x = Plus $ Positive.Unsafe.fromNatural x++toNatural :: Signed -> Maybe Natural+toNatural (Minus _) = Nothing+toNatural Zero = Just 0+toNatural (Plus x) = Just (Positive.Unsafe.toNatural x)++add :: Signed -> Signed -> Signed+add Zero x = x+add x Zero = x+add (NonZero sa a) (NonZero sb b) = case (sa, sb) of+ (PlusSign, PlusSign) -> Plus $ a Num.+ b+ (MinusSign, MinusSign) -> Minus $ a Num.+ b++ (MinusSign, PlusSign) -> case Ord.compare a b of+ Ord.EQ -> Zero+ Ord.LT -> Plus $ Positive.Unsafe.subtract b a+ Ord.GT -> Minus $ Positive.Unsafe.subtract a b++ (PlusSign, MinusSign) -> case Ord.compare a b of+ Ord.EQ -> Zero+ Ord.LT -> Minus $ Positive.Unsafe.subtract b a+ Ord.GT -> Plus $ Positive.Unsafe.subtract a b++negate :: Signed -> Signed+negate Zero = Zero+negate (NonZero s x) = NonZero (Sign.negate s) x++multiply :: Signed -> Signed -> Signed+multiply Zero _ = Zero+multiply _ Zero = Zero+multiply (NonZero sa a) (NonZero sb b) =+ NonZero (Sign.multiply sa sb) (a Num.* b)++abs :: Signed -> Signed+abs Zero = Zero+abs x@(NonZero s p) = case s of+ PlusSign -> x+ MinusSign -> NonZero PlusSign p++signum :: Signed -> Signed+signum Zero = Zero+signum (NonZero s _) = NonZero s Positive.Unsafe.one++fromInteger :: Integer -> Signed+fromInteger x = case Ord.compare x 0 of+ Ord.EQ -> Zero+ Ord.LT -> Minus $ Positive.Unsafe.fromInteger $ Num.abs x+ Ord.GT -> Plus $ Positive.Unsafe.fromInteger x++toInteger :: Signed -> Integer+toInteger Zero = 0+toInteger (Plus x) = Positive.Unsafe.toInteger x+toInteger (Minus x) = Num.negate $ Positive.Unsafe.toInteger x++toInt :: Signed -> Maybe Int+toInt x = case x of+ Zero -> Just 0+ Plus p -> if ok then Just (Num.fromInteger i) else Nothing+ where+ ok = i Ord.<= Num.toInteger (Bounded.maxBound :: Int)+ i = Positive.Unsafe.toInteger p+ Minus p -> if ok then Just (Num.fromInteger i) else Nothing+ where+ ok = i Ord.>= Num.toInteger (Bounded.minBound :: Int)+ i = Num.negate (Positive.Unsafe.toInteger p)++fromInt :: Int -> Signed+fromInt x = case Ord.compare x 0 of+ Ord.EQ -> Zero+ Ord.GT -> Plus $ Positive.Unsafe.fromInt x+ Ord.LT -> Minus $ Positive.Unsafe.fromInteger $ Num.negate $ Num.toInteger x++toWord :: Signed -> Maybe Word+toWord x = case x of+ Zero -> Just 0+ Plus p -> if ok then Just (Num.fromInteger i) else Nothing+ where+ ok = i Ord.<= Num.toInteger (Bounded.maxBound :: Word)+ i = Positive.Unsafe.toInteger p+ Minus _ -> Nothing++fromWord :: Word -> Signed+fromWord x = case x of+ 0 -> Zero+ _ -> Plus $ Positive.Unsafe.fromInteger (Num.toInteger x)++type Div a = a -> a -> (a, a)++divisionOp :: Div Integer -> Div Signed+divisionOp o a b =+ let (q, r) = o (toInteger a) (toInteger b)+ in (fromInteger q, fromInteger r)++instance Num Signed+ where+ (+) = add+ (*) = multiply+ negate = negate+ abs = abs+ signum = signum+ fromInteger = fromInteger++instance Enum Signed+ where+ pred = fromInteger . Enum.pred . toInteger+ succ = fromInteger . Enum.succ . toInteger++ toEnum = fromInteger . Enum.toEnum+ fromEnum = Enum.fromEnum . toInteger++ enumFrom a = List.map fromInteger $ Enum.enumFrom (toInteger a)+ enumFromTo a b = List.map fromInteger $ Enum.enumFromTo (toInteger a) (toInteger b)+ enumFromThen a b = List.map fromInteger $ Enum.enumFromThen (toInteger a) (toInteger b)+ enumFromThenTo a b c = List.map fromInteger $ Enum.enumFromThenTo (toInteger a) (toInteger b) (toInteger c)++instance Real Signed+ where+ toRational = Num.toRational . toInteger++instance Integral Signed+ where+ toInteger = toInteger+ quotRem = divisionOp Num.quotRem+ divMod = divisionOp Num.divMod++instance Show Signed+ where+ show = Show.show . Num.toInteger+ showsPrec i = Show.showsPrec i . Num.toInteger
+ integer-types/Integer/Subtraction.hs view
@@ -0,0 +1,47 @@+module Integer.Subtraction+ (+ Subtraction (subtractInteger, subtractSigned),+ Subtraction' (subtract),+ )+ where++import Integer.Integer (Integer)+import Integer.Natural (Natural)+import Integer.Positive (Positive)+import Integer.Signed (Signed)++import qualified Integer.Natural as Natural+import qualified Integer.Positive as Positive+import qualified Integer.Signed as Signed+import qualified Prelude as Num (Num (..))++-- | Domain of a subtraction operation+class Subtraction a where+ subtractInteger :: a -> a -> Integer+ subtractInteger a b = Signed.toInteger (subtractSigned a b)++ subtractSigned :: a -> a -> Signed+ subtractSigned a b = Signed.fromInteger (subtractInteger a b)++instance Subtraction Integer where+ subtractInteger = (Num.-)++instance Subtraction Signed where+ subtractInteger a b = (Num.-) (Signed.toInteger a) (Signed.toInteger b)+ subtractSigned = (Num.-)++instance Subtraction Natural where+ subtractSigned = Natural.subtract++instance Subtraction Positive where+ subtractSigned = Positive.subtract++-- | Codomain of a subtraction operation+class Subtraction' b where+ subtract :: Subtraction a => a -> a -> b++instance Subtraction' Integer where+ subtract = subtractInteger++instance Subtraction' Signed where+ subtract = subtractSigned
+ license.txt view
@@ -0,0 +1,13 @@+Copyright 2022 Mission Valley Software LLC++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++ http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.
+ readme.md view
@@ -0,0 +1,135 @@+## Core types: Integer, Natural, Positive++The primary module of the `integer-types` package is `Integer`, which exports+the following integer-like types:++Type | Range+--------------|------------+`Integer` | (-∞, ∞)+`Natural` | (0, ∞)+`Positive` | (1, ∞)++## The Signed type++In addition to `Integer`, there is also an equivalent type called `Signed` that+is represented as:++```haskell+data Signed = Zero | NonZero Sign Positive++data Sign = MinusSign | PlusSign+```++`Signed` also comes with bundled pattern synonyms that allow it to be used as if+it had the following definition:++```haskell+data Signed = Minus Positive | Zero | Plus Positive+```++## Monomorphic conversions++The following modules contain monomorphic conversion functions:++- `Integer.Integer`+- `Integer.Natural`+- `Integer.Positive`+- `Integer.Signed`++For example, you can convert from `Positive` to `Integer` using either+`Integer.Positive.toInteger` or `Integer.Integer.fromPositive`, which are two+names for the same function of type `Positive -> Integer`.++Since not all integers are positive, the corresponding function in the reverse+direction has a `Maybe` codomain. `Integer.Integer.toPositive` and+`Integer.Positive.fromInteger` have the type `Integer -> Maybe Positive`.++## Polymorphic conversions++The `Integer` module exports two polymorphic conversion functions. The first is+for conversions that always succeed, such as `Positive -> Integer`.++```haskell+convert :: IntegerConvert a b => a -> b+```++The second is for conversions that may fail because they convert to a subset of+the domain, such as `Integer -> Maybe Positive`.++```haskell+narrow :: IntegerNarrow a b => a -> Maybe b+```++## Finite integer subsets++In addition to the conversion utilities discussed above, this library also+provides some minimal support for converting to/from the `Word` and `Int` types.+These are system-dependent finite subsets of `Integer` that are sometimes used+for performance reasons.++```haskell+toFinite :: (ConvertWithFinite a, Finite b) => a -> Maybe b+fromFinite :: (ConvertWithFinite a, Finite b) => b -> Maybe a+```++For example, `toFinite` may specialize as `Positive -> Maybe Int`, and+`fromFinite` may specialize as `Int -> Maybe Positive`.++## Monomorphic subtraction++For the `Integer` and `Signed` types that represent the full range of integers,+the standard arithmetic operations in the `Num` and `Integral` classes are+suitable.++For `Natural` and `Positive`, which are subsets of the integers, the standard+classes are not entirely appropriate. Consider, for example, subtraction.++```haskell+(-) :: Num a => a -> a -> a+```++`Natural` and `Positive` do belong to the `Num` class, but subtraction and some+other operations are partial; the expression `1 - 2` throws instead of returning+a value, because the integer result `-1` is negative and not representable by+either `Natural` or `Positive`.++For this reason, `Natural` and `Positive` have their own subtraction functions+that return `Signed`.++```haskell+-- from Integer.Positive+subtract :: Positive -> Positive -> Signed++-- from Integer.Natural+subtract :: Natural -> Natural -> Signed+```++## Polymorphic subtraction++In addition to the `(-)` method from the `Num` class and the `subtract`+functions for `Natural` and `Positive`, there are some polymorphic subtraction+functions in the `Integer` module. `subtractSigned` generalizes the two+monomorphic functions discussed in the previous section. Its codomain is+`Signed`.++```haskell+subtractSigned :: forall a. Subtraction a =>+ a -> a -> Signed+```++`subtractInteger` does the same thing, but gives the result as `Integer` instead+of `Signed`.++```haskell+subtractInteger :: forall a. Subtraction a =>+ a -> a -> Integer+```++The `subtract` function generalizes further. Its domain is any subtractable type+(`Natural`, `Positive`, `Integer`, or `Signed`) and its codomain is any type+that can represent the full range of integers (`Integer` or `Signed`).++```haskell+subtract :: forall b a. (Subtraction' b, Subtraction a) =>+ a -> a -> b+```
+ test-integer-arithmetic/Main.hs view
@@ -0,0 +1,78 @@+{-# language TemplateHaskell #-}+{-# options_ghc -fno-warn-missing-signatures #-}++module Main (main) where++import Integer++import Control.Applicative (pure)+import Control.Monad (Monad)+import Data.Eq (Eq)+import Data.Function (($), (.))+import Data.Maybe (Maybe (Just, Nothing))+import Hedgehog ((===))+import Integer.Gen (GenIntegral)+import Prelude (Num, fromInteger, toInteger, ($!), (*), (+), (-))+import Text.Show (Show)++import qualified Control.Exception as Exception (ArithException (Underflow))+import qualified Control.Monad.Catch as Exception (MonadCatch, try)+import qualified Data.Either as Either+import qualified Hedgehog+import qualified Hedgehog.Main as Hedgehog+import qualified Integer.Gen as Gen++main = Hedgehog.defaultMain [Hedgehog.checkSequential $$(Hedgehog.discover)]++testLimit :: Hedgehog.TestLimit+testLimit = 1000++property = Hedgehog.withTests testLimit . Hedgehog.property++-- | Assert that a closed binary 'Num' operation behaves the same+-- on a type as it does when applied via conversion with 'Integer'+checkNumOp :: forall a m. GenIntegral a => Monad m =>+ (forall b. Num b => b -> b -> b) -> Hedgehog.PropertyT m ()+checkNumOp o = do+ x :: a <- Hedgehog.forAll Gen.integral+ y :: a <- Hedgehog.forAll Gen.integral+ x `o` y === fromInteger (toInteger x `o` toInteger y)++-- | Assert that 'subtract' in @a@ gives the same result+-- as '(-)' via @b@.+checkSubtract :: 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 ()+checkSubtract = do+ x :: a <- Hedgehog.forAll Gen.integral+ y :: a <- Hedgehog.forAll Gen.integral+ (subtract x y :: b) === (convert x - convert y :: b)++-- | Assert that '(-)' in @a@ gives the same result as+-- '(-)' via @Integer@ if the result is within the range+-- of @a@, and is undefined otherwise+checkPartialSubtract :: forall a m.+ (GenIntegral a, Subtraction a, IntegerNarrow Integer a) =>+ Exception.MonadCatch m => Hedgehog.PropertyT m ()+checkPartialSubtract = 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++prop_add_positive = property $ checkNumOp @Positive (+)+prop_add_signed = property $ checkNumOp @Signed (+)+prop_multiply_positive = property $ checkNumOp @Positive (*)+prop_multiply_signed = property $ checkNumOp @Signed (*)++prop_subtract_natural_signed = property $ checkSubtract @Natural @Signed+prop_subtract_natural_integer = property $ checkSubtract @Natural @Integer+prop_subtract_positive_signed = property $ checkSubtract @Positive @Signed+prop_subtract_positive_integer = property $ checkSubtract @Positive @Integer++prop_partial_subtract_positive = property $ checkPartialSubtract @Positive
+ test-integer-conversions/Main.hs view
@@ -0,0 +1,91 @@+{-# language TemplateHaskell #-}+{-# options_ghc -fno-warn-missing-signatures #-}++module Main (main) where++import Integer++import Control.Applicative (pure)+import Control.Monad (Monad)+import Data.Eq (Eq)+import Data.Function (($), (.))+import Data.Maybe (Maybe (..))+import Hedgehog (evalMaybe, (===))+import Integer.Gen (GenIntegral)+import Prelude (($!))+import Text.Show (Show)++import qualified Control.Exception as Exception (ArithException (Underflow))+import qualified Control.Monad.Catch as Exception (MonadCatch, try)+import qualified Data.Either as Either+import qualified Data.Ord as Ord+import qualified Hedgehog+import qualified Hedgehog.Main as Hedgehog+import qualified Integer.Gen as Gen+import qualified Prelude as Num (toInteger)++main = Hedgehog.defaultMain [Hedgehog.checkSequential $$(Hedgehog.discover)]++testLimit :: Hedgehog.TestLimit+testLimit = 1000++property = Hedgehog.withTests testLimit . Hedgehog.property++-- | Half of an isomorphism test: @review (view x)@ = @x@+checkIso :: forall a b m. (GenIntegral a, IntegerEquiv a b) =>+ Monad m => Hedgehog.PropertyT m ()+checkIso = do+ x :: a <- Hedgehog.forAll Gen.integral+ convert (convert x :: b) === x++-- | Half of a prism test: @preview (x review)@ = @Just@+checkConvert :: forall a b m.+ (GenIntegral a, IntegerConvert a b, IntegerNarrow b a) =>+ Monad m => Hedgehog.PropertyT m ()+checkConvert = do+ x :: a <- Hedgehog.forAll Gen.integral+ narrow (convert x :: b) === Just x++-- | Half of a prism test: @fmap review (preview x)@ = @Just x@+-- for @x@ in range, @Nothing@ otherwise+checkNarrow :: forall a b m. (GenIntegral a, BoundedBelow b) =>+ (IntegerConvert b a, IntegerNarrow a b) =>+ (Show b, Eq b) => Monad m => Hedgehog.PropertyT m ()+checkNarrow = 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++-- | Like 'checkNarrow @Integer', but tests the partial 'yolo'+-- function rather than the safe 'convert' function+checkYolo :: forall a m. (GenIntegral a, BoundedBelow a) =>+ Exception.MonadCatch m => Hedgehog.PropertyT m ()+checkYolo = 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++prop_iso_integer_signed = property $ checkIso @Integer @Signed+prop_iso_signed_integer = property $ checkIso @Signed @Integer++prop_prism_natural_integer = property $ checkConvert @Natural @Integer+prop_prism_integer_natural = property $ checkNarrow @Integer @Natural+prop_prism_natural_signed = property $ checkConvert @Natural @Signed+prop_prism_signed_natural = property $ checkNarrow @Signed @Natural+prop_prism_positive_integer = property $ checkConvert @Positive @Integer+prop_prism_integer_positive = property $ checkNarrow @Integer @Positive+prop_prism_positive_signed = property $ checkConvert @Positive @Signed+prop_prism_signed_positive = property $ checkNarrow @Signed @Positive+prop_prism_positive_natural = property $ checkConvert @Positive @Natural+prop_prism_natural_positive = property $ checkNarrow @Natural @Positive++prop_yolo_positive = property $ checkYolo @Positive+prop_yolo_natural = property $ checkYolo @Natural
+ test-integer-deepseq/Main.hs view
@@ -0,0 +1,40 @@+{-# options_ghc -fno-warn-missing-signatures #-}++module Main (main) where++import Integer++import Control.Applicative (pure)+import Control.DeepSeq (NFData, ($!!))+import Control.Exception (Exception, throw)+import Data.Either (Either (..))+import Data.Eq (Eq)+import Data.Function (($))+import Test.Hspec (describe, hspec, it, shouldBe)+import Text.Show (Show)++import qualified Control.Monad.Catch as Exception (MonadCatch, try)++data X = X+ deriving (Eq, Show)++instance Exception X++force :: NFData a => Exception.MonadCatch m => a -> m (Either X a)+force x = Exception.try (pure $!! x)++main = hspec $ do+ describe "Signed" $ do+ describe "deepseq" $ do+ it "can succeed" $ do+ x <- force (NonZero MinusSign 5)+ x `shouldBe` Right (-5)+ it "can force an error" $ do+ x <- force (throw X :: Signed)+ x `shouldBe` Left X+ it "can force an error in sign" $ do+ x <- force (NonZero (throw X) 5)+ x `shouldBe` Left X+ it "can force an error in magnitude" $ do+ x <- force (NonZero MinusSign (throw X))+ x `shouldBe` Left X
+ test-integer-enum/Main.hs view
@@ -0,0 +1,71 @@+{-# options_ghc -fno-warn-missing-signatures #-}++module Main (main) where++import Integer++import Data.Function (($))+import Data.List (take)+import Test.Hspec (describe, hspec, it, shouldBe)++main = hspec $ do+ describe "Positive" $ do+ describe "[a ..]" $ do+ it "counts upward" $+ take 3 [5 :: Positive ..] `shouldBe` [5, 6, 7]+ it "can start with 1" $+ take 3 [1 :: Positive ..] `shouldBe` [1, 2, 3]+ describe "[a .. b]" $ do+ it "counts upward" $+ [5 .. 8 :: Positive] `shouldBe` [5, 6, 7, 8]+ it "can start with 1" $+ [1 .. 5 :: Positive] `shouldBe` [1, 2, 3, 4, 5]+ it "does not count downward" $ do+ [8 .. 5 :: Positive] `shouldBe` []+ [8 .. 7 :: Positive] `shouldBe` []+ it "can return 1 item" $ do+ [3 .. 3 :: Positive] `shouldBe` [3]+ [1 .. 1 :: Positive] `shouldBe` [1]+ describe "[a, b ..]" $ do+ it "can count upward by 1" $ do+ take 5 [5, 6 :: Positive ..] `shouldBe` [5, 6, 7, 8, 9]+ take 5 [1, 2 :: Positive ..] `shouldBe` [1, 2, 3, 4, 5]+ it "can count downward by 1" $+ [5, 4 :: Positive ..] `shouldBe` [5, 4, 3, 2, 1]+ it "can count upward by 2" $ do+ take 5 [5, 7 :: Positive ..] `shouldBe` [5, 7, 9, 11, 13]+ take 5 [1, 3 :: Positive ..] `shouldBe` [1, 3, 5, 7, 9]+ it "can count downward by 2" $+ [9, 7 :: Positive ..] `shouldBe` [9, 7, 5, 3, 1]+ it "can count downward by 2 without exactly reaching its lower bound" $+ [8, 6 :: Positive ..] `shouldBe` [8, 6, 4, 2]+ it "can repeat 1 item indefinitely" $+ take 5 [4, 4 :: Positive ..] `shouldBe` [4, 4, 4, 4, 4]+ describe "[a, b .. c]" $ do+ it "can count upward by 1" $ do+ [5, 6 .. 9 :: Positive] `shouldBe` [5, 6, 7, 8, 9]+ [1, 2 .. 5 :: Positive] `shouldBe` [1, 2, 3, 4, 5]+ it "can count downward by 1" $+ [9, 8 .. 5 :: Positive] `shouldBe` [9, 8, 7, 6, 5]+ it "can count upward by 2" $ do+ [5, 7 .. 11 :: Positive] `shouldBe` [5, 7, 9, 11]+ [1, 3 .. 7 :: Positive] `shouldBe` [1, 3, 5, 7]+ it "can count upward without exactly reaching its upper bound" $+ [5, 7 .. 12 :: Positive] `shouldBe` [5, 7, 9, 11]+ it "can count downward by 2" $+ [11, 9 .. 5 :: Positive] `shouldBe` [11, 9, 7, 5]+ it "can count downward by 2 without exactly reaching its lower bound" $+ [11, 9 .. 4 :: Positive] `shouldBe` [11, 9, 7, 5]+ it "can count downward with a lower bound of 1" $ do+ [7, 5 .. 1 :: Positive] `shouldBe` [7, 5, 3, 1]+ [8, 6 .. 1 :: Positive] `shouldBe` [8, 6, 4, 2]+ it "can repeat 1 item indefinitely" $ do+ take 5 [4, 4 .. 9 :: Positive] `shouldBe` [4, 4, 4, 4, 4]+ take 5 [4, 4 .. 4 :: Positive] `shouldBe` [4, 4, 4, 4, 4]+ it "can return 1 item" $ do+ [4, 5 .. 4 :: Positive] `shouldBe` [4]+ [4, 3 .. 4 :: Positive] `shouldBe` [4]+ it "can return an empty list" $ do+ [4, 4 .. 3 :: Positive] `shouldBe` []+ [4, 5 .. 3 :: Positive] `shouldBe` []+ [5, 4 .. 6 :: Positive] `shouldBe` []
+ test-integer-finite/Main.hs view
@@ -0,0 +1,71 @@+{-# language TemplateHaskell #-}+{-# options_ghc -fno-warn-missing-signatures #-}++module Main (main) where++import Integer++import Control.Monad (Monad)+import Data.Eq (Eq)+import Data.Function (($), (.))+import Data.Int (Int)+import Data.Maybe (Maybe (..))+import Data.Word (Word)+import Hedgehog ((===))+import Integer.Gen (GenFinite, GenIntegral)+import Text.Show (Show)++import qualified Data.Bool as Bool+import qualified Data.Ord as Ord+import qualified Hedgehog+import qualified Hedgehog.Main as Hedgehog+import qualified Integer.Gen as Gen+import qualified Prelude as Bounded (Bounded (..))+import qualified Prelude as Num (fromInteger, toInteger)++main = Hedgehog.defaultMain [Hedgehog.checkSequential $$(Hedgehog.discover)]++testLimit :: Hedgehog.TestLimit+testLimit = 1000++property = Hedgehog.withTests testLimit . Hedgehog.property++checkToFinite :: forall a b m. Monad m =>+ (ConvertWithFinite a, GenIntegral a, Show a) =>+ (Integer.Finite b, Eq b, Show b) =>+ Hedgehog.PropertyT m ()+checkToFinite = 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++checkFromFinite :: forall a b m. Monad m =>+ (ConvertWithFinite a, IntegerNarrow Integer a, Eq a, Show a) =>+ (Finite b, GenFinite b, Show b) =>+ Hedgehog.PropertyT m ()+checkFromFinite = do+ x :: b <- Hedgehog.forAll Gen.finite+ (Integer.fromFinite x :: Maybe a) === Integer.narrow (Num.toInteger x)++prop_convert_integer_int = property $ checkToFinite @Integer @Int+prop_convert_int_integer = property $ checkFromFinite @Integer @Int+prop_convert_integer_word = property $ checkToFinite @Integer @Word+prop_convert_word_integer = property $ checkFromFinite @Integer @Word++prop_convert_natural_int = property $ checkToFinite @Natural @Int+prop_convert_int_natural = property $ checkFromFinite @Natural @Int+prop_convert_natural_word = property $ checkToFinite @Natural @Word+prop_convert_word_natural = property $ checkFromFinite @Natural @Word++prop_convert_positive_int = property $ checkToFinite @Positive @Int+prop_convert_int_positive = property $ checkFromFinite @Positive @Int+prop_convert_positive_word = property $ checkToFinite @Positive @Word+prop_convert_word_positive = property $ checkFromFinite @Positive @Word++prop_convert_signed_int = property $ checkToFinite @Signed @Int+prop_convert_int_signed = property $ checkFromFinite @Signed @Int+prop_convert_signed_word = property $ checkToFinite @Signed @Word+prop_convert_word_signed = property $ checkFromFinite @Signed @Word