packages feed

kind-integer (empty) → 0.1

raw patch · 6 files changed

+858/−0 lines, 6 filesdep +basedep +ghc-primdep +kind-integer

Dependencies added: base, ghc-prim, kind-integer

Files

+ CHANGELOG.md view
@@ -0,0 +1,3 @@+# Version 0.1++* Initial version.
+ LICENSE view
@@ -0,0 +1,27 @@+Copyright (c) 2023, Renzo Carbonara.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are+met:++1. Redistributions of source code must retain the above copyright+notice, this list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright+notice, this list of conditions and the following disclaimer in the+documentation and/or other materials provided with the distribution.++3. Neither the name of the copyright holder nor the names of its+contributors may be used to endorse or promote products derived from+this software without specific prior written permission.  THIS SOFTWARE+IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,4 @@+# kind-integer++Haskell type-level `Integer`s. Like `KnownNat`, but for `Integer`s.+
+ kind-integer.cabal view
@@ -0,0 +1,46 @@+cabal-version: 2.4+name: kind-integer+version: 0.1+license: BSD-3-Clause+license-file: LICENSE+extra-source-files: README.md CHANGELOG.md+author: Renzo Carbonara+maintainer: renλren.zone+copyright: Copyright (c) Renzo Carbonara 2023+category: Types+build-type: Simple+synopsis: Type-level integers. Like KnownNat, but for integers.+description: Type-level integers. Like KnownNat, but for integers.+homepage: https://github.com/k0001/hs-kind+bug-reports: https://github.com/k0001/hs-kind/issues+tested-with: GHC ==9.2.5, GHC ==9.4.3++source-repository head+  type: git+  location: https://github.com/k0001/hs-kind+  subdir: kind-integer++common basic+  default-language: GHC2021+  ghc-options: -O2 -Wall -Werror=incomplete-patterns+  build-depends: base ==4.*+  default-extensions:+    DataKinds+    NoStarIsType+    PatternSynonyms+    TypeFamilies+    TypeOperators+    ViewPatterns++library+  import: basic+  hs-source-dirs: lib+  build-depends: ghc-prim+  exposed-modules: KindInteger++test-suite test+  import: basic+  type: exitcode-stdio-1.0+  hs-source-dirs: test+  main-is: Main.hs+  build-depends: kind-integer
+ lib/KindInteger.hs view
@@ -0,0 +1,404 @@+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE UndecidableInstances #-}++-- | This module provides a type-level representation for term-level+-- 'P.Integer's. This type-level representation is also named 'P.Integer',+-- So import this module qualified to avoid name conflicts.+--+-- @+-- import "KindInteger" qualified as K+-- @+--+-- The implementation details are the same as the ones for type-level 'Natural's+-- in "GHC.TypeNats" as of @base-4.18@, and it will continue to evolve together+-- with @base@, trying to follow its API as much as possible until the day+-- @base@ provides its own type-level integer, making this module redundant.+module KindInteger+  ( -- * Integer Kind+    Integer+  , type P+  , type N+  , Normalize++    -- * Linking type and value level+  , KnownInteger(integerSing), integerVal, integerVal'+  , SomeInteger(..)+  , someIntegerVal+  , sameInteger++    -- ** Singleton values+  , SInteger+  , pattern SInteger+  , fromSInteger+  , withSomeSInteger+  , withKnownInteger++    -- * Arithmethic+  , type (+), type (*), type (^), type (-)+  , Negate, Div, Mod, Quot, Rem, Log2++    -- * Comparisons+  , CmpInteger+  , cmpInteger+  , type (==?), type (==), type (/=?), type (/=)+  ) where++import GHC.Base (WithDict(..))+import GHC.Types (TYPE, Constraint)+import GHC.Show (appPrec, appPrec1)+import GHC.Prim (Proxy#)+import GHC.TypeLits qualified as L+import Data.Proxy+import Data.Type.Coercion+import Data.Type.Equality (TestEquality(..), (:~:)(..))+import Data.Type.Bool (If)+import Data.Type.Ord+import Numeric.Natural (Natural)+import Prelude hiding (Integer, (==), (/=))+import Prelude qualified as P+import Unsafe.Coerce(unsafeCoerce)++--------------------------------------------------------------------------------++-- | Type-level version of 'P.Integer', only ever used as a /kind/+-- for 'P' and 'N'+--+-- * A positive number /+x/ is represented as @'P' x@.+--+-- * A negative number /-x/ is represented as @'N' x@.+--+-- * /Zero/ can be represented as @'P' 0@ or @'N' 0@. For consistency, all+-- /zero/ outputs from type families in this "KindInteger" module use the+-- @'P' 0@, but don't assume that this will be the case elsewhere. So, if you+-- need to treat /zero/ specially in some situation, be sure to handle both the+-- @'P' 0@ and @'N' 0@ cases.+data Integer+  = Positive Natural+  | Negative Natural++-- | * A positive number /+x/ is represented as @'P' x@.+--+-- * /Zero/ can be represented as @'P' 0@ (see notes at 'Integer').+type P (x :: Natural) = 'Positive x :: Integer++-- | * A negative number /-x/ is represented as @'N' x@.+--+-- * /Zero/ can be represented as @'N' 0@ (but often isn't, see notes at 'Integer').+type N (x :: Natural) = 'Negative x :: Integer++-- Not used:+--+-- typeIntegerToTermInteger :: Integer -> P.Integer+-- typeIntegerToTermInteger (P n) = toInteger n+-- typeIntegerToTermInteger (N n) = negate (toInteger n)++termIntegerToTypeInteger :: P.Integer -> Integer+termIntegerToTypeInteger i = let n = fromInteger i+                             in  if i >= 0 then Positive n else Negative n++-- | We are not interested in giving any instance to type-level 'Integer's,+-- so we implement 'showsPrec' here.+showsPrecInteger :: Int -> Integer -> ShowS+showsPrecInteger p i = showParen (p > appPrec) $ case i of+  Positive x -> showString "P " . shows x+  Negative x -> showString "N " . shows x++--------------------------------------------------------------------------------++-- | This class gives the integer associated with a type-level integer.+-- There are instances of the class for every integer.+class KnownInteger (i :: Integer) where+  integerSing :: SInteger i++-- | Positive numbers and zero.+instance L.KnownNat x => KnownInteger (P x) where+  integerSing = UnsafeSInteger (L.natVal (Proxy @x))++-- | Negative numbers and zero.+instance L.KnownNat x => KnownInteger (N x) where+  integerSing = UnsafeSInteger (negate (L.natVal (Proxy @x)))++-- | Term-level 'P.Integer' representation of the type-level 'Integer' @i@.+integerVal :: forall i proxy. KnownInteger i => proxy i -> P.Integer+integerVal _ = case integerSing :: SInteger i of UnsafeSInteger x -> x++-- | Term-level 'P.Integer' representation of the type-level 'Integer' @i@.+integerVal' :: forall i. KnownInteger i => Proxy# i -> P.Integer+integerVal' _ = case integerSing :: SInteger i of UnsafeSInteger x -> x++-- | This type represents unknown type-level 'Integer'.+data SomeInteger = forall n. KnownInteger n => SomeInteger (Proxy n)++-- | Convert a term-level 'P.Integer' into an unknown type-level 'Integer'.+someIntegerVal :: P.Integer -> SomeInteger+someIntegerVal i = withSomeSInteger i (\(si :: SInteger i) ->+                   withKnownInteger si (SomeInteger @i Proxy))++instance Eq SomeInteger where+  SomeInteger x == SomeInteger y = integerVal x P.== integerVal y++instance Ord SomeInteger where+  compare (SomeInteger x) (SomeInteger y) =+    compare (integerVal x) (integerVal y)++instance Show SomeInteger where+  showsPrec p (SomeInteger x) = showsPrec p (integerVal x)++instance Read SomeInteger where+  readsPrec p xs = do (a, ys) <- readsPrec p xs+                      [(someIntegerVal a, ys)]++--------------------------------------------------------------------------------+-- Within this module, we use these “normalization” tools to make sure that+-- /zero/ is always represented as @'P' 0@. We don't export any of these+-- normalization tools to end-users because it seems like we can't make them+-- reliable enough so as to offer a decent user experience. So, we just tell+-- users to deal with the fact that both @'P' 0@ and @'N' 0@ mean /zero/.++-- | Make sure /zero/ is represented as @'P' 0@, not as @'N' 0@+--+-- Notice that all the tools in the "KindInteger" can readily handle+-- non-'Normalize'd inputs. This 'Normalize' type-family is offered offered+-- only as a convenience in case you want to simplify /your/ dealing with+-- /zeros/.+type family Normalize (i :: Integer) :: Integer where+  Normalize (N 0) = P 0+  Normalize i     = i++-- | Construct a 'Normalize'd 'N'egative type-level 'Integer'.+--+-- To be used for producing all negative outputs in this module.+type NN (a :: Natural) = Normalize (N a) :: Integer++--------------------------------------------------------------------------------++infixl 6 +, -+infixl 7 *, `Div`, `Mod`, `Quot`, `Rem`+infixr 8 ^++-- | Negation of type-level 'Integer's.+type family Negate (x :: Integer) :: Integer where+  Negate (P 0) = P 0+  Negate (P x) = N x+  Negate (N x) = P x++-- | Addition of type-level 'Integer's.+type (a :: Integer) + (b :: Integer) = Add_ (Normalize a) (Normalize b) :: Integer+type family Add_ (a :: Integer) (b :: Integer) :: Integer where+  Add_ (P a) (P b) = P (a L.+ b)+  Add_ (N a) (N b) = NN (a L.+ b)+  Add_ (P a) (N b) = If (b <=? a) (P (a L.- b)) (NN (b L.- a))+  Add_ (N a) (P b) = Add_ (P b) (N a)++-- | Multiplication of type-level 'Integer's.+type (a :: Integer) * (b :: Integer) = Mul_ (Normalize a) (Normalize b) :: Integer+type family Mul_ (a :: Integer) (b :: Integer) :: Integer where+  Mul_ (P a) (P b) = P (a L.* b)+  Mul_ (N a) (N b) = Mul_ (P a) (P b)+  Mul_ (P a) (N b) = NN (a L.* b)+  Mul_ (N a) (P b) = Mul_ (P a) (N b)++-- | Exponentiation of type-level 'Integer's.+--+-- * Exponentiation by negative 'Integer' doesn't type-check.+type (a :: Integer) ^ (b :: Integer) = Pow_ (Normalize a) (Normalize b) :: Integer+type family Pow_ (a :: Integer) (b :: Integer) :: Integer where+  Pow_ (P a) (P b) = P (a L.^ b)+  Pow_ (N a) (P b) = NN (a L.^ b)+  Pow_ _     (N _) = L.TypeError ('L.Text "KindInteger.(^): Negative exponent")++-- | Subtraction of type-level 'Integer's.+type (a :: Integer) - (b :: Integer) = a + Negate b :: Integer++-- | Division ('floor'ed) of type-level 'Integer's.+--+-- @+-- forall (a :: 'Integer') (b :: 'Integer').+--   /such that/ (b '/=' 0).+--     a  '=='  'Div' a b '*' 'Negate' b '+' 'Mod' a b+-- @+--+-- * Division by /zero/ doesn't type-check.+type Div (a :: Integer) (b :: Integer) = Div_ (Normalize a) (Normalize b) :: Integer+type family Div_ (a :: Integer) (b :: Integer) :: Integer where+  Div_ _ (P 0) = L.TypeError ('L.Text "KindInteger.Div: Division by zero")+  Div_ (P a) (P b) = P (L.Div a b)+  Div_ (N a) (N b) = Div_ (P a) (P b)+  Div_ (P a) (N b) = NN (If (b L.* (L.Div a b) ==? a) (L.Div a b) (L.Div a b L.+ 1))+  Div_ (N a) (P b) = Div_ (P a) (N b)++-- | Modulus ('floor'ed division) of type-level 'Integer's.+--+-- @+-- forall (a :: 'Integer') (b :: 'Integer').+--   /such that/ (b '/=' 0).+--     a  '=='  'Div' a b '*' 'Negate' b '+' 'Mod' a b+-- @+--+-- * Modulus by /zero/ doesn't type-check.+type Mod (a :: Integer) (b :: Integer) = Div a b * Negate b + a :: Integer++-- | Division ('truncate'd) of type-level 'Integer's.+--+-- @+-- forall (a :: 'Integer') (b :: 'Integer').+--   /such that/ (b '/=' 0).+--     a  '=='  'Quot' a b '*' 'Negate' b '+' 'Rem' a b+-- @+--+-- * Division by /zero/ doesn't type-check.+type Quot (a :: Integer) (b :: Integer) = Quot_ (Normalize a) (Normalize b) :: Integer+type family Quot_ (a :: Integer) (b :: Integer) :: Integer where+  Quot_ _ (P 0) = L.TypeError ('L.Text "KindInteger.Quot: Division by zero")+  Quot_ (P a) (P b) = P (L.Div a b)+  Quot_ (N a) (N b) = Quot_ (P a) (P b)+  Quot_ (P a) (N b) = Negate (Quot_ (P a) (P b))+  Quot_ (N a) (P b) = Quot_ (P a) (N b)++-- | Remulus ('truncate'd division) of type-level 'Integer's.+--+-- @+-- forall (a :: 'Integer') (b :: 'Integer').+--   /such that/ (b '/=' 0).+--     a  '=='  'Quot' a b '*' 'Negate' b '+' 'Rem' a b+-- @+--+-- * Remulus by /zero/ doesn't type-check.+type Rem (a :: Integer) (b :: Integer) = Quot a b * Negate b + a :: Integer++-- | Log base 2 ('floor'ed) of integer numbers.+--+-- * Logarithm of /zero/ doesn't type-check.+--+-- * Logarithm of negative number doesn't type-check.+type Log2 (a :: Integer) = Log2_ (Normalize a) :: Integer+type family Log2_ (a :: Integer) :: Integer where+  Log2_ (P 0) = L.TypeError ('L.Text "KindInteger.Log2: Logarithm of zero")+  Log2_ (P a) = P (L.Log2 a)+  Log2_ (N a) = L.TypeError ('L.Text "KindInteger.Log2: Logarithm of negative number")++-- | Comparison of type-level integers, as a function.+type CmpInteger (a :: Integer) (b :: Integer) = CmpInteger_ (Normalize a) (Normalize b) :: Ordering+type family CmpInteger_ (a :: Integer) (b :: Integer) :: Ordering where+  CmpInteger_ a a = 'EQ+  CmpInteger_ (P a) (P b) = Compare a b+  CmpInteger_ (N a) (N b) = Compare b a+  CmpInteger_ (N _) (P _) = 'LT+  CmpInteger_ (P _) (N _) = 'GT++-- | "Data.Type.Ord" support for type-level 'Integer's.+type instance Compare (a :: Integer) (b :: Integer) = CmpInteger a b :: Ordering++--------------------------------------------------------------------------------++-- | We either get evidence that this function was instantiated with the+-- same type-level 'Integer's, or 'Nothing'.+sameInteger+  :: forall a b proxy1 proxy2+  .  (KnownInteger a, KnownInteger b)+  => proxy1 a+  -> proxy2 b+  -> Maybe (a :~: b)+sameInteger _ _ = testEquality (integerSing @a) (integerSing @b)++-- | Like 'sameInteger', but if the type-level 'Integer's aren't equal, this+-- additionally provides proof of 'LT' or 'GT'.+cmpInteger+  :: forall a b proxy1 proxy2+  .  (KnownInteger a, KnownInteger b)+  => proxy1 a+  -> proxy2 b+  -> OrderingI a b+cmpInteger x y = case compare (integerVal x) (integerVal y) of+  EQ -> case unsafeCoerce (Refl, Refl) :: (CmpInteger a b :~: 'EQ, a :~: b) of+    (Refl, Refl) -> EQI+  LT -> case unsafeCoerce Refl :: (CmpInteger a b :~: 'LT) of+    Refl -> LTI+  GT -> case unsafeCoerce Refl :: (CmpInteger a b :~: 'GT) of+    Refl -> GTI++--------------------------------------------------------------------------------++-- | Singleton type for a type-level 'Integer' @i@.+newtype SInteger (i :: Integer) = UnsafeSInteger P.Integer++-- | A explicitly bidirectional pattern synonym relating an 'SInteger' to a+-- 'KnownInteger' constraint.+--+-- As an __expression__: Constructs an explicit @'SInteger' i@ value from an+-- implicit @'KnownInteger' i@ constraint:+--+-- @+-- 'SInteger' @i :: 'KnownInteger' i => 'SInteger' i+-- @+--+-- As a __pattern__: Matches on an explicit @'SInteger' i@ value bringing+-- an implicit @'KnownInteger' i@ constraint into scope:+--+-- @+-- f :: 'SInteger' i -> ..+-- f SInteger = {- SInteger i in scope -}+-- @+pattern SInteger :: forall i. () => KnownInteger i => SInteger i+pattern SInteger <- (knownIntegerInstance -> KnownIntegeregerInstance)+  where SInteger = integerSing++-- | An internal data type that is only used for defining the 'SInteger' pattern+-- synonym.+data KnownIntegeregerInstance (i :: Integer) where+  KnownIntegeregerInstance :: KnownInteger i => KnownIntegeregerInstance i++-- | An internal function that is only used for defining the 'SInteger' pattern+-- synonym.+knownIntegerInstance :: SInteger i -> KnownIntegeregerInstance i+knownIntegerInstance si = withKnownInteger si KnownIntegeregerInstance++instance Show (SInteger i) where+  showsPrec p (UnsafeSInteger i) = showParen (p > appPrec) $+    showString "SInteger @" .+    showsPrecInteger appPrec1 (termIntegerToTypeInteger i)++instance TestEquality SInteger where+  testEquality (UnsafeSInteger x) (UnsafeSInteger y)+    | x P.== y  = Just (unsafeCoerce Refl)+    | otherwise = Nothing++instance TestCoercion SInteger where+  testCoercion x y = fmap (\Refl -> Coercion) (testEquality x y)++-- | Return the type-level 'P.Integer' number corresponding to @i@ in+-- a @'SInteger' i@ value.+fromSInteger :: SInteger i -> P.Integer+fromSInteger (UnsafeSInteger i) = i++-- | Convert an explicit @'SInteger' i@ value into an implicit+-- @'KnownInteger' i@ constraint.+withKnownInteger+  :: forall i rep (r :: TYPE rep). SInteger i -> (KnownInteger i => r) -> r+withKnownInteger = withDict @(KnownInteger i)++-- | Convert a 'P.Integer' number into an @'SInteger' n@ value, where @n@ is a+-- fresh type-level 'Integer'.+withSomeSInteger+  :: forall rep (r :: TYPE rep). P.Integer -> (forall n. SInteger n -> r) -> r+withSomeSInteger n k = k (UnsafeSInteger n)+-- It's very important to keep this NOINLINE! See the docs at "GHC.TypeNats"+{-# NOINLINE withSomeSInteger #-}++--------------------------------------------------------------------------------+-- Extras++infixr 4 /=, /=?, ==, ==?++-- | This should be exported by 'Data.Type.Ord'.+type (a :: k) ==? (b :: k) = OrdCond (Compare a b) 'False 'True 'False :: Bool++-- | This should be exported by 'Data.Type.Ord'.+type (a :: k) == (b :: k) = (a ==? b) ~ 'True :: Constraint++-- | This should be exported by 'Data.Type.Ord'.+type (a :: k) /=? (b :: k) = OrdCond (Compare a b) 'True 'False 'True :: Bool++-- | This should be exported by 'Data.Type.Ord'.+type (a :: k) /= (b :: k) = (a /=? b) ~ 'True :: Constraint+
+ test/Main.hs view
@@ -0,0 +1,374 @@+{-# LANGUAGE MagicHash #-}+module Main (main) where++import Control.Applicative+import Data.Maybe+import Data.Proxy+import Data.Type.Ord (type (<=))+import GHC.Exts (Constraint, proxy#)+import System.Exit+import Text.Read++import KindInteger (P, N)+import KindInteger qualified as K++--------------------------------------------------------------------------------++data Dict (c :: Constraint) where+  Dict :: c => Dict c++--------------------------------------------------------------------------------++_testEq =  Dict+_testEq :: Dict+  ( P 0 K.== P 0,   'True ~ (P 0 K.==? P 0)+  , N 0 K.== N 0,   'True ~ (N 0 K.==? N 0)+  , P 0 K.== N 0,   'True ~ (P 0 K.==? N 0)+  , N 0 K.== P 0,   'True ~ (N 0 K.==? P 0)++  , P 0 K./= P 1,   'True ~ (P 0 K./=? P 1)+  , P 0 K./= N 1,   'True ~ (P 0 K./=? N 1)++  , N 0 K./= N 1,   'True ~ (N 0 K./=? N 1)+  , N 0 K./= N 1,   'True ~ (N 0 K./=? N 1)++  , P 1 K./= P 0,   'True ~ (P 1 K./=? P 0)+  , P 1 K./= N 0,   'True ~ (P 1 K./=? N 0)++  , N 1 K./= N 0,   'True ~ (N 1 K./=? N 0)+  , N 1 K./= N 0,   'True ~ (N 1 K./=? N 0)+  )++_testCmp =  Dict+_testCmp :: Dict+  ( P 0 <= P 0+  , P 0 <= N 0+  , N 0 <= P 0+  , N 0 <= N 0++  , N 2 <= N 1+  , N 1 <= N 0+  , N 0 <= P 1++  , P 0 <= P 1+  , P 1 <= P 2+  )++_testAdd  = Dict+_testAdd :: Dict+  ( P 0 ~ P 0 K.+ P 0+  , P 0 ~ N 0 K.+ N 0+  , P 0 ~ P 0 K.+ N 0+  , P 0 ~ N 0 K.+ P 0++  , P 1 ~ P 1 K.+ P 0+  , N 1 ~ N 1 K.+ N 0+  , P 1 ~ P 1 K.+ N 0+  , N 1 ~ N 1 K.+ P 0++  , P 1 ~ P 0 K.+ P 1+  , N 1 ~ N 0 K.+ N 1+  , N 1 ~ P 0 K.+ N 1+  , P 1 ~ N 0 K.+ P 1++  , P 2 ~ P 1 K.+ P 1+  , N 2 ~ N 1 K.+ N 1+  , P 0 ~ P 1 K.+ N 1+  , P 0 ~ N 1 K.+ P 1+  )++_testMul  = Dict+_testMul :: Dict+  ( P 0 ~ P 0 K.* P 0+  , P 0 ~ N 0 K.* N 0+  , P 0 ~ P 0 K.* N 0+  , P 0 ~ N 0 K.* P 0++  , P 0 ~ P 1 K.* P 0+  , P 0 ~ N 1 K.* N 0+  , P 0 ~ P 1 K.* N 0+  , P 0 ~ N 1 K.* P 0++  , P 0 ~ P 0 K.* P 1+  , P 0 ~ N 0 K.* N 1+  , P 0 ~ P 0 K.* N 1+  , P 0 ~ N 0 K.* P 1++  , P 1 ~ P 1 K.* P 1+  , P 1 ~ N 1 K.* N 1+  , N 1 ~ P 1 K.* N 1+  , N 1 ~ N 1 K.* P 1++  , P 2 ~ P 2 K.* P 1+  , P 2 ~ N 2 K.* N 1+  , N 2 ~ P 2 K.* N 1+  , N 2 ~ N 2 K.* P 1++  , P 6 ~ P 2 K.* P 3+  , P 6 ~ N 2 K.* N 3+  , N 6 ~ P 2 K.* N 3+  , N 6 ~ N 2 K.* P 3+  )++_testDiv  = Dict+_testDiv :: Dict+  ( P 0 ~ P 0 `K.Div` P 1+  , P 0 ~ N 0 `K.Div` N 1+  , P 0 ~ P 0 `K.Div` N 1+  , P 0 ~ N 0 `K.Div` P 1++  , P 1 ~ P 1 `K.Div` P 1+  , P 1 ~ N 1 `K.Div` N 1+  , N 1 ~ P 1 `K.Div` N 1+  , N 1 ~ N 1 `K.Div` P 1++  , P 2 ~ P 2 `K.Div` P 1+  , P 2 ~ N 2 `K.Div` N 1+  , N 2 ~ P 2 `K.Div` N 1+  , N 2 ~ N 2 `K.Div` P 1++  , P 1 ~ P 2 `K.Div` P 2+  , P 1 ~ N 2 `K.Div` N 2+  , N 1 ~ P 2 `K.Div` N 2+  , N 1 ~ N 2 `K.Div` P 2++  , P 1 ~ P 3 `K.Div` P 2+  , P 1 ~ N 3 `K.Div` N 2+  , N 2 ~ P 3 `K.Div` N 2+  , N 2 ~ N 3 `K.Div` P 2++  , P 0 ~ P 0 `K.Div` P 1+  , P 0 ~ N 0 `K.Div` N 1+  , P 0 ~ P 0 `K.Div` N 1+  , P 0 ~ N 0 `K.Div` P 1++  , P 0 ~ P 1 `K.Div` P 2+  , P 0 ~ N 1 `K.Div` N 2+  , N 1 ~ P 1 `K.Div` N 2+  , N 1 ~ N 1 `K.Div` P 2+  )++_testMod  = Dict+_testMod :: Dict+  ( P 0 ~ P 0 `K.Mod` P 1+  , P 0 ~ N 0 `K.Mod` N 1+  , P 0 ~ P 0 `K.Mod` N 1+  , P 0 ~ N 0 `K.Mod` P 1++  , P 0 ~ P 1 `K.Mod` P 1+  , P 0 ~ N 1 `K.Mod` N 1+  , P 0 ~ P 1 `K.Mod` N 1+  , P 0 ~ N 1 `K.Mod` P 1++  , P 0 ~ P 2 `K.Mod` P 1+  , P 0 ~ N 2 `K.Mod` N 1+  , P 0 ~ P 2 `K.Mod` N 1+  , P 0 ~ N 2 `K.Mod` P 1++  , P 0 ~ P 2 `K.Mod` P 2+  , P 0 ~ N 2 `K.Mod` N 2+  , P 0 ~ P 2 `K.Mod` N 2+  , P 0 ~ N 2 `K.Mod` P 2++  , P 1 ~ P 3 `K.Mod` P 2+  , N 1 ~ N 3 `K.Mod` N 2+  , N 1 ~ P 3 `K.Mod` N 2+  , P 1 ~ N 3 `K.Mod` P 2++  , P 0 ~ P 0 `K.Mod` P 1+  , P 0 ~ N 0 `K.Mod` N 1+  , P 0 ~ P 0 `K.Mod` N 1+  , P 0 ~ N 0 `K.Mod` P 1++  , P 1 ~ P 1 `K.Mod` P 2+  , N 1 ~ N 1 `K.Mod` N 2+  , N 1 ~ P 1 `K.Mod` N 2+  , P 1 ~ N 1 `K.Mod` P 2+  )++_testQuot  = Dict+_testQuot :: Dict+  ( P 0 ~ P 0 `K.Quot` P 1+  , P 0 ~ N 0 `K.Quot` N 1+  , P 0 ~ P 0 `K.Quot` N 1+  , P 0 ~ N 0 `K.Quot` P 1++  , P 1 ~ P 1 `K.Quot` P 1+  , P 1 ~ N 1 `K.Quot` N 1+  , N 1 ~ P 1 `K.Quot` N 1+  , N 1 ~ N 1 `K.Quot` P 1++  , P 2 ~ P 2 `K.Quot` P 1+  , P 2 ~ N 2 `K.Quot` N 1+  , N 2 ~ P 2 `K.Quot` N 1+  , N 2 ~ N 2 `K.Quot` P 1++  , P 1 ~ P 2 `K.Quot` P 2+  , P 1 ~ N 2 `K.Quot` N 2+  , N 1 ~ P 2 `K.Quot` N 2+  , N 1 ~ N 2 `K.Quot` P 2++  , P 1 ~ P 3 `K.Quot` P 2+  , P 1 ~ N 3 `K.Quot` N 2+  , N 1 ~ P 3 `K.Quot` N 2+  , N 1 ~ N 3 `K.Quot` P 2++  , P 0 ~ P 0 `K.Quot` P 1+  , P 0 ~ N 0 `K.Quot` N 1+  , P 0 ~ P 0 `K.Quot` N 1+  , P 0 ~ N 0 `K.Quot` P 1++  , P 0 ~ P 1 `K.Quot` P 2+  , P 0 ~ N 1 `K.Quot` N 2+  , P 0 ~ P 1 `K.Quot` N 2+  , P 0 ~ N 1 `K.Quot` P 2+  )++_testRem  = Dict+_testRem :: Dict+  ( P 0 ~ P 0 `K.Rem` P 1+  , P 0 ~ N 0 `K.Rem` N 1+  , P 0 ~ P 0 `K.Rem` N 1+  , P 0 ~ N 0 `K.Rem` P 1++  , P 0 ~ P 1 `K.Rem` P 1+  , P 0 ~ N 1 `K.Rem` N 1+  , P 0 ~ P 1 `K.Rem` N 1+  , P 0 ~ N 1 `K.Rem` P 1++  , P 0 ~ P 2 `K.Rem` P 1+  , P 0 ~ N 2 `K.Rem` N 1+  , P 0 ~ P 2 `K.Rem` N 1+  , P 0 ~ N 2 `K.Rem` P 1++  , P 0 ~ P 2 `K.Rem` P 2+  , P 0 ~ N 2 `K.Rem` N 2+  , P 0 ~ P 2 `K.Rem` N 2+  , P 0 ~ N 2 `K.Rem` P 2++  , P 1 ~ P 3 `K.Rem` P 2+  , N 1 ~ N 3 `K.Rem` N 2+  , P 1 ~ P 3 `K.Rem` N 2+  , N 1 ~ N 3 `K.Rem` P 2++  , P 0 ~ P 0 `K.Rem` P 1+  , P 0 ~ N 0 `K.Rem` N 1+  , P 0 ~ P 0 `K.Rem` N 1+  , P 0 ~ N 0 `K.Rem` P 1++  , P 1 ~ P 1 `K.Rem` P 2+  , N 1 ~ N 1 `K.Rem` N 2+  , P 1 ~ P 1 `K.Rem` N 2+  , N 1 ~ N 1 `K.Rem` P 2+  )++_testLog2 =  Dict+_testLog2 :: Dict+  ( P 0 ~ K.Log2 (P 1)+  , P 1 ~ K.Log2 (P 2)+  , P 1 ~ K.Log2 (P 3)+  , P 2 ~ K.Log2 (P 4)+  , P 2 ~ K.Log2 (P 5)+  , P 2 ~ K.Log2 (P 6)+  , P 2 ~ K.Log2 (P 7)+  , P 3 ~ K.Log2 (P 8)+  , P 3 ~ K.Log2 (P 9)+  , P 3 ~ K.Log2 (P 10)+  , P 3 ~ K.Log2 (P 11)+  , P 3 ~ K.Log2 (P 12)+  , P 3 ~ K.Log2 (P 13)+  , P 3 ~ K.Log2 (P 14)+  , P 3 ~ K.Log2 (P 15)+  , P 4 ~ K.Log2 (P 16)+  , P 4 ~ K.Log2 (P 17)+  , P 4 ~ K.Log2 (P 18)+  , P 4 ~ K.Log2 (P 19)+  , P 4 ~ K.Log2 (P 20)+  , P 4 ~ K.Log2 (P 21)+  , P 4 ~ K.Log2 (P 22)+  , P 4 ~ K.Log2 (P 23)+  , P 4 ~ K.Log2 (P 24)+  , P 4 ~ K.Log2 (P 25)+  , P 4 ~ K.Log2 (P 26)+  , P 4 ~ K.Log2 (P 27)+  , P 4 ~ K.Log2 (P 28)+  , P 4 ~ K.Log2 (P 29)+  , P 4 ~ K.Log2 (P 30)+  , P 4 ~ K.Log2 (P 31)+  , P 5 ~ K.Log2 (P 32)+  )++--------------------------------------------------------------------------------++assert+  :: String  -- ^ Test name+  -> Bool    -- ^ Successful is true+  -> IO Bool -- ^ Return the same 'Bool' given as input.+assert n x = do+  putStrLn ((if x then "[OK] " else "[FAIL] ") <> n)+  pure x++testsMain :: [IO Bool] -> IO a+testsMain xs = do+  oks <- sequence xs+  if and oks+     then do putStrLn "All tests passed successfully."+             exitSuccess+     else do putStrLn "Some tests failed."+             exitFailure++main :: IO ()+main = testsMain+  [ assert "integerVal . someIntegerVal == id" $+    flip all [-5 .. 5] $ \a ->+      case K.someIntegerVal a of+        K.SomeInteger pa ->+          a == K.integerVal pa++  , assert "integerVal' . someIntegerVal == id" $+    flip all [-5 .. 5] $ \a ->+      case K.someIntegerVal a of+        K.SomeInteger (_ :: Proxy a) ->+          a == K.integerVal' (proxy# @a)++  , assert "sameIntegerVal a a" $+    flip all [-5 .. 5] $ \a ->+      case K.someIntegerVal a of+        K.SomeInteger pa ->+          isJust (K.sameInteger pa pa)++  , assert "sameIntegerVal a a'" $+    flip all [-5 .. 5] $ \a ->+      case (K.someIntegerVal a, K.someIntegerVal a) of+        (K.SomeInteger pa1, K.SomeInteger pa2) ->+          isJust (K.sameInteger pa1 pa2)++  , assert "sameIntegerVal a b" $+    flip all (liftA2 (,) [-5 .. 5] [-5 .. 5])$ \(a, b) ->+      case (K.someIntegerVal a, K.someIntegerVal b) of+        (K.SomeInteger pa, K.SomeInteger pb)+          | a == b    -> isJust    (K.sameInteger pa pb)+          | otherwise -> isNothing (K.sameInteger pa pb)++  , assert "Eq SomeInteger" $+    flip all (liftA2 (,) [-5 .. 5] [-5 .. 5])$ \(a, b) ->+      (a == b) == (K.someIntegerVal a == K.someIntegerVal b)++  , assert "Ord SomeInteger" $+    flip all (liftA2 (,) [-5 .. 5] [-5 .. 5])$ \(a, b) ->+      (a `compare` b) == (K.someIntegerVal a `compare` K.someIntegerVal b)++  , assert "Show SomeInteger" $+    flip all [-5 .. 5] $ \i ->+      show i == show (K.someIntegerVal i)++  , assert "Read SomeInteger" $+    flip all [-5 .. 5] $ \i ->+      let str = show (i :: Integer)+      in readMaybe @Integer str+            == fmap (\(K.SomeInteger p) -> K.integerVal p)+                    (readMaybe @K.SomeInteger str)++  ]++