bv-sized 0.5.0 → 0.6.0
raw patch · 29 files changed
+2259/−586 lines, 29 filesdep +prettydep −prettyclass
Dependencies added: pretty
Dependencies removed: prettyclass
Files
- bv-sized.cabal +6/−5
- cabal.project +2/−0
- changelog.md +10/−0
- src/Data/BitVector/Sized.hs +10/−397
- src/Data/BitVector/Sized/App.hs +65/−17
- src/Data/BitVector/Sized/BitLayout.hs +26/−5
- src/Data/BitVector/Sized/Internal.hs +419/−0
- stack.yaml +0/−12
- submodules/parameterized-utils/.travis.yml +103/−0
- submodules/parameterized-utils/parameterized-utils.cabal +13/−4
- submodules/parameterized-utils/src/Data/Parameterized/Classes.hs +21/−0
- submodules/parameterized-utils/src/Data/Parameterized/ClassesC.hs +52/−0
- submodules/parameterized-utils/src/Data/Parameterized/Compose.hs +45/−0
- submodules/parameterized-utils/src/Data/Parameterized/Context.hs +161/−39
- submodules/parameterized-utils/src/Data/Parameterized/Context/Safe.hs +6/−1
- submodules/parameterized-utils/src/Data/Parameterized/Context/Unsafe.hs +11/−1
- submodules/parameterized-utils/src/Data/Parameterized/DecidableEq.hs +37/−0
- submodules/parameterized-utils/src/Data/Parameterized/Map.hs +88/−27
- submodules/parameterized-utils/src/Data/Parameterized/NatRepr.hs +205/−42
- submodules/parameterized-utils/src/Data/Parameterized/Nonce.hs +36/−31
- submodules/parameterized-utils/src/Data/Parameterized/Peano.hs +285/−0
- submodules/parameterized-utils/src/Data/Parameterized/TestEquality.hs +36/−0
- submodules/parameterized-utils/src/Data/Parameterized/TraversableF.hs +33/−3
- submodules/parameterized-utils/src/Data/Parameterized/TraversableFC.hs +2/−2
- submodules/parameterized-utils/src/Data/Parameterized/Utils/Endian.hs +3/−0
- submodules/parameterized-utils/src/Data/Parameterized/Vector.hs +507/−0
- submodules/parameterized-utils/test/Test/Context.hs +8/−0
- submodules/parameterized-utils/test/Test/Vector.hs +67/−0
- submodules/parameterized-utils/test/UnitTest.hs +2/−0
bv-sized.cabal view
@@ -1,16 +1,16 @@ name: bv-sized-version: 0.5.0+version: 0.6.0 category: Bit Vectors synopsis: a BitVector datatype that is parameterized by the vector width description: This module defines a width-parameterized 'BitVector' type and various associated operations that assume a 2's complement representation.-homepage: https://github.com/benjaminselfridge/bv-sized+homepage: https://github.com/GaloisInc/bv-sized license: BSD3 license-file: LICENSE author: Ben Selfridge maintainer: benselfridge@galois.com-copyright: March 2018+copyright: Galois Inc., Ben Selfridge March 2018 build-type: Simple cabal-version: >=1.10 extra-source-files: README.md@@ -19,12 +19,13 @@ exposed-modules: Data.BitVector.Sized , Data.BitVector.Sized.App , Data.BitVector.Sized.BitLayout+ other-modules: Data.BitVector.Sized.Internal build-depends: base >= 4.7 && < 5 , containers >= 0.5.10 && < 0.6 , lens >= 4 && < 5 , mtl >= 2 && < 3 , parameterized-utils- , prettyclass >= 1.0 && < 2.0+ , pretty , random >= 1.1 && < 1.2 , QuickCheck >= 2.11 && < 2.12 hs-source-dirs: src@@ -42,6 +43,6 @@ , bv-sized , lens >= 4 && < 5 , parameterized-utils- , prettyclass >= 1.0 && < 2.0+ , pretty , random >= 1.1 && < 1.2 , QuickCheck >= 2.11 && < 2.12
+ cabal.project view
@@ -0,0 +1,2 @@+packages: .+ submodules/parameterized-utils
changelog.md view
@@ -1,5 +1,15 @@ # Changelog for [`bv-sized` package](http://hackage.haskell.org/package/bv-sized) +## 0.6.0 *March 2019*+* changed WithRepr functions to '+* added Num, Bits instances+* bitVector now takes arbitrary Integral argument+* add 'bitLayoutAssignmentList' function (see haddocks for details+* Hid BV constructor, exposed BitVector as pattern++## 0.5.1 *August 2018*+ * fixed github URL+ ## 0.5.0 *August 2018* * Added a lot of better support for the App module, including a type class for embedding BVApp expressions along with associated smart constructors
src/Data/BitVector/Sized.hs view
@@ -1,8 +1,8 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE KindSignatures #-}+{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeOperators #-} {-|@@ -19,12 +19,12 @@ module Data.BitVector.Sized ( -- * BitVector type- BitVector(..)- , bitVector+ BitVector, pattern BitVector+ , bitVector, bitVector' , bv0 -- * Bitwise operations (width-preserving)- -- | These are alternative versions of some of the 'Bits' functions where we do- -- not need to know the width at compile time. They are all width-preserving.+ -- | These are alternative versions of some of the 'Data.Bits' functions where we+ -- do not need to know the width at compile time. They are all width-preserving. , bvAnd, bvOr, bvXor , bvComplement , bvShift, bvShiftL, bvShiftRA, bvShiftRL, bvRotate@@ -41,10 +41,10 @@ , bvLTS, bvLTU -- * Variable-width operations -- | These are functions that involve bit vectors of different lengths.- , bvConcat, (<:>), bvConcatMany, bvConcatManyWithRepr- , bvExtract, bvExtractWithRepr- , bvZext, bvZextWithRepr- , bvSext, bvSextWithRepr+ , bvConcat, (<:>), bvConcatMany, bvConcatMany'+ , bvExtract, bvExtract'+ , bvZext, bvZext'+ , bvSext, bvSext' -- * Conversions to Integer , bvIntegerU , bvIntegerS@@ -52,391 +52,4 @@ , bvGetBytesU ) where -import Data.Bits-import Data.Ix-import Data.Parameterized-import GHC.TypeLits-import Numeric-import System.Random-import Test.QuickCheck (Arbitrary(..), choose)-import Text.PrettyPrint.HughesPJClass-import Text.Printf-import Unsafe.Coerce (unsafeCoerce)--------------------------------------------- BitVector data type definitions---- | BitVector datatype, parameterized by width.-data BitVector (w :: Nat) :: * where- BV :: NatRepr w -> Integer -> BitVector w---- | Construct a bit vector with a particular width, where the width is inferrable--- from the type context. The 'Integer' input (an unbounded data type, hence with an--- infinite-width bit representation), whether positive or negative, is silently--- truncated to fit into the number of bits demanded by the return type.------ >>> bitVector 0xA :: BitVector 4--- 0xa--- >>> bitVector 0xA :: BitVector 2--- 0x2-bitVector :: KnownNat w => Integer -> BitVector w-bitVector x = BV wRepr (truncBits width (fromIntegral x))- where wRepr = knownNat- width = natValue wRepr---- | The zero bitvector with width 0.-bv0 :: BitVector 0-bv0 = bitVector 0--------------------------------------------- BitVector -> Integer functions---- | Unsigned interpretation of a bit vector as a (positive) Integer.-bvIntegerU :: BitVector w -> Integer-bvIntegerU (BV _ x) = x---- | Signed interpretation of a bit vector as an Integer.-bvIntegerS :: BitVector w -> Integer-bvIntegerS bv = if bvTestBit bv (width - 1)- then bvIntegerU bv - (1 `shiftL` width)- else bvIntegerU bv- where width = bvWidth bv--------------------------------------------- BitVector w operations (fixed width)---- | Bitwise and.-bvAnd :: BitVector w -> BitVector w -> BitVector w-bvAnd (BV wRepr x) (BV _ y) = BV wRepr (x .&. y)---- | Bitwise or.-bvOr :: BitVector w -> BitVector w -> BitVector w-bvOr (BV wRepr x) (BV _ y) = BV wRepr (x .|. y)---- | Bitwise xor.-bvXor :: BitVector w -> BitVector w -> BitVector w-bvXor (BV wRepr x) (BV _ y) = BV wRepr (x `xor` y)---- | Bitwise complement (flip every bit).-bvComplement :: BitVector w -> BitVector w-bvComplement (BV wRepr x) = BV wRepr (truncBits width (complement x))- where width = natValue wRepr---- | Bitwise shift. Uses an arithmetic right shift.-bvShift :: BitVector w -> Int -> BitVector w-bvShift bv@(BV wRepr _) shf = BV wRepr (truncBits width (x `shift` shf))- where width = natValue wRepr- x = bvIntegerS bv -- arithmetic right shift when negative--toPos :: Int -> Int-toPos x | x < 0 = 0-toPos x = x---- | Left shift.-bvShiftL :: BitVector w -> Int -> BitVector w-bvShiftL bv shf = bvShift bv (toPos shf)---- | Right arithmetic shift.-bvShiftRA :: BitVector w -> Int -> BitVector w-bvShiftRA bv shf = bvShift bv (- (toPos shf))---- | Right logical shift.-bvShiftRL :: BitVector w -> Int -> BitVector w-bvShiftRL bv@(BV wRepr _) shf = BV wRepr (truncBits width (x `shift` (- toPos shf)))- where width = natValue wRepr- x = bvIntegerU bv---- | Bitwise rotate.-bvRotate :: BitVector w -> Int -> BitVector w-bvRotate bv rot' = leftChunk `bvOr` rightChunk- where rot = rot' `mod` bvWidth bv- leftChunk = bvShift bv rot- rightChunk = bvShift bv (rot - bvWidth bv)---- | Get the width of a 'BitVector'.-bvWidth :: BitVector w -> Int-bvWidth (BV wRepr _) = fromIntegral (natValue wRepr)---- | Test if a particular bit is set.-bvTestBit :: BitVector w -> Int -> Bool-bvTestBit (BV _ x) b = testBit x b---- | Get the number of 1 bits in a 'BitVector'.-bvPopCount :: BitVector w -> Int-bvPopCount (BV _ x) = popCount x---- | Truncate a bit vector to a particular width given at runtime, while keeping the--- type-level width constant.-bvTruncBits :: BitVector w -> Int -> BitVector w-bvTruncBits (BV wRepr x) b = BV wRepr (truncBits b x)--------------------------------------------- BitVector w arithmetic operations (fixed width)---- | Bitwise add.-bvAdd :: BitVector w -> BitVector w -> BitVector w-bvAdd (BV wRepr x) (BV _ y) = BV wRepr (truncBits width (x + y))- where width = natValue wRepr---- | Bitwise multiply.-bvMul :: BitVector w -> BitVector w -> BitVector w-bvMul (BV wRepr x) (BV _ y) = BV wRepr (truncBits width (x * y))- where width = natValue wRepr---- | Bitwise division (unsigned). Rounds to zero.-bvQuotU :: BitVector w -> BitVector w -> BitVector w-bvQuotU (BV wRepr x) (BV _ y) = BV wRepr (x `quot` y)---- | Bitwise division (signed). Rounds to zero (not negative infinity).-bvQuotS :: BitVector w -> BitVector w -> BitVector w-bvQuotS bv1@(BV wRepr _) bv2 = BV wRepr (truncBits width (x `quot` y))- where x = bvIntegerS bv1- y = bvIntegerS bv2- width = natValue wRepr---- | Bitwise remainder after division (unsigned), when rounded to zero.-bvRemU :: BitVector w -> BitVector w -> BitVector w-bvRemU (BV wRepr x) (BV _ y) = BV wRepr (x `rem` y)---- | Bitwise remainder after division (signed), when rounded to zero (not negative--- infinity).-bvRemS :: BitVector w -> BitVector w -> BitVector w-bvRemS bv1@(BV wRepr _) bv2 = BV wRepr (truncBits width (x `rem` y))- where x = bvIntegerS bv1- y = bvIntegerS bv2- width = natValue wRepr---- | Bitwise absolute value.-bvAbs :: BitVector w -> BitVector w-bvAbs bv@(BV wRepr _) = BV wRepr abs_x- where width = natValue wRepr- x = bvIntegerS bv- abs_x = truncBits width (abs x) -- this is necessary---- | Bitwise negation.-bvNegate :: BitVector w -> BitVector w-bvNegate (BV wRepr x) = BV wRepr (truncBits width (-x))- where width = fromIntegral (natValue wRepr) :: Integer---- | Get the sign bit as a 'BitVector'.-bvSignum :: BitVector w -> BitVector w-bvSignum bv@(BV wRepr _) = bvShift bv (1 - width) `bvAnd` BV wRepr 0x1- where width = fromIntegral (natValue wRepr)---- | Signed less than.-bvLTS :: BitVector w -> BitVector w -> Bool-bvLTS bv1 bv2 = bvIntegerS bv1 < bvIntegerS bv2---- | Unsigned less than.-bvLTU :: BitVector w -> BitVector w -> Bool-bvLTU bv1 bv2 = bvIntegerU bv1 < bvIntegerU bv2--------------------------------------------- Width-changing operations---- | Concatenate two bit vectors.------ >>> (0xAA :: BitVector 8) `bvConcat` (0xBCDEF0 :: BitVector 24)--- 0xaabcdef0--- >>> :type it--- it :: BitVector 32------ Note that the first argument gets placed in the higher-order bits. The above--- example should be illustrative enough.-bvConcat :: BitVector v -> BitVector w -> BitVector (v+w)-bvConcat (BV hiWRepr hi) (BV loWRepr lo) =- BV (hiWRepr `addNat` loWRepr) ((hi `shiftL` loWidth) .|. lo)- where loWidth = fromIntegral (natValue loWRepr)---- | Infix 'bvConcat'.-(<:>) :: BitVector v -> BitVector w -> BitVector (v+w)-(<:>) = bvConcat--bvConcatSome :: Some BitVector -> Some BitVector -> Some BitVector-bvConcatSome (Some bv1) (Some bv2) = Some (bv2 <:> bv1)---- | Concatenate a list of 'BitVector's into a 'BitVector' of arbitrary width. The ordering is little endian:------ >>> bvConcatMany [0xAA :: BitVector 8, 0xBB] :: BitVector 16--- 0xbbaa--- >>> bvConcatMany [0xAA :: BitVector 8, 0xBB, 0xCC] :: BitVector 16--- 0xbbaa------ If the sum of the widths of the input 'BitVector's exceeds the output width, we--- ignore the tail end of the list.-bvConcatMany :: KnownNat w' => [BitVector w] -> BitVector w'-bvConcatMany = bvConcatManyWithRepr knownNat---- | 'bvConcatMany' with an explicit 'NatRepr'.-bvConcatManyWithRepr :: NatRepr w' -> [BitVector w] -> BitVector w'-bvConcatManyWithRepr wRepr bvs =- viewSome (bvZextWithRepr wRepr) $ foldl bvConcatSome (Some bv0) (Some <$> bvs)--infixl 6 <:>---- | Slice out a smaller bit vector from a larger one. The lowest significant bit is--- given explicitly as an argument of type 'Int', and the length of the slice is--- inferred from a type-level context.------ >>> bvExtract 12 (0xAABCDEF0 :: BitVector 32) :: BitVector 8--- 0xcd------ Note that 'bvExtract' does not do any bounds checking whatsoever; if you try and--- extract bits that aren't present in the input, you will get 0's.-bvExtract :: forall w w' . (KnownNat w')- => Int- -> BitVector w- -> BitVector w'-bvExtract pos bv = bitVector xShf- where (BV _ xShf) = bvShift bv (- pos)---- | Unconstrained variant of 'bvExtract' with an explicit 'NatRepr' argument.-bvExtractWithRepr :: NatRepr w'- -> Int- -> BitVector w- -> BitVector w'-bvExtractWithRepr repr pos bv = BV repr (truncBits width xShf)- where (BV _ xShf) = bvShift bv (- pos)- width = natValue repr---- | Zero-extend a vector to one of greater length. If given an input of greater--- length than the output type, this performs a truncation.-bvZext :: forall w w' . KnownNat w'- => BitVector w- -> BitVector w'-bvZext (BV _ x) = bitVector x---- | Unconstrained variant of 'bvZext' with an explicit 'NatRepr' argument.-bvZextWithRepr :: NatRepr w'- -> BitVector w- -> BitVector w'-bvZextWithRepr repr (BV _ x) = BV repr (truncBits width x)- where width = natValue repr---- | Sign-extend a vector to one of greater length. If given an input of greater--- length than the output type, this performs a truncation.-bvSext :: forall w w' . KnownNat w'- => BitVector w- -> BitVector w'-bvSext bv = bitVector (bvIntegerS bv)---- | Unconstrained variant of 'bvSext' with an explicit 'NatRepr' argument.-bvSextWithRepr :: NatRepr w'- -> BitVector w- -> BitVector w'-bvSextWithRepr repr bv = BV repr (truncBits width (bvIntegerS bv))- where width = natValue repr--------------------------------------------- Byte decomposition---- | Given a 'BitVector' of arbitrary length, decompose it into a list of bytes. Uses--- an unsigned interpretation of the input vector, so if you ask for more bytes that--- the 'BitVector' contains, you get zeros. The result is little-endian, so the first--- element of the list will be the least significant byte of the input vector.-bvGetBytesU :: Int -> BitVector w -> [BitVector 8]-bvGetBytesU n _ | n <= 0 = []-bvGetBytesU n bv = bvExtract 0 bv : bvGetBytesU (n-1) (bvShiftRL bv 8)--------------------------------------------- Bits---- | Mask for a specified number of lower bits.-lowMask :: (Integral a, Bits b) => a -> b-lowMask numBits = complement (complement zeroBits `shiftL` fromIntegral numBits)---- | Truncate to a specified number of lower bits.-truncBits :: (Integral a, Bits b) => a -> b -> b-truncBits width b = b .&. lowMask width--------------------------------------------- Class instances-$(return [])--instance Show (BitVector w) where- show (BV _ x) = "0x" ++ showHex x ""--instance KnownNat w => Read (BitVector w) where- readsPrec s =- (fmap . fmap) (\(a,s') -> (bitVector a, s')) (readsPrec s :: ReadS Integer)--instance ShowF BitVector--instance Eq (BitVector w) where- (BV _ x) == (BV _ y) = x == y--instance EqF BitVector where- (BV _ x) `eqF` (BV _ y) = x == y--instance Ord (BitVector w) where- (BV _ x) `compare` (BV _ y) = x `compare` y--instance OrdF BitVector where- (BV xRepr x) `compareF` (BV yRepr y) =- case xRepr `compareF` yRepr of- EQF -> fromOrdering (x `compare` y)- cmp -> cmp--instance TestEquality BitVector where- testEquality (BV wRepr x) (BV wRepr' y) =- if natValue wRepr == natValue wRepr' && x == y- then Just (unsafeCoerce (Refl :: a :~: a))- else Nothing--instance KnownNat w => Bits (BitVector w) where- (.&.) = bvAnd- (.|.) = bvOr- xor = bvXor- complement = bvComplement- shift = bvShift- rotate = bvRotate- bitSize = bvWidth- bitSizeMaybe = Just . bvWidth- isSigned = const False- testBit = bvTestBit- bit = bitVector . bit- popCount = bvPopCount--instance KnownNat w => FiniteBits (BitVector w) where- finiteBitSize = bvWidth--instance KnownNat w => Num (BitVector w) where- (+) = bvAdd- (*) = bvMul- abs = bvAbs- signum = bvSignum- fromInteger = bitVector- negate = bvNegate--instance KnownNat w => Enum (BitVector w) where- toEnum = bitVector . fromIntegral- fromEnum = fromIntegral . bvIntegerU--instance KnownNat w => Ix (BitVector w) where- range (lo, hi) = bitVector <$> [bvIntegerU lo .. bvIntegerU hi]- index (lo, hi) bv = index (bvIntegerU lo, bvIntegerU hi) (bvIntegerU bv)- inRange (lo, hi) bv = inRange (bvIntegerU lo, bvIntegerU hi) (bvIntegerU bv)--instance KnownNat w => Bounded (BitVector w) where- minBound = bitVector 0- maxBound = bitVector (-1)--instance KnownNat w => Arbitrary (BitVector w) where- arbitrary = choose (minBound, maxBound)--instance KnownNat w => Random (BitVector w) where- randomR (bvLo, bvHi) gen =- let (x, gen') = randomR (bvIntegerU bvLo, bvIntegerU bvHi) gen- in (bitVector x, gen')- random gen =- let (x, gen') = random gen- in (bitVector x, gen')--prettyHex :: (Integral a, PrintfArg a, Show a) => a -> Integer -> String-prettyHex width val = printf format val width- where numDigits = (width+3) `quot` 4- format = "0x%." ++ show numDigits ++ "x<%d>"--instance Pretty (BitVector w) where- -- | Pretty print a bit vector (shows its width)- pPrint (BV wRepr x) = text $ prettyHex (natValue wRepr) x+import Data.BitVector.Sized.Internal
src/Data/BitVector/Sized/App.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE PolyKinds #-}@@ -48,6 +49,9 @@ , quotsE , remuE , remsE+ , negateE+ , absE+ , signumE , sllE , srlE , sraE@@ -56,9 +60,9 @@ , ltuE , ltsE -- ** Width-changing- , zextE, zextEWithRepr- , sextE, sextEWithRepr- , extractE, extractEWithRepr+ , zextE, zextE'+ , sextE, sextE'+ , extractE, extractE' , concatE -- ** Control , iteE@@ -66,6 +70,7 @@ import Control.Monad.Identity import Data.BitVector.Sized+import Data.Bits import Data.Parameterized import Data.Parameterized.TH.GADT import Foreign.Marshal.Utils (fromBool)@@ -96,6 +101,9 @@ QuotSApp :: !(expr w) -> !(expr w) -> BVApp expr w RemUApp :: !(expr w) -> !(expr w) -> BVApp expr w RemSApp :: !(expr w) -> !(expr w) -> BVApp expr w+ NegateApp :: !(expr w) -> BVApp expr w+ AbsApp :: !(expr w) -> BVApp expr w+ SignumApp :: !(expr w) -> BVApp expr w -- Comparisons EqApp :: !(expr w) -> !(expr w) -> BVApp expr 1@@ -120,6 +128,9 @@ instance TestEquality expr => Eq (BVApp expr w) where (==) = \x y -> isJust (testEquality x y) +instance TestEquality expr => EqF (BVApp expr) where+ eqF = (==)+ instance OrdF expr => OrdF (BVApp expr) where compareF = $(structuralTypeOrd [t|BVApp|] [ (AnyType `TypeApp` AnyType, [|compareF|]) ])@@ -160,12 +171,15 @@ evalBVAppM eval (QuotUApp e1 e2) = bvQuotU <$> eval e1 <*> eval e2 evalBVAppM eval (RemSApp e1 e2) = bvRemS <$> eval e1 <*> eval e2 evalBVAppM eval (RemUApp e1 e2) = bvRemU <$> eval e1 <*> eval e2+evalBVAppM eval (NegateApp e) = bvNegate <$> eval e+evalBVAppM eval (AbsApp e) = bvAbs <$> eval e+evalBVAppM eval (SignumApp e) = bvSignum <$> eval e evalBVAppM eval (EqApp e1 e2) = fromBool <$> ((==) <$> eval e1 <*> eval e2) evalBVAppM eval (LtuApp e1 e2) = fromBool <$> (bvLTU <$> eval e1 <*> eval e2) evalBVAppM eval (LtsApp e1 e2) = fromBool <$> (bvLTS <$> eval e1 <*> eval e2)-evalBVAppM eval (ZExtApp wRepr e) = bvZextWithRepr wRepr <$> eval e-evalBVAppM eval (SExtApp wRepr e) = bvSextWithRepr wRepr <$> eval e-evalBVAppM eval (ExtractApp wRepr base e) = bvExtractWithRepr wRepr base <$> eval e+evalBVAppM eval (ZExtApp wRepr e) = bvZext' wRepr <$> eval e+evalBVAppM eval (SExtApp wRepr e) = bvSext' wRepr <$> eval e+evalBVAppM eval (ExtractApp wRepr base e) = bvExtract' wRepr base <$> eval e evalBVAppM eval (ConcatApp e1 e2) = do e1Val <- eval e1 e2Val <- eval e2@@ -186,6 +200,31 @@ class BVExpr (expr :: Nat -> *) where appExpr :: BVApp expr w -> expr w +instance (KnownNat w, BVExpr expr) => Num (BVApp expr w) where+ app1 + app2 = AddApp (appExpr app1) (appExpr app2)+ app1 * app2 = MulApp (appExpr app1) (appExpr app2)+ abs app = AbsApp (appExpr app)+ signum app = SignumApp (appExpr app)+ fromInteger x = LitBVApp (fromInteger x)+ negate app = NegateApp (appExpr app)+ app1 - app2 = SubApp (appExpr app1) (appExpr app2)++-- TODO: finish+instance (KnownNat w, BVExpr expr, TestEquality expr) => Bits (BVApp expr w) where+ app1 .&. app2 = AndApp (appExpr app1) (appExpr app2)+ app1 .|. app2 = OrApp (appExpr app1) (appExpr app2)+ app1 `xor` app2 = XorApp (appExpr app1) (appExpr app2)+ complement app = NotApp (appExpr app)+ shiftL app x = SllApp (appExpr app) (litBV (bitVector x))+ shiftR app x = SraApp (appExpr app) (litBV (bitVector x))+ rotate = undefined+ bitSize = undefined+ bitSizeMaybe = undefined+ isSigned = undefined+ testBit = undefined+ bit = undefined+ popCount = undefined+ -- | Literal bit vector. litBV :: BVExpr expr => BitVector w -> expr w litBV = appExpr . LitBVApp@@ -214,27 +253,36 @@ subE :: BVExpr expr => expr w -> expr w -> expr w subE e1 e2 = appExpr (SubApp e1 e2) --- | Signed multiply two 'BitVectors', doubling the width of the result to hold all+-- | Signed multiply two 'BitVector's, doubling the width of the result to hold all -- arithmetic overflow bits. mulE :: BVExpr expr => expr w -> expr w -> expr w mulE e1 e2 = appExpr (MulApp e1 e2) --- | Signed divide two 'BitVectors', rounding to zero.+-- | Signed divide two 'BitVector's, rounding to zero. quotsE :: BVExpr expr => expr w -> expr w -> expr w quotsE e1 e2 = appExpr (QuotSApp e1 e2) --- | Unsigned divide two 'BitVectors', rounding to zero.+-- | Unsigned divide two 'BitVector's, rounding to zero. quotuE :: BVExpr expr => expr w -> expr w -> expr w quotuE e1 e2 = appExpr (QuotUApp e1 e2) --- | Remainder after signed division of two 'BitVectors', when rounded to zero.+-- | Remainder after signed division of two 'BitVector's, when rounded to zero. remsE :: BVExpr expr => expr w -> expr w -> expr w remsE e1 e2 = appExpr (RemSApp e1 e2) --- | Remainder after unsigned division of two 'BitVectors', when rounded to zero.+-- | Remainder after unsigned division of two 'BitVector's, when rounded to zero. remuE :: BVExpr expr => expr w -> expr w -> expr w remuE e1 e2 = appExpr (RemUApp e1 e2) +negateE :: BVExpr expr => expr w -> expr w+negateE e = appExpr (NegateApp e)++absE :: BVExpr expr => expr w -> expr w+absE e = appExpr (AbsApp e)++signumE :: BVExpr expr => expr w -> expr w+signumE e = appExpr (SignumApp e)+ -- | Left logical shift the first expression by the second. sllE :: BVExpr expr => expr w -> expr w -> expr w sllE e1 e2 = appExpr (SllApp e1 e2)@@ -264,24 +312,24 @@ zextE e = appExpr (ZExtApp knownNat e) -- | Zero-extension with an explicit width argument-zextEWithRepr :: BVExpr expr => NatRepr w' -> expr w -> expr w'-zextEWithRepr repr e = appExpr (ZExtApp repr e)+zextE' :: BVExpr expr => NatRepr w' -> expr w -> expr w'+zextE' repr e = appExpr (ZExtApp repr e) -- | Sign-extension sextE :: (BVExpr expr, KnownNat w') => expr w -> expr w' sextE e = appExpr (SExtApp knownNat e) -- | Sign-extension with an explicit width argument-sextEWithRepr :: BVExpr expr => NatRepr w' -> expr w -> expr w'-sextEWithRepr repr e = appExpr (SExtApp repr e)+sextE' :: BVExpr expr => NatRepr w' -> expr w -> expr w'+sextE' repr e = appExpr (SExtApp repr e) -- | Extract bits extractE :: (BVExpr expr, KnownNat w') => Int -> expr w -> expr w' extractE base e = appExpr (ExtractApp knownNat base e) -- | Extract bits with an explicit width argument-extractEWithRepr :: BVExpr expr => NatRepr w' -> Int -> expr w -> expr w'-extractEWithRepr wRepr base e = appExpr (ExtractApp wRepr base e)+extractE' :: BVExpr expr => NatRepr w' -> Int -> expr w -> expr w'+extractE' wRepr base e = appExpr (ExtractApp wRepr base e) -- | Concatenation concatE :: BVExpr expr => expr w -> expr w' -> expr (w+w')
src/Data/BitVector/Sized/BitLayout.hs view
@@ -32,9 +32,11 @@ , extract -- * Lenses , layoutLens, layoutsLens+ -- * Utilities+ , bitLayoutAssignmentList ) where -import Data.BitVector.Sized+import Data.BitVector.Sized.Internal import Data.Foldable import qualified Data.Functor.Product as P import Control.Lens (lens, Simple, Lens)@@ -198,8 +200,8 @@ -> BitVector s -> BitVector t -> BitVector t-bvOrAt start sVec tVec@(BV tRepr _) =- (bvZextWithRepr tRepr sVec `bvShift` start) `bvOr` tVec+bvOrAt start sVec tVec@(BitVector tRepr _) =+ (bvZext' tRepr sVec `bvShift` start) `bvOr` tVec -- | Given a list of 'Chunk's, inject each chunk from a source 'BitVector' @s@ into a -- target 'BitVector' @t@.@@ -230,7 +232,7 @@ -> BitVector s extractChunk sRepr sStart (Some (Chunk chunkRepr chunkStart)) tVec = bvShift extractedChunk sStart- where extractedChunk = bvZextWithRepr sRepr (bvExtractWithRepr chunkRepr chunkStart tVec)+ where extractedChunk = bvZext' sRepr (bvExtract' chunkRepr chunkStart tVec) extractAll :: NatRepr s -- ^ determines width of output vector -> Int -- ^ current position in output vector@@ -241,7 +243,7 @@ extractAll sRepr outStart (chk@(Some (Chunk chunkRepr _)) : chunks) tVec = extractChunk sRepr outStart chk tVec `bvOr` extractAll sRepr (outStart + chunkWidth) chunks tVec- where chunkWidth = fromInteger (natValue chunkRepr)+ where chunkWidth = fromInteger (intValue chunkRepr) -- | Use a 'BitLayout' to extract a smaller vector from a larger one. extract :: BitLayout t s -- ^ The layout@@ -260,3 +262,22 @@ (\bv bvFlds -> ifoldr (\_ (P.Pair fld layout) bv' -> inject layout bv' fld) bv (izipWith (const P.Pair) bvFlds layouts))++-- | From a `BitLayout`, get a list representing the position of each bit from the+-- source to the target. The list+--+-- @+-- [3,4,5,10,11,12,13]+-- @+--+-- means that bit 0 of the source is placed in bit 3 of the target, bit 1 of the+-- source is placed in bit 4 of the target, etc.++bitLayoutAssignmentList :: BitLayout t s -> [Int]+bitLayoutAssignmentList (BitLayout _ _ someChunks) = reverse (bitLayoutAssignmentList' (toList someChunks))++bitLayoutAssignmentList' :: [Some Chunk] -> [Int]+bitLayoutAssignmentList' [] = []+bitLayoutAssignmentList' (Some (Chunk wRepr start):rst) =+ reverse [start..start+w-1] ++ bitLayoutAssignmentList' rst+ where w = fromIntegral (natValue wRepr)
+ src/Data/BitVector/Sized/Internal.hs view
@@ -0,0 +1,419 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeOperators #-}++{-|+Module : Data.BitVector.Sized+Copyright : (c) Galois Inc. 2018+License : BSD-3+Maintainer : benselfridge@galois.com+Stability : experimental+Portability : portable++This module defines a width-parameterized 'BitVector' type and various associated+operations that assume a 2's complement representation.+-}++module Data.BitVector.Sized.Internal where++import Data.Bits+import Data.Ix+import Data.Parameterized+import GHC.TypeLits+import Numeric+import System.Random+import Test.QuickCheck (Arbitrary(..), choose)+import Text.PrettyPrint.HughesPJClass+import Text.Printf+import Unsafe.Coerce (unsafeCoerce)++----------------------------------------+-- BitVector data type definitions++-- | BitVector datatype, parameterized by width.+data BitVector (w :: Nat) :: * where+ BV :: NatRepr w -> Integer -> BitVector w++-- | 'BitVector' can be treated as a constructor for pattern matching, but to build+-- one you must use the smart constructor `bitVector`.+pattern BitVector :: NatRepr w -> Integer -> BitVector w+pattern BitVector wRepr x <- BV wRepr x+{-# COMPLETE BitVector #-}++-- | Construct a bit vector with a particular width, where the width is inferrable+-- from the type context. The input (an unbounded data type, hence with an+-- infinite-width bit representation), whether positive or negative, is silently+-- truncated to fit into the number of bits demanded by the return type.+--+-- >>> bitVector 0xA :: BitVector 4+-- 0xa+-- >>> bitVector 0xA :: BitVector 2+-- 0x2+bitVector :: (Integral a, KnownNat w) => a -> BitVector w+bitVector x = bitVector' knownNat x++-- | Like 'bitVector', but with an explict 'NatRepr'.+bitVector' :: Integral a => NatRepr w -> a -> BitVector w+bitVector' wRepr x = BV wRepr (truncBits width (fromIntegral x))+ where width = natValue wRepr++-- | The zero bitvector with width 0.+bv0 :: BitVector 0+bv0 = bitVector (0 :: Integer)++----------------------------------------+-- BitVector -> Integer functions++-- | Unsigned interpretation of a bit vector as a (positive) Integer.+bvIntegerU :: BitVector w -> Integer+bvIntegerU (BV _ x) = x++-- | Signed interpretation of a bit vector as an Integer.+bvIntegerS :: BitVector w -> Integer+bvIntegerS bv = if bvTestBit bv (width - 1)+ then bvIntegerU bv - (1 `shiftL` width)+ else bvIntegerU bv+ where width = bvWidth bv++----------------------------------------+-- BitVector w operations (fixed width)++-- | Bitwise and.+bvAnd :: BitVector w -> BitVector w -> BitVector w+bvAnd (BV wRepr x) (BV _ y) = BV wRepr (x .&. y)++-- | Bitwise or.+bvOr :: BitVector w -> BitVector w -> BitVector w+bvOr (BV wRepr x) (BV _ y) = BV wRepr (x .|. y)++-- | Bitwise xor.+bvXor :: BitVector w -> BitVector w -> BitVector w+bvXor (BV wRepr x) (BV _ y) = BV wRepr (x `xor` y)++-- | Bitwise complement (flip every bit).+bvComplement :: BitVector w -> BitVector w+bvComplement (BV wRepr x) = BV wRepr (truncBits width (complement x))+ where width = natValue wRepr++-- | Bitwise shift. Uses an arithmetic right shift.+bvShift :: BitVector w -> Int -> BitVector w+bvShift bv@(BV wRepr _) shf = BV wRepr (truncBits width (x `shift` shf))+ where width = natValue wRepr+ x = bvIntegerS bv -- arithmetic right shift when negative++toPos :: Int -> Int+toPos x | x < 0 = 0+toPos x = x++-- | Left shift.+bvShiftL :: BitVector w -> Int -> BitVector w+bvShiftL bv shf = bvShift bv (toPos shf)++-- | Right arithmetic shift.+bvShiftRA :: BitVector w -> Int -> BitVector w+bvShiftRA bv shf = bvShift bv (- (toPos shf))++-- | Right logical shift.+bvShiftRL :: BitVector w -> Int -> BitVector w+bvShiftRL bv@(BV wRepr _) shf = BV wRepr (truncBits width (x `shift` (- toPos shf)))+ where width = natValue wRepr+ x = bvIntegerU bv++-- | Bitwise rotate.+bvRotate :: BitVector w -> Int -> BitVector w+bvRotate bv rot' = leftChunk `bvOr` rightChunk+ where rot = rot' `mod` bvWidth bv+ leftChunk = bvShift bv rot+ rightChunk = bvShift bv (rot - bvWidth bv)++-- | Get the width of a 'BitVector'.+bvWidth :: BitVector w -> Int+bvWidth (BV wRepr _) = fromIntegral (natValue wRepr)++-- | Test if a particular bit is set.+bvTestBit :: BitVector w -> Int -> Bool+bvTestBit (BV _ x) b = testBit x b++-- | Get the number of 1 bits in a 'BitVector'.+bvPopCount :: BitVector w -> Int+bvPopCount (BV _ x) = popCount x++-- | Truncate a bit vector to a particular width given at runtime, while keeping the+-- type-level width constant.+bvTruncBits :: BitVector w -> Int -> BitVector w+bvTruncBits (BV wRepr x) b = BV wRepr (truncBits b x)++----------------------------------------+-- BitVector w arithmetic operations (fixed width)++-- | Bitwise add.+bvAdd :: BitVector w -> BitVector w -> BitVector w+bvAdd (BV wRepr x) (BV _ y) = BV wRepr (truncBits width (x + y))+ where width = natValue wRepr++-- | Bitwise multiply.+bvMul :: BitVector w -> BitVector w -> BitVector w+bvMul (BV wRepr x) (BV _ y) = BV wRepr (truncBits width (x * y))+ where width = natValue wRepr++-- | Bitwise division (unsigned). Rounds to zero.+bvQuotU :: BitVector w -> BitVector w -> BitVector w+bvQuotU (BV wRepr x) (BV _ y) = BV wRepr (x `quot` y)++-- | Bitwise division (signed). Rounds to zero (not negative infinity).+bvQuotS :: BitVector w -> BitVector w -> BitVector w+bvQuotS bv1@(BV wRepr _) bv2 = BV wRepr (truncBits width (x `quot` y))+ where x = bvIntegerS bv1+ y = bvIntegerS bv2+ width = natValue wRepr++-- | Bitwise remainder after division (unsigned), when rounded to zero.+bvRemU :: BitVector w -> BitVector w -> BitVector w+bvRemU (BV wRepr x) (BV _ y) = BV wRepr (x `rem` y)++-- | Bitwise remainder after division (signed), when rounded to zero (not negative+-- infinity).+bvRemS :: BitVector w -> BitVector w -> BitVector w+bvRemS bv1@(BV wRepr _) bv2 = BV wRepr (truncBits width (x `rem` y))+ where x = bvIntegerS bv1+ y = bvIntegerS bv2+ width = natValue wRepr++-- | Bitwise absolute value.+bvAbs :: BitVector w -> BitVector w+bvAbs bv@(BV wRepr _) = BV wRepr abs_x+ where width = natValue wRepr+ x = bvIntegerS bv+ abs_x = truncBits width (abs x) -- this is necessary++-- | Bitwise negation.+bvNegate :: BitVector w -> BitVector w+bvNegate (BV wRepr x) = BV wRepr (truncBits width (-x))+ where width = fromIntegral (natValue wRepr) :: Integer++-- | Get the sign bit as a 'BitVector'.+bvSignum :: BitVector w -> BitVector w+bvSignum bv@(BV wRepr _) = bvShift bv (1 - width) `bvAnd` BV wRepr 0x1+ where width = fromIntegral (natValue wRepr)++-- | Signed less than.+bvLTS :: BitVector w -> BitVector w -> Bool+bvLTS bv1 bv2 = bvIntegerS bv1 < bvIntegerS bv2++-- | Unsigned less than.+bvLTU :: BitVector w -> BitVector w -> Bool+bvLTU bv1 bv2 = bvIntegerU bv1 < bvIntegerU bv2++----------------------------------------+-- Width-changing operations++-- | Concatenate two bit vectors.+--+-- >>> (0xAA :: BitVector 8) `bvConcat` (0xBCDEF0 :: BitVector 24)+-- 0xaabcdef0+-- >>> :type it+-- it :: BitVector 32+--+-- Note that the first argument gets placed in the higher-order bits. The above+-- example should be illustrative enough.+bvConcat :: BitVector v -> BitVector w -> BitVector (v+w)+bvConcat (BV hiWRepr hi) (BV loWRepr lo) =+ BV (hiWRepr `addNat` loWRepr) ((hi `shiftL` loWidth) .|. lo)+ where loWidth = fromIntegral (natValue loWRepr)++-- | Infix 'bvConcat'.+(<:>) :: BitVector v -> BitVector w -> BitVector (v+w)+(<:>) = bvConcat++bvConcatSome :: Some BitVector -> Some BitVector -> Some BitVector+bvConcatSome (Some bv1) (Some bv2) = Some (bv2 <:> bv1)++-- | Concatenate a list of 'BitVector's into a 'BitVector' of arbitrary width. The ordering is little endian:+--+-- >>> bvConcatMany [0xAA :: BitVector 8, 0xBB] :: BitVector 16+-- 0xbbaa+-- >>> bvConcatMany [0xAA :: BitVector 8, 0xBB, 0xCC] :: BitVector 16+-- 0xbbaa+--+-- If the sum of the widths of the input 'BitVector's exceeds the output width, we+-- ignore the tail end of the list.+bvConcatMany :: KnownNat w' => [BitVector w] -> BitVector w'+bvConcatMany = bvConcatMany' knownNat++-- | 'bvConcatMany' with an explicit 'NatRepr'.+bvConcatMany' :: NatRepr w' -> [BitVector w] -> BitVector w'+bvConcatMany' wRepr bvs =+ viewSome (bvZext' wRepr) $ foldl bvConcatSome (Some bv0) (Some <$> bvs)++infixl 6 <:>++-- | Slice out a smaller bit vector from a larger one. The lowest significant bit is+-- given explicitly as an argument of type 'Int', and the length of the slice is+-- inferred from a type-level context.+--+-- >>> bvExtract 12 (0xAABCDEF0 :: BitVector 32) :: BitVector 8+-- 0xcd+--+-- Note that 'bvExtract' does not do any bounds checking whatsoever; if you try and+-- extract bits that aren't present in the input, you will get 0's.+bvExtract :: forall w w' . (KnownNat w')+ => Int+ -> BitVector w+ -> BitVector w'+bvExtract pos bv = bitVector xShf+ where (BV _ xShf) = bvShift bv (- pos)++-- | Unconstrained variant of 'bvExtract' with an explicit 'NatRepr' argument.+bvExtract' :: NatRepr w'+ -> Int+ -> BitVector w+ -> BitVector w'+bvExtract' repr pos bv = BV repr (truncBits width xShf)+ where (BV _ xShf) = bvShift bv (- pos)+ width = natValue repr++-- | Zero-extend a vector to one of greater length. If given an input of greater+-- length than the output type, this performs a truncation.+bvZext :: forall w w' . KnownNat w'+ => BitVector w+ -> BitVector w'+bvZext (BV _ x) = bitVector x++-- | Unconstrained variant of 'bvZext' with an explicit 'NatRepr' argument.+bvZext' :: NatRepr w'+ -> BitVector w+ -> BitVector w'+bvZext' repr (BV _ x) = BV repr (truncBits width x)+ where width = natValue repr++-- | Sign-extend a vector to one of greater length. If given an input of greater+-- length than the output type, this performs a truncation.+bvSext :: forall w w' . KnownNat w'+ => BitVector w+ -> BitVector w'+bvSext bv = bitVector (bvIntegerS bv)++-- | Unconstrained variant of 'bvSext' with an explicit 'NatRepr' argument.+bvSext' :: NatRepr w'+ -> BitVector w+ -> BitVector w'+bvSext' repr bv = BV repr (truncBits width (bvIntegerS bv))+ where width = natValue repr++----------------------------------------+-- Byte decomposition++-- | Given a 'BitVector' of arbitrary length, decompose it into a list of bytes. Uses+-- an unsigned interpretation of the input vector, so if you ask for more bytes that+-- the 'BitVector' contains, you get zeros. The result is little-endian, so the first+-- element of the list will be the least significant byte of the input vector.+bvGetBytesU :: Int -> BitVector w -> [BitVector 8]+bvGetBytesU n _ | n <= 0 = []+bvGetBytesU n bv = bvExtract 0 bv : bvGetBytesU (n-1) (bvShiftRL bv 8)++----------------------------------------+-- Bits++-- | Mask for a specified number of lower bits.+lowMask :: (Integral a, Bits b) => a -> b+lowMask numBits = complement (complement zeroBits `shiftL` fromIntegral numBits)++-- | Truncate to a specified number of lower bits.+truncBits :: (Integral a, Bits b) => a -> b -> b+truncBits width b = b .&. lowMask width++----------------------------------------+-- Class instances+$(return [])++instance Show (BitVector w) where+ show (BV _ x) = "0x" ++ showHex x ""++instance KnownNat w => Read (BitVector w) where+ readsPrec s =+ (fmap . fmap) (\(a,s') -> (bitVector a, s')) (readsPrec s :: ReadS Integer)++instance ShowF BitVector++instance Eq (BitVector w) where+ (BV _ x) == (BV _ y) = x == y++instance EqF BitVector where+ (BV _ x) `eqF` (BV _ y) = x == y++instance Ord (BitVector w) where+ (BV _ x) `compare` (BV _ y) = x `compare` y++instance OrdF BitVector where+ (BV xRepr x) `compareF` (BV yRepr y) =+ case xRepr `compareF` yRepr of+ EQF -> fromOrdering (x `compare` y)+ cmp -> cmp++instance TestEquality BitVector where+ testEquality (BV wRepr x) (BV wRepr' y) =+ if natValue wRepr == natValue wRepr' && x == y+ then Just (unsafeCoerce (Refl :: a :~: a))+ else Nothing++instance KnownNat w => Bits (BitVector w) where+ (.&.) = bvAnd+ (.|.) = bvOr+ xor = bvXor+ complement = bvComplement+ shift = bvShift+ rotate = bvRotate+ bitSize = bvWidth+ bitSizeMaybe = Just . bvWidth+ isSigned = const False+ testBit = bvTestBit+ bit = bitVector . (bit :: Int -> Integer)+ popCount = bvPopCount++instance KnownNat w => FiniteBits (BitVector w) where+ finiteBitSize = bvWidth++instance KnownNat w => Num (BitVector w) where+ (+) = bvAdd+ (*) = bvMul+ abs = bvAbs+ signum = bvSignum+ fromInteger = bitVector+ negate = bvNegate++instance KnownNat w => Enum (BitVector w) where+ toEnum = bitVector+ fromEnum = fromIntegral . bvIntegerU++instance KnownNat w => Ix (BitVector w) where+ range (lo, hi) = bitVector <$> [bvIntegerU lo .. bvIntegerU hi]+ index (lo, hi) bv = index (bvIntegerU lo, bvIntegerU hi) (bvIntegerU bv)+ inRange (lo, hi) bv = inRange (bvIntegerU lo, bvIntegerU hi) (bvIntegerU bv)++instance KnownNat w => Bounded (BitVector w) where+ minBound = bitVector (0 :: Integer)+ maxBound = bitVector ((-1) :: Integer)++instance KnownNat w => Arbitrary (BitVector w) where+ arbitrary = choose (minBound, maxBound)++instance KnownNat w => Random (BitVector w) where+ randomR (bvLo, bvHi) gen =+ let (x, gen') = randomR (bvIntegerU bvLo, bvIntegerU bvHi) gen+ in (bitVector x, gen')+ random gen =+ let (x :: Integer, gen') = random gen+ in (bitVector x, gen')++prettyHex :: (Integral a, PrintfArg a, Show a) => a -> Integer -> String+prettyHex width val = printf format val width+ where numDigits = (width+3) `quot` 4+ format = "0x%." ++ show numDigits ++ "x<%d>"++instance Pretty (BitVector w) where+ -- | Pretty print a bit vector (shows its width)+ pPrint (BV wRepr x) = text $ prettyHex (natValue wRepr) x
− stack.yaml
@@ -1,12 +0,0 @@-resolver: lts-12.5--packages:-- .-- submodules/parameterized-utils--extra-deps:-- containers-0.5.11.0-- lens-4.16-- prettyclass-1.0.0.0-- random-1.1-- QuickCheck-2.11.3
+ submodules/parameterized-utils/.travis.yml view
@@ -0,0 +1,103 @@+# This Travis job script has been generated by a script via+#+# runghc make_travis_yml_2.hs 'parameterized-utils.cabal'+#+# For more information, see https://github.com/haskell-CI/haskell-ci+#+language: c+sudo: false++git:+ submodules: false # whether to recursively clone submodules++cache:+ directories:+ - $HOME/.cabal/packages+ - $HOME/.cabal/store++before_cache:+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log+ # remove files that are regenerated by 'cabal update'+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.*+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx++ - rm -rfv $HOME/.cabal/packages/head.hackage++matrix:+ include:+ - compiler: "ghc-8.4.3"+ # env: TEST=--disable-tests BENCH=--disable-benchmarks+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.4.3], sources: [hvr-ghc]}}++before_install:+ - HC=${CC}+ - HCPKG=${HC/ghc/ghc-pkg}+ - unset CC+ - ROOTDIR=$(pwd)+ - mkdir -p $HOME/.local/bin+ - "PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$HOME/local/bin:$PATH"+ - HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 * 10000 + \2 * 100 + \3/') ))+ - echo $HCNUMVER++install:+ - cabal --version+ - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"+ - BENCH=${BENCH---enable-benchmarks}+ - TEST=${TEST---enable-tests}+ - HADDOCK=${HADDOCK-true}+ - UNCONSTRAINED=${UNCONSTRAINED-true}+ - NOINSTALLEDCONSTRAINTS=${NOINSTALLEDCONSTRAINTS-false}+ - GHCHEAD=${GHCHEAD-false}+ - travis_retry cabal update -v+ - "sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config"+ - rm -fv cabal.project cabal.project.local+ - grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$'+ - "printf 'packages: \".\"\\n' > cabal.project"+ - touch cabal.project.local+ - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | grep -vw -- parameterized-utils | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"+ - cat cabal.project || true+ - cat cabal.project.local || true+ - if [ -f "./configure.ac" ]; then+ (cd "." && autoreconf -i);+ fi+ - rm -f cabal.project.freeze+ - cabal new-build -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project" --dep -j2 all+ - cabal new-build -w ${HC} --disable-tests --disable-benchmarks --project-file="cabal.project" --dep -j2 all+ - rm -rf .ghc.environment.* "."/dist+ - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)++# Here starts the actual work to be performed for the package under test;+# any command which exits with a non-zero exit code causes the build to fail.+script:+ # test that source-distributions can be generated+ - (cd "." && cabal sdist)+ - mv "."/dist/parameterized-utils-*.tar.gz ${DISTDIR}/+ - cd ${DISTDIR} || false+ - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \;+ - "printf 'packages: parameterized-utils-*/*.cabal\\n' > cabal.project"+ - touch cabal.project.local+ - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | grep -vw -- parameterized-utils | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"+ - cat cabal.project || true+ - cat cabal.project.local || true+ # this builds all libraries and executables (without tests/benchmarks)+ - cabal new-build -w ${HC} --disable-tests --disable-benchmarks all++ # build & run tests, build benchmarks+ - cabal new-build -w ${HC} ${TEST} ${BENCH} all+ - if [ "x$TEST" = "x--enable-tests" ]; then cabal new-test -w ${HC} ${TEST} ${BENCH} all; fi++ # cabal check+ - (cd parameterized-utils-* && cabal check)++ # haddock+ - rm -rf ./dist-newstyle+ - if $HADDOCK; then cabal new-haddock -w ${HC} ${TEST} ${BENCH} all; else echo "Skipping haddock generation";fi++ # Build without installed constraints for packages in global-db+ - if $UNCONSTRAINED; then rm -f cabal.project.local; echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks all; else echo "Not building without installed constraints"; fi++# REGENDATA ["parameterized-utils.cabal"]+# EOF
submodules/parameterized-utils/parameterized-utils.cabal view
@@ -1,5 +1,5 @@ Name: parameterized-utils-Version: 1.0.0+Version: 1.0.8 Author: Galois Inc. Maintainer: jhendrix@galois.com Build-type: Simple@@ -14,6 +14,7 @@ intended for things like expression libraries where one wishes to leverage the Haskell type-checker to improve type-safety by encoding the object language type system into data kinds.+tested-with: GHC==8.4.3, GHC==8.6.1 -- Many (but not all, sadly) uses of unsafe operations are -- controlled by this compile flag. When this flag is set@@ -30,8 +31,9 @@ library build-depends:- base >= 4.7 && < 4.12,+ base >= 4.7 && < 4.13, th-abstraction >=0.1 && <0.3,+ constraints >= 0.10 && < 0.11, containers, deepseq, ghc-prim,@@ -48,11 +50,14 @@ exposed-modules: Data.Parameterized Data.Parameterized.Classes+ Data.Parameterized.ClassesC+ Data.Parameterized.Compose Data.Parameterized.Context Data.Parameterized.Context.Safe Data.Parameterized.Context.Unsafe Data.Parameterized.Ctx Data.Parameterized.Ctx.Proofs+ Data.Parameterized.DecidableEq Data.Parameterized.HashTable Data.Parameterized.List Data.Parameterized.Map@@ -60,13 +65,16 @@ Data.Parameterized.Nonce Data.Parameterized.Nonce.Transformers Data.Parameterized.Nonce.Unsafe+ Data.Parameterized.Pair+ Data.Parameterized.Peano Data.Parameterized.Some Data.Parameterized.SymbolRepr- Data.Parameterized.Pair Data.Parameterized.TH.GADT Data.Parameterized.TraversableF Data.Parameterized.TraversableFC Data.Parameterized.Utils.BinTree+ Data.Parameterized.Utils.Endian+ Data.Parameterized.Vector ghc-options: -Wall @@ -84,6 +92,7 @@ other-modules: Test.Context Test.NatRepr+ Test.Vector build-depends: base,@@ -94,7 +103,7 @@ mtl, parameterized-utils, tasty,- tasty-ant-xml,+ tasty-ant-xml >= 1.1.0, tasty-hunit, tasty-quickcheck >= 0.8.1, QuickCheck >= 2.7
submodules/parameterized-utils/src/Data/Parameterized/Classes.hs view
@@ -37,6 +37,7 @@ , orderingF_refl , toOrdering , fromOrdering+ , ordFCompose -- * Typeclass generalizations , ShowF(..) , showsF@@ -55,11 +56,14 @@ ) where import Data.Functor.Const+import Data.Functor.Compose (Compose(..)) import Data.Hashable import Data.Maybe (isJust) import Data.Proxy import Data.Type.Equality as Equality +import Data.Parameterized.Compose ()+ -- We define these type alias here to avoid importing Control.Lens -- modules, as this apparently causes problems with the safe Hasekll -- checking.@@ -193,6 +197,23 @@ -> (a ~ b => OrderingF c d) -> OrderingF c d lexCompareF x y = joinOrderingF (compareF x y)++-- | If the \"outer\" functor has an 'OrdF' instance, then one can be generated+-- for the \"inner\" functor. The type-level evidence of equality is deduced+-- via generativity of @g@, e.g. the inference @g x ~ g y@ implies @x ~ y@.+ordFCompose :: forall (f :: k -> *) (g :: l -> k) x y.+ (forall w z. f w -> f z -> OrderingF w z)+ -> Compose f g x+ -> Compose f g y+ -> OrderingF x y+ordFCompose ordF_ (Compose x) (Compose y) =+ case ordF_ x y of+ LTF -> LTF+ GTF -> GTF+ EQF -> EQF++instance OrdF f => OrdF (Compose f g) where+ compareF x y = ordFCompose compareF x y ------------------------------------------------------------------------ -- ShowF
+ submodules/parameterized-utils/src/Data/Parameterized/ClassesC.hs view
@@ -0,0 +1,52 @@+{-|+Copyright : (c) Galois, Inc 2014-2015+Maintainer : Langston Barrett <langston@galois.com>++This module declares classes for working with types with the kind+@(k -> *) -> *@ for any kind @k@.++These classes generally require type-level evidence for operations+on their subterms, but don't actually provide it themselves (because+their types are not themselves parameterized, unlike those in+"Data.Parameterized.TraverableFC").++Note that there is still some ambiguity around naming conventions, see+<https://github.com/GaloisInc/parameterized-utils/issues/23 issue 23>.+-}++{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE Safe #-}+{-# LANGUAGE TypeOperators #-}++module Data.Parameterized.ClassesC+ ( TestEqualityC(..)+ , OrdC(..)+ ) where++import Data.Type.Equality ((:~:)(..))+import Data.Maybe (isJust)+import Data.Parameterized.Classes (OrderingF, toOrdering)+import Data.Parameterized.Some (Some(..))++class TestEqualityC (t :: (k -> *) -> *) where+ testEqualityC :: (forall x y. f x -> f y -> Maybe (x :~: y))+ -> t f+ -> t f+ -> Bool++class TestEqualityC t => OrdC (t :: (k -> *) -> *) where+ compareC :: (forall x y. f x -> g y -> OrderingF x y)+ -> t f+ -> t g+ -> Ordering++-- | This instance demonstrates where the above class is useful: namely, in+-- types with existential quantification.+instance TestEqualityC Some where+ testEqualityC subterms (Some someone) (Some something) =+ isJust (subterms someone something)++instance OrdC Some where+ compareC subterms (Some someone) (Some something) =+ toOrdering (subterms someone something)
+ submodules/parameterized-utils/src/Data/Parameterized/Compose.hs view
@@ -0,0 +1,45 @@+{-|+Copyright : (c) Galois, Inc 2014-2018+Maintainer : Langston Barrett <langston@galois.com++Utilities for working with "Data.Functor.Compose".++NB: This module contains an orphan instance. It will be included in GHC 8.10,+see https://gitlab.haskell.org/ghc/ghc/merge_requests/273.+-}++{-# LANGUAGE GADTs #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE Safe #-}++module Data.Parameterized.Compose+ ( testEqualityComposeBare+ ) where++import Data.Functor.Compose+import Data.Type.Equality++-- | The deduction (via generativity) that if @g x :~: g y@ then @x :~: y@.+--+-- See https://gitlab.haskell.org/ghc/ghc/merge_requests/273.+testEqualityComposeBare :: forall (f :: k -> *) (g :: l -> k) x y.+ (forall w z. f w -> f z -> Maybe (w :~: z))+ -> Compose f g x+ -> Compose f g y+ -> Maybe (x :~: y)+testEqualityComposeBare testEquality_ (Compose x) (Compose y) =+ case (testEquality_ x y :: Maybe (g x :~: g y)) of+ Just Refl -> Just (Refl :: x :~: y)+ Nothing -> Nothing++testEqualityCompose :: forall (f :: k -> *) (g :: l -> k) x y. (TestEquality f)+ => Compose f g x+ -> Compose f g y+ -> Maybe (x :~: y)+testEqualityCompose = testEqualityComposeBare testEquality++instance (TestEquality f) => TestEquality (Compose f g) where+ testEquality = testEqualityCompose
submodules/parameterized-utils/src/Data/Parameterized/Context.hs view
@@ -33,50 +33,63 @@ module Data.Parameterized.Context ( #ifdef UNSAFE_OPS- module Data.Parameterized.Context.Unsafe+ module Data.Parameterized.Context.Unsafe #else- module Data.Parameterized.Context.Safe+ module Data.Parameterized.Context.Safe #endif- , singleton- , toVector- , pattern (:>)- , pattern Empty- , decompose- , Data.Parameterized.Context.null- , Data.Parameterized.Context.init- , Data.Parameterized.Context.last- , Data.Parameterized.Context.view- , forIndexM- , generateSome- , generateSomeM- , fromList- -- * Context extension and embedding utilities- , CtxEmbedding(..)- , ExtendContext(..)- , ExtendContext'(..)- , ApplyEmbedding(..)- , ApplyEmbedding'(..)- , identityEmbedding- , extendEmbeddingRightDiff- , extendEmbeddingRight- , extendEmbeddingBoth- , ctxeSize- , ctxeAssignment+ , singleton+ , toVector+ , pattern (:>)+ , pattern Empty+ , decompose+ , Data.Parameterized.Context.null+ , Data.Parameterized.Context.init+ , Data.Parameterized.Context.last+ , Data.Parameterized.Context.view+ , Data.Parameterized.Context.take+ , forIndexM+ , generateSome+ , generateSomeM+ , fromList+ , traverseAndCollect - -- * Static indexing and lenses for assignments- , Idx- , field- , natIndex- , natIndexProxy- -- * Currying and uncurrying for assignments- , CurryAssignment- , CurryAssignmentClass(..)- ) where+ -- * Context extension and embedding utilities+ , CtxEmbedding(..)+ , ExtendContext(..)+ , ExtendContext'(..)+ , ApplyEmbedding(..)+ , ApplyEmbedding'(..)+ , identityEmbedding+ , extendEmbeddingRightDiff+ , extendEmbeddingRight+ , extendEmbeddingBoth+ , appendEmbedding+ , ctxeSize+ , ctxeAssignment + -- * Static indexing and lenses for assignments+ , Idx+ , field+ , natIndex+ , natIndexProxy+ -- * Currying and uncurrying for assignments+ , CurryAssignment+ , CurryAssignmentClass(..)+ -- * Size and Index values+ , size1, size2, size3, size4, size5, size6+ , i1of2, i2of2+ , i1of3, i2of3, i3of3+ , i1of4, i2of4, i3of4, i4of4+ , i1of5, i2of5, i3of5, i4of5, i5of5+ , i1of6, i2of6, i3of6, i4of6, i5of6, i6of6+ ) where++import Control.Applicative (liftA2) import Control.Lens hiding (Index, (:>), Empty) import qualified Data.Vector as V import qualified Data.Vector.Mutable as MV import GHC.TypeLits (Nat, type (-))+import Data.Monoid ((<>)) import Data.Parameterized.Classes import Data.Parameterized.Some@@ -152,7 +165,7 @@ #endif ----------------------------------------------------------------------------------- | Views+-- Views -- | Return true if assignment is empty. null :: Assignment f ctx -> Bool@@ -181,10 +194,15 @@ view :: forall f ctx . Assignment f ctx -> AssignView f ctx view = viewAssign +take :: forall f ctx ctx'. Size ctx -> Size ctx' -> Assignment f (ctx <+> ctx') -> Assignment f ctx+take sz sz' asgn =+ let diff = appendDiff sz' in+ generate sz (\i -> asgn ! extendIndex' diff i)+ ----------------------------------------------------------------------------------- | Context embedding.+-- Context embedding. --- This datastructure contains a proof that the first context is+-- | This datastructure contains a proof that the first context is -- embeddable in the second. This is useful if we want to add extend -- an existing term under a larger context. @@ -248,6 +266,11 @@ extendEmbeddingRight :: CtxEmbedding ctx ctx' -> CtxEmbedding ctx (ctx' ::> tp) extendEmbeddingRight = extendEmbeddingRightDiff knownDiff +appendEmbedding :: Size ctx -> Size ctx' -> CtxEmbedding ctx (ctx <+> ctx')+appendEmbedding sz sz' = CtxEmbedding (addSize sz sz') (generate sz (extendIndex' diff))+ where+ diff = appendDiff sz'+ extendEmbeddingBoth :: forall ctx ctx' tp. CtxEmbedding ctx ctx' -> CtxEmbedding (ctx ::> tp) (ctx' ::> tp) extendEmbeddingBoth ctxe = updated & ctxeAssignment %~ flip extend (nextIndex (ctxe ^. ctxeSize)) where@@ -339,3 +362,102 @@ where go :: Assignment f ctx -> [Some f] -> Some (Assignment f) go prev [] = Some prev go prev (Some g:next) = (go $! prev `extend` g) next+++newtype Collector m w a = Collector { runCollector :: m w }+instance Functor (Collector m w) where+ fmap _ (Collector x) = Collector x+instance (Applicative m, Monoid w) => Applicative (Collector m w) where+ pure _ = Collector (pure mempty)+ Collector x <*> Collector y = Collector (liftA2 (<>) x y)++-- | Visit each of the elements in an @Assignment@ in order+-- from left to right and collect the results using the provided @Monoid@.+traverseAndCollect ::+ (Monoid w, Applicative m) =>+ (forall tp. Index ctx tp -> f tp -> m w) ->+ Assignment f ctx ->+ m w+traverseAndCollect f =+ runCollector . traverseWithIndex (\i x -> Collector (f i x))++--------------------------------------------------------------------------------+-- Size and Index values++size1 :: Size (EmptyCtx ::> a)+size1 = incSize zeroSize++size2 :: Size (EmptyCtx ::> a ::> b)+size2 = incSize size1++size3 :: Size (EmptyCtx ::> a ::> b ::> c)+size3 = incSize size2++size4 :: Size (EmptyCtx ::> a ::> b ::> c ::> d)+size4 = incSize size3++size5 :: Size (EmptyCtx ::> a ::> b ::> c ::> d ::> e)+size5 = incSize size4++size6 :: Size (EmptyCtx ::> a ::> b ::> c ::> d ::> e ::> f)+size6 = incSize size5++i1of2 :: Index (EmptyCtx ::> a ::> b) a+i1of2 = skipIndex baseIndex++i2of2 :: Index (EmptyCtx ::> a ::> b) b+i2of2 = nextIndex size1++i1of3 :: Index (EmptyCtx ::> a ::> b ::> c) a+i1of3 = skipIndex i1of2++i2of3 :: Index (EmptyCtx ::> a ::> b ::> c) b+i2of3 = skipIndex i2of2++i3of3 :: Index (EmptyCtx ::> a ::> b ::> c) c+i3of3 = nextIndex size2++i1of4 :: Index (EmptyCtx ::> a ::> b ::> c ::> d) a+i1of4 = skipIndex i1of3++i2of4 :: Index (EmptyCtx ::> a ::> b ::> c ::> d) b+i2of4 = skipIndex i2of3++i3of4 :: Index (EmptyCtx ::> a ::> b ::> c ::> d) c+i3of4 = skipIndex i3of3++i4of4 :: Index (EmptyCtx ::> a ::> b ::> c ::> d) d+i4of4 = nextIndex size3++i1of5 :: Index (EmptyCtx ::> a ::> b ::> c ::> d ::> e) a+i1of5 = skipIndex i1of4++i2of5 :: Index (EmptyCtx ::> a ::> b ::> c ::> d ::> e) b+i2of5 = skipIndex i2of4++i3of5 :: Index (EmptyCtx ::> a ::> b ::> c ::> d ::> e) c+i3of5 = skipIndex i3of4++i4of5 :: Index (EmptyCtx ::> a ::> b ::> c ::> d ::> e) d+i4of5 = skipIndex i4of4++i5of5 :: Index (EmptyCtx ::> a ::> b ::> c ::> d ::> e) e+i5of5 = nextIndex size4++i1of6 :: Index (EmptyCtx ::> a ::> b ::> c ::> d ::> e ::> f) a+i1of6 = skipIndex i1of5++i2of6 :: Index (EmptyCtx ::> a ::> b ::> c ::> d ::> e ::> f) b+i2of6 = skipIndex i2of5++i3of6 :: Index (EmptyCtx ::> a ::> b ::> c ::> d ::> e ::> f) c+i3of6 = skipIndex i3of5++i4of6 :: Index (EmptyCtx ::> a ::> b ::> c ::> d ::> e ::> f) d+i4of6 = skipIndex i4of5++i5of6 :: Index (EmptyCtx ::> a ::> b ::> c ::> d ::> e ::> f) e+i5of6 = skipIndex i5of5++i6of6 :: Index (EmptyCtx ::> a ::> b ::> c ::> d ::> e ::> f) f+i6of6 = nextIndex size5
submodules/parameterized-utils/src/Data/Parameterized/Context/Safe.hs view
@@ -60,6 +60,7 @@ , Diff , noDiff , extendRight+ , appendDiff , DiffView(..) , viewDiff , KnownDiff(..)@@ -120,7 +121,7 @@ ------------------------------------------------------------------------ -- Size --- | A indexed singleton type representing the size of a context.+-- | An indexed singleton type representing the size of a context. data Size (ctx :: Ctx k) where SizeZero :: Size 'EmptyCtx SizeSucc :: !(Size ctx) -> Size (ctx '::> tp)@@ -185,6 +186,10 @@ -- | Extend the difference to a sub-context of the right side. extendRight :: Diff l r -> Diff l (r '::> tp) extendRight diff = DiffThere diff++appendDiff :: Size r -> Diff l (l <+> r)+appendDiff SizeZero = DiffHere+appendDiff (SizeSucc sz) = DiffThere (appendDiff sz) composeDiff :: Diff a b -> Diff b c -> Diff a c composeDiff x DiffHere = x
submodules/parameterized-utils/src/Data/Parameterized/Context/Unsafe.hs view
@@ -32,6 +32,7 @@ , Diff , noDiff , extendRight+ , appendDiff , DiffView(..) , viewDiff , KnownDiff(..)@@ -97,10 +98,14 @@ -- Size -- | Represents the size of a context.-newtype Size (ctx :: Ctx k) = Size { sizeInt :: Int }+newtype Size (ctx :: Ctx k) = Size Int type role Size nominal +-- | Convert a context size to an 'Int'.+sizeInt :: Size ctx -> Int+sizeInt (Size n) = n+ -- | The size of an empty context. zeroSize :: Size 'EmptyCtx zeroSize = Size 0@@ -143,6 +148,8 @@ newtype Diff (l :: Ctx k) (r :: Ctx k) = Diff { _contextExtSize :: Int } +type role Diff nominal nominal+ -- | The identity difference. noDiff :: Diff l l noDiff = Diff 0@@ -150,6 +157,9 @@ -- | Extend the difference to a sub-context of the right side. extendRight :: Diff l r -> Diff l (r '::> tp) extendRight (Diff i) = Diff (i+1)++appendDiff :: Size r -> Diff l (l <+> r)+appendDiff (Size r) = Diff r instance Cat.Category Diff where id = Diff 0
+ submodules/parameterized-utils/src/Data/Parameterized/DecidableEq.hs view
@@ -0,0 +1,37 @@+{-|+Copyright : (c) Galois, Inc 2014-2018+Maintainer : Langston Barrett <langston@galois.com>++This defines a class @DecidableEq@, which represents decidable equality on a+type family.++This is different from GHC's @TestEquality@ in that it provides evidence+of non-equality. In fact, it is a superclass of @TestEquality@.+-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeInType #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE Safe #-}+module Data.Parameterized.DecidableEq+ ( DecidableEq(..)+ ) where++import Data.Void (Void)+import Data.Type.Equality ((:~:))++-- | Decidable equality.+class DecidableEq f where+ decEq :: f a -> f b -> Either (a :~: b) ((a :~: b) -> Void)++-- TODO: instances for sums, products of types with decidable equality++-- import Data.Type.Equality ((:~:), TestEquality(..))+-- instance (DecidableEq f) => TestEquality f where+-- testEquality a b =+-- case decEq a b of+-- Left prf -> Just prf+-- Right _ -> Nothing
submodules/parameterized-utils/src/Data/Parameterized/Map.hs view
@@ -45,13 +45,21 @@ , fromKeys , fromKeysM -- * Filter+ , filter+ , filterWithKey , filterGt , filterLt -- * Folds+ , foldlWithKey+ , foldlWithKey' , foldrWithKey+ , foldrWithKey'+ , foldMapWithKey -- * Traversal , map+ , mapWithKey , mapMaybe+ , mapMaybeWithKey , traverseWithKey , traverseWithKey_ -- * Complex interface.@@ -65,18 +73,18 @@ , Pair(..) ) where -import Control.Applicative hiding (empty)-import Control.Lens (Traversal', Lens')-import Control.Monad.Identity-import Data.List (intercalate, foldl')-import Data.Maybe ()-import Data.Kind(Type)+import Control.Applicative hiding (empty)+import Control.Lens (Traversal', Lens')+import Control.Monad.Identity+import Data.Kind (Type)+import Data.List (intercalate, foldl')+import Data.Monoid -import Data.Parameterized.Classes-import Data.Parameterized.Some-import Data.Parameterized.Pair ( Pair(..) )-import Data.Parameterized.TraversableF-import Data.Parameterized.Utils.BinTree+import Data.Parameterized.Classes+import Data.Parameterized.Some+import Data.Parameterized.Pair ( Pair(..) )+import Data.Parameterized.TraversableF+import Data.Parameterized.Utils.BinTree ( MaybeS(..) , fromMaybeS , Updated(..)@@ -91,9 +99,9 @@ import qualified Data.Parameterized.Utils.BinTree as Bin #if MIN_VERSION_base(4,8,0)-import Prelude hiding (lookup, map, traverse, null)+import Prelude hiding (filter, lookup, map, traverse, null) #else-import Prelude hiding (lookup, map, null)+import Prelude hiding (filter, lookup, map, null) #endif ------------------------------------------------------------------------@@ -106,7 +114,7 @@ ------------------------------------------------------------------------ -- MapF --- | A map from parameterized keys to values with the same paramter type.+-- | A map from parameterized keys to values with the same parameter type. data MapF (k :: v -> Type) (a :: v -> Type) where Bin :: {-# UNPACK #-} !Size -- Number of elements in tree.@@ -165,18 +173,30 @@ #-} #endif ++-- | Apply function to all elements in map.+mapWithKey+ :: (forall tp . ktp tp -> f tp -> g tp)+ -> MapF ktp f+ -> MapF ktp g+mapWithKey _ Tip = Tip+mapWithKey f (Bin sx kx x l r) = Bin sx kx (f kx x) (mapWithKey f l) (mapWithKey f r)+ -- | Modify elements in a map map :: (forall tp . f tp -> g tp) -> MapF ktp f -> MapF ktp g-map _ Tip = Tip-map f (Bin sx kx x l r) = Bin sx kx (f x) (map f l) (map f r)+map f = mapWithKey (\_ x -> f x) --- | Run partial map over elements.+-- | Map keys and elements and collect `Just` results.+mapMaybeWithKey :: (forall tp . k tp -> f tp -> Maybe (g tp)) -> MapF k f -> MapF k g+mapMaybeWithKey _ Tip = Tip+mapMaybeWithKey f (Bin _ k x l r) =+ case f k x of+ Just y -> Bin.link (Pair k y) (mapMaybeWithKey f l) (mapMaybeWithKey f r)+ Nothing -> Bin.merge (mapMaybeWithKey f l) (mapMaybeWithKey f r)++-- | Map elements and collect `Just` results. mapMaybe :: (forall tp . f tp -> Maybe (g tp)) -> MapF ktp f -> MapF ktp g-mapMaybe _ Tip = Tip-mapMaybe f (Bin _ k x l r) =- case f x of- Just y -> Bin.link (Pair k y) (mapMaybe f l) (mapMaybe f r)- Nothing -> Bin.merge (mapMaybe f l) (mapMaybe f r)+mapMaybe f = mapMaybeWithKey (\_ x -> f x) -- | Traverse elements in a map traverse :: Applicative m => (forall tp . f tp -> m (g tp)) -> MapF ktp f -> m (MapF ktp g)@@ -270,13 +290,43 @@ elems :: MapF k a -> [Some a] elems = foldrF (\e l -> Some e : l) [] --- | Perform a fold with the key also provided.+-- | Perform a left fold with the key also provided.+foldlWithKey :: (forall s . b -> k s -> a s -> b) -> b -> MapF k a -> b+foldlWithKey _ z Tip = z+foldlWithKey f z (Bin _ kx x l r) =+ let lz = foldlWithKey f z l+ kz = f lz kx x+ in foldlWithKey f kz r++-- | Perform a strict left fold with the key also provided.+foldlWithKey' :: (forall s . b -> k s -> a s -> b) -> b -> MapF k a -> b+foldlWithKey' _ z Tip = z+foldlWithKey' f z (Bin _ kx x l r) =+ let lz = foldlWithKey f z l+ kz = seq lz $ f lz kx x+ in seq kz $ foldlWithKey f kz r++-- | Perform a right fold with the key also provided. foldrWithKey :: (forall s . k s -> a s -> b -> b) -> b -> MapF k a -> b-foldrWithKey f z = go z- where- go z' Tip = z'- go z' (Bin _ kx x l r) = go (f kx x (go z' r)) l+foldrWithKey _ z Tip = z+foldrWithKey f z (Bin _ kx x l r) =+ let rz = foldrWithKey f z r+ kz = f kx x rz+ in foldrWithKey f kz l +-- | Perform a strict right fold with the key also provided.+foldrWithKey' :: (forall s . k s -> a s -> b -> b) -> b -> MapF k a -> b+foldrWithKey' _ z Tip = z+foldrWithKey' f z (Bin _ kx x l r) =+ let rz = foldrWithKey f z r+ kz = seq rz $ f kx x rz+ in seq kz $ foldrWithKey f kz l++-- | Fold the keys and values using the given monoid.+foldMapWithKey :: Monoid m => (forall s . k s -> a s -> m) -> MapF k a -> m+foldMapWithKey _ Tip = mempty+foldMapWithKey f (Bin _ kx x l r) = foldMapWithKey f l <> f kx x <> foldMapWithKey f r+ showMap :: (forall tp . ktp tp -> String) -> (forall tp . rtp tp -> String) -> MapF ktp rtp@@ -286,6 +336,17 @@ ------------------------------------------------------------------------ -- filter++-- | Return entries with values that satisfy predicate.+filter :: (forall tp . f tp -> Bool) -> MapF k f -> MapF k f+filter f = filterWithKey (\_ v -> f v)++-- | Return key-value pairs that satisfy predicate.+filterWithKey :: (forall tp . k tp -> f tp -> Bool) -> MapF k f -> MapF k f+filterWithKey _ Tip = Tip+filterWithKey f (Bin _ k x l r)+ | f k x = Bin.link (Pair k x) (filterWithKey f l) (filterWithKey f r)+ | otherwise = Bin.merge (filterWithKey f l) (filterWithKey f r) compareKeyPair :: OrdF k => k tp -> Pair k a -> Ordering compareKeyPair k = \(Pair x _) -> toOrdering (compareF k x)
submodules/parameterized-utils/src/Data/Parameterized/NatRepr.hs view
@@ -1,5 +1,5 @@ {-|-Copyright : (c) Galois, Inc 2014-2015+Copyright : (c) Galois, Inc 2014-2018 Maintainer : Joe Hendrix <jhendrix@galois.com> This defines a type 'NatRepr' for representing a type-level natural@@ -8,14 +8,17 @@ @n@. This can be used to help use type-level variables on code with data dependendent types. -The 'TestEquality' instance for 'NatRepr' is implemented using-'unsafeCoerce', as is the `isZeroNat` function. This should be-typesafe because we maintain the invariant that the integer value+The @TestEquality@ and @DecidableEq@ instances for 'NatRepr'+are implemented using 'unsafeCoerce', as is the `isZeroNat` function. This+should be typesafe because we maintain the invariant that the integer value contained in a NatRepr value matches its static type. -} {-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-}+{-# LANGUAGE EmptyCase #-}+{-# LANGUAGE EmptyDataDecls #-} {-# LANGUAGE ExplicitNamespaces #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}@@ -24,19 +27,25 @@ {-# LANGUAGE TypeOperators #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RoleAnnotations #-} {-# LANGUAGE PatternGuards #-} {-# LANGUAGE Trustworthy #-} {-# LANGUAGE TypeApplications #-} #if MIN_VERSION_base(4,9,0) {-# OPTIONS_GHC -fno-warn-redundant-constraints #-} #endif+#if __GLASGOW_HASKELL__ >= 805+{-# LANGUAGE NoStarIsType #-}+#endif module Data.Parameterized.NatRepr ( NatRepr , natValue+ , intValue , knownNat , withKnownNat , IsZeroNat(..) , isZeroNat+ , isZeroOrGT1 , NatComparison(..) , compareNat , decNat@@ -49,11 +58,18 @@ , withDivModNat , natMultiply , someNat+ , mkNatRepr , maxNat , natRec+ , natRecStrong+ , natRecBounded , natForEach+ , natFromZero , NatCases(..) , testNatCases+ -- * Strict order+ , lessThanIrreflexive+ , lessThanAsymmetric -- * Bitvector utilities , widthVal , minUnsigned@@ -66,6 +82,7 @@ , signedClamp -- * LeqProof , LeqProof(..)+ , decideLeq , testLeq , testStrictLeq , leqRefl@@ -98,6 +115,7 @@ , withSubMulDistribRight , mulCancelR , mul2Plus+ , lemmaMul -- * Re-exports typelists basics -- , NatK , type (+)@@ -107,20 +125,25 @@ , Equality.TestEquality(..) , (Equality.:~:)(..) , Data.Parameterized.Some.Some+ -- * Backdoor, no touchy+ , activateNatReprCoercionBackdoor_IPromiseIKnowWhatIAmDoing ) where -import Data.Bits ((.&.))+import Data.Bits ((.&.), bit) import Data.Hashable import Data.Proxy as Proxy import Data.Type.Equality as Equality-import GHC.TypeLits as TypeLits+import Data.Void as Void+import Numeric.Natural+import GHC.TypeNats as TypeNats import Unsafe.Coerce import Data.Parameterized.Classes+import Data.Parameterized.DecidableEq import Data.Parameterized.Some -maxInt :: Integer-maxInt = toInteger (maxBound :: Int)+maxInt :: Natural+maxInt = fromIntegral (maxBound :: Int) ------------------------------------------------------------------------ -- Nat@@ -129,15 +152,27 @@ -- -- This can be used for performing dynamic checks on a type-level natural -- numbers.-newtype NatRepr (n::Nat) = NatRepr { natValue :: Integer- -- ^ The underlying integer value of the number.+newtype NatRepr (n::Nat) = NatRepr { natValue :: Natural+ -- ^ The underlying natural value of the number. } deriving (Hashable) +type role NatRepr nominal++intValue :: NatRepr n -> Integer+intValue n = toInteger (natValue n)+{-# INLINE intValue #-}++-- | If you are not 110% sure what the consequences of using this are and+-- how to use it, don't.+activateNatReprCoercionBackdoor_IPromiseIKnowWhatIAmDoing :: ((Natural -> NatRepr n) -> a) -> a+activateNatReprCoercionBackdoor_IPromiseIKnowWhatIAmDoing k = k NatRepr+{-# INLINE activateNatReprCoercionBackdoor_IPromiseIKnowWhatIAmDoing #-}+ -- | Return the value of the nat representation. widthVal :: NatRepr n -> Int-widthVal (NatRepr i) | i < maxInt = fromInteger i- | otherwise = error "Width is too large."+widthVal (NatRepr i) | i <= maxInt = fromIntegral i+ | otherwise = error ("Width is too large: " ++ show i) instance Eq (NatRepr m) where _ == _ = True@@ -147,20 +182,26 @@ | m == n = Just (unsafeCoerce Refl) | otherwise = Nothing +instance DecidableEq NatRepr where+ decEq (NatRepr m) (NatRepr n)+ | m == n = Left $ unsafeCoerce Refl+ | otherwise = Right $+ \x -> seq x $ error "Impossible [DecidableEq on NatRepr]"+ -- | Result of comparing two numbers. data NatComparison m n where -- First number is less than second.- NatLT :: !(NatRepr y) -> NatComparison x (x+(y+1))+ NatLT :: x+1 <= x+(y+1) => !(NatRepr y) -> NatComparison x (x+(y+1)) NatEQ :: NatComparison x x -- First number is greater than second.- NatGT :: !(NatRepr y) -> NatComparison (x+(y+1)) x+ NatGT :: x+1 <= x+(y+1) => !(NatRepr y) -> NatComparison (x+(y+1)) x compareNat :: NatRepr m -> NatRepr n -> NatComparison m n compareNat m n = case compare (natValue m) (natValue n) of- LT -> unsafeCoerce $ NatLT (NatRepr (natValue n - natValue m - 1))- EQ -> unsafeCoerce $ NatEQ- GT -> unsafeCoerce $ NatGT (NatRepr (natValue m - natValue n - 1))+ LT -> unsafeCoerce (NatLT @0 @0) (NatRepr (natValue n - natValue m - 1))+ EQ -> unsafeCoerce NatEQ+ GT -> unsafeCoerce (NatGT @0 @0) (NatRepr (natValue m - natValue n - 1)) instance OrdF NatRepr where compareF x y =@@ -187,14 +228,12 @@ instance (KnownNat n) => KnownRepr NatRepr n where knownRepr = knownNat -{-# DEPRECATED withKnownNat "This function is potentially unsafe and is schedueled to be removed." #-} withKnownNat :: forall n r. NatRepr n -> (KnownNat n => r) -> r withKnownNat (NatRepr nVal) v = case someNatVal nVal of- Just (SomeNat (Proxy :: Proxy n')) ->- case unsafeCoerce (Refl :: 0 :~: 0) :: n :~: n' of+ SomeNat (Proxy :: Proxy n') ->+ case unsafeCoerce (Refl :: n :~: n) :: n :~: n' of Refl -> v- Nothing -> error "withKnownNat: inner value in NatRepr is not a natural" data IsZeroNat n where ZeroNat :: IsZeroNat 0@@ -204,11 +243,35 @@ isZeroNat (NatRepr 0) = unsafeCoerce ZeroNat isZeroNat (NatRepr _) = unsafeCoerce NonZeroNat +-- | Every nat is either zero or >= 1.+isZeroOrGT1 :: NatRepr n -> Either (n :~: 0) (LeqProof 1 n)+isZeroOrGT1 n =+ case isZeroNat n of+ ZeroNat -> Left Refl+ NonZeroNat -> Right $+ -- We have n = m + 1 for some m.+ let+ -- | x <= x + 1+ leqSucc:: forall x. LeqProof x (x+1)+ leqSucc = leqAdd2 (LeqProof :: LeqProof x x) (LeqProof :: LeqProof 0 1)+ leqPlus :: forall f x y. ((x + 1) ~ y) => f x -> LeqProof 1 y+ leqPlus fx =+ case (plusComm fx (knownNat @1) :: x + 1 :~: 1 + x) of { Refl ->+ case (plusMinusCancel (knownNat @1) fx :: 1+x-x :~: 1) of { Refl ->+ case (LeqProof :: LeqProof (x+1) y) of { LeqProof ->+ case (LeqProof :: LeqProof (1+x-x) (y-x)) of { LeqProof ->+ leqTrans (LeqProof :: LeqProof 1 (y-x))+ (leqSub (LeqProof :: LeqProof y y)+ (leqTrans (leqSucc :: LeqProof x (x+1))+ (LeqProof) :: LeqProof x y) :: LeqProof (y - x) y)+ }}}}+ in leqPlus (predNat n)+ -- | Decrement a @NatRepr@ decNat :: (1 <= n) => NatRepr n -> NatRepr (n-1) decNat (NatRepr i) = NatRepr (i-1) --- | Get the predicessor of a nat+-- | Get the predecessor of a nat predNat :: NatRepr (n+1) -> NatRepr n predNat (NatRepr i) = NatRepr (i-1) @@ -254,15 +317,15 @@ -- | Return maximum unsigned value for bitvector with given width. maxUnsigned :: NatRepr w -> Integer-maxUnsigned w = 2^(natValue w) - 1+maxUnsigned w = bit (widthVal w) - 1 -- | Return minimum value for bitvector in 2s complement with given width. minSigned :: (1 <= w) => NatRepr w -> Integer-minSigned w = negate (2^(natValue w - 1))+minSigned w = negate (bit (widthVal w - 1)) -- | Return maximum value for bitvector in 2s complement with given width. maxSigned :: (1 <= w) => NatRepr w -> Integer-maxSigned w = 2^(natValue w - 1) - 1+maxSigned w = bit (widthVal w - 1) - 1 -- | @toUnsigned w i@ maps @i@ to a @i `mod` 2^w@. toUnsigned :: NatRepr w -> Integer -> Integer@@ -272,7 +335,7 @@ -- signed number in two's complement notation and returns that value. toSigned :: (1 <= w) => NatRepr w -> Integer -> Integer toSigned w i0- | i > maxSigned w = i - 2^(natValue w)+ | i > maxSigned w = i - bit (widthVal w) | otherwise = i where i = i0 .&. maxUnsigned w @@ -295,10 +358,16 @@ ------------------------------------------------------------------------ -- Some NatRepr -someNat :: Integer -> Maybe (Some NatRepr)-someNat n | 0 <= n && n <= toInteger maxInt = Just (Some (NatRepr (fromInteger n)))- | otherwise = Nothing+-- | Turn an @Integral@ value into a @NatRepr@. Returns @Nothing@+-- if the given value is negative.+someNat :: Integral a => a -> Maybe (Some NatRepr)+someNat x | x >= 0 = Just . Some . NatRepr $! fromIntegral x+someNat _ = Nothing +-- | Turn a @Natural@ into the corresponding @NatRepr@+mkNatRepr :: Natural -> Some NatRepr+mkNatRepr n = Some (NatRepr n)+ -- | Return the maximum of two nat representations. maxNat :: NatRepr m -> NatRepr n -> Some NatRepr maxNat x y@@ -345,8 +414,6 @@ case unsafeCoerce (Refl :: 0 :~: 0) of (Refl :: (((n * p) - (m * p)) :~: ((n - m) * p)) ) -> f -- ------------------------------------------------------------------------ -- LeqProof @@ -355,6 +422,13 @@ data LeqProof m n where LeqProof :: (m <= n) => LeqProof m n +-- | (<=) is a decidable relation on nats.+decideLeq :: NatRepr a -> NatRepr b -> Either (LeqProof a b) ((LeqProof a b) -> Void)+decideLeq (NatRepr m) (NatRepr n)+ | m <= n = Left $ unsafeCoerce (LeqProof :: LeqProof 0 0)+ | otherwise = Right $+ \x -> seq x $ error "Impossible [decidable <= on NatRepr]"+ testStrictLeq :: forall m n . (m <= n) => NatRepr m@@ -384,6 +458,31 @@ GT -> NatCaseGT (unsafeCoerce (LeqProof :: LeqProof 0 0)) {-# NOINLINE testNatCases #-} +-- | The strict order (<), defined by n < m <-> n + 1 <= m, is irreflexive.+lessThanIrreflexive :: forall f (a :: Nat). f a -> LeqProof (1 + a) a -> Void+lessThanIrreflexive a prf =+ let prf1 :: LeqProof (1 + a - a) (a - a)+ prf1 = leqSub2 prf (LeqProof :: LeqProof a a)+ prf2 :: 1 + a - a :~: 1+ prf2 = plusMinusCancel (knownNat @1) a+ prf3 :: a - a :~: 0+ prf3 = plusMinusCancel (knownNat @0) a+ prf4 :: LeqProof 1 0+ prf4 = case prf2 of Refl -> case prf3 of { Refl -> prf1 }+ in case prf4 of {}++-- | The strict order on the naturals is irreflexive.+lessThanAsymmetric :: forall m f n+ . LeqProof (n+1) m+ -> LeqProof (m+1) n+ -> f n+ -> Void+lessThanAsymmetric nLTm mLTn n =+ case plusComm n (knownNat @1) :: n + 1 :~: 1 + n of { Refl ->+ case leqAdd (LeqProof :: LeqProof m m) (knownNat @1) :: LeqProof m (m+1) of+ LeqProof -> lessThanIrreflexive n $ leqTrans (leqTrans nLTm LeqProof) mLTn+ }+ -- | @x `testLeq` y@ checks whether @x@ is less than or equal to @y@. testLeq :: forall m n . NatRepr m -> NatRepr n -> Maybe (LeqProof m n) testLeq (NatRepr m) (NatRepr n)@@ -395,7 +494,6 @@ leqRefl :: forall f n . f n -> LeqProof n n leqRefl _ = LeqProof - -- | Apply transitivity to LeqProof leqTrans :: LeqProof m n -> LeqProof n p -> LeqProof m p leqTrans LeqProof LeqProof = unsafeCoerce (LeqProof :: LeqProof 0 0)@@ -509,19 +607,84 @@ -> [a] natForEach l h f = natForEach' l h (\LeqProof LeqProof -> f) +-- | Apply a function to each element in a range starting at zero;+-- return the list of values obtained.+natFromZero :: forall h a+ . NatRepr h+ -> (forall n. (n <= h) => NatRepr n -> a)+ -> [a]+natFromZero = natForEach (knownNat @0)+ -- | Recursor for natural numbeers.-natRec :: forall m f- . NatRepr m- -> f 0+natRec :: forall p f+ . NatRepr p+ -> f 0 {- ^ base case -} -> (forall n. NatRepr n -> f n -> f (n + 1))- -> f m-natRec n f0 ih = go n- where- go :: forall n'. NatRepr n' -> f n'- go n' = case isZeroNat n' of- ZeroNat -> f0- NonZeroNat -> let n'' = predNat n' in ih n'' (go n'')+ -> f p+natRec n base ind =+ case isZeroNat n of+ ZeroNat -> base+ NonZeroNat -> let n' = predNat n+ in ind n' (natRec n' base ind) +-- | Strong induction variant of the recursor.+natRecStrong :: forall p f+ . NatRepr p+ -> f 0 {- ^ base case -}+ -> (forall n.+ NatRepr n ->+ (forall m. (m <= n) => NatRepr m -> f m) ->+ f (n + 1)) {- ^ inductive step -}+ -> f p+natRecStrong p base ind = natRecStrong' base ind p+ where -- We can't use use "flip" or some other basic combinator+ -- because type variables can't be instantiated to contain "forall"s.+ natRecStrong' :: forall p' f'+ . f' 0 {- ^ base case -}+ -> (forall n.+ NatRepr n ->+ (forall m. (m <= n) => NatRepr m -> f' m) ->+ f' (n + 1)) {- ^ inductive step -}+ -> NatRepr p'+ -> f' p'+ natRecStrong' base' ind' n =+ case isZeroNat n of+ ZeroNat -> base'+ NonZeroNat -> ind' (predNat n) (natRecStrong' base' ind')++-- | Bounded recursor for natural numbers.+--+-- If you can prove:+-- - Base case: f 0+-- - Inductive step: if n <= h and (f n) then (f (n + 1))+-- You can conclude: for all n <= h, (f (n + 1)).+natRecBounded :: forall m h f. (m <= h)+ => NatRepr m+ -> NatRepr h+ -> f 0+ -> (forall n. (n <= h) => NatRepr n -> f n -> f (n + 1))+ -> f (m + 1)+natRecBounded m h base indH =+ case isZeroOrGT1 m of+ Left Refl -> indH (knownNat @0) base+ Right LeqProof ->+ case decideLeq m h of+ Left LeqProof {- :: m <= h -} ->+ let -- Since m is non-zero, it is n + 1 for some n.+ lemma :: LeqProof (m-1) h+ lemma = leqSub (LeqProof :: LeqProof m h) (LeqProof :: LeqProof 1 m)+ in indH m $+ case lemma of { LeqProof ->+ case minusPlusCancel m (knownNat @1) of { Refl ->+ natRecBounded @(m - 1) @h @f (predNat m) h base indH+ }}+ Right f {- :: (m <= h) -> Void -} ->+ absurd $ f (LeqProof :: LeqProof m h)+ mulCancelR :: (1 <= c, (n1 * c) ~ (n2 * c)) => f1 n1 -> f2 n2 -> f3 c -> (n1 :~: n2) mulCancelR _ _ _ = unsafeCoerce Refl++-- | Used in @Vector@+lemmaMul :: (1 <= n) => p w -> q n -> (w + (n-1) * w) :~: (n * w)+lemmaMul = unsafeCoerce Refl
submodules/parameterized-utils/src/Data/Parameterized/Nonce.hs view
@@ -16,6 +16,7 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE EmptyDataDecls #-}+{-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE PolyKinds #-}@@ -29,6 +30,7 @@ ( -- * NonceGenerator NonceGenerator , freshNonce+ , countNoncesGenerated , Nonce , indexValue -- * Accessing a nonce generator@@ -53,7 +55,7 @@ import Data.Parameterized.Classes import Data.Parameterized.Some -#if MIN_VERSION_base(4,9,0)+#if MIN_VERSION_base(4,9,0) && __GLASGOW_HASKELL__ < 805 import Data.Kind #endif @@ -61,47 +63,53 @@ -- -- The first type parameter @m@ is the monad used for generating names, and -- the second parameter @s@ is used for the counter.-data NonceGenerator (m :: * -> *) (s :: *) = NonceGenerator {+data NonceGenerator (m :: * -> *) (s :: *) where+ STNG :: !(STRef t Word64) -> NonceGenerator (ST t) s+ IONG :: !(IORef Word64) -> NonceGenerator IO s+ #if MIN_VERSION_base(4,9,0) -- We have to make the k explicit in GHC 8.0 to avoid a warning.- freshNonce :: forall k (tp :: k) . m (Nonce s tp)+freshNonce :: forall m s k (tp :: k) . NonceGenerator m s -> m (Nonce s tp) #else- freshNonce :: forall (tp :: k) . m (Nonce s tp)+freshNonce :: forall m s (tp :: k) . NonceGenerator m s -> m (Nonce s tp) #endif- }+freshNonce (IONG r) =+ atomicModifyIORef' r $ \n -> (n+1, Nonce n)+freshNonce (STNG r) = do+ i <- readSTRef r+ writeSTRef r $! i+1+ return $ Nonce i+ -- (Weirdly, there's no atomicModifySTRef'. Yes, only the IO monad+ -- does concurrency, but the ST monad is part of the IO monad via+ -- stToIO, so there's no guarantee that ST code won't be run in+ -- multiple threads.) +{-# INLINE freshNonce #-}+ -- Inlining is particularly necessary since there's no @Monad m@+ -- constraint on 'freshNonce', so SPECIALIZE doesn't work on it. In+ -- this case, though, we get specialization for free from inlining.+ -- For instance, a @NonceGenerator IO s@ must be an @IONG@, so the+ -- simplifier eliminates the STNG branch.++-- | The number of nonces generated so far by this generator. Only+-- really useful for profiling.+countNoncesGenerated :: NonceGenerator m s -> m Integer+countNoncesGenerated (IONG r) = toInteger <$> readIORef r+countNoncesGenerated (STNG r) = toInteger <$> readSTRef r+ -- | Create a new counter. withGlobalSTNonceGenerator :: (forall t . NonceGenerator (ST t) t -> ST t r) -> r withGlobalSTNonceGenerator f = runST $ do r <- newSTRef (toEnum 0)- f $! NonceGenerator {- freshNonce = do- i <- readSTRef r- writeSTRef r $! succ i- return $! Nonce i- }+ f $! STNG r -- | Create a new nonce generator in the ST monad. newSTNonceGenerator :: ST t (Some (NonceGenerator (ST t)))-newSTNonceGenerator = g <$> newSTRef (toEnum 0)- where g r = Some $!- NonceGenerator {- freshNonce = do- i <- readSTRef r- writeSTRef r $! succ i- return $! Nonce i- }+newSTNonceGenerator = Some . STNG <$> newSTRef (toEnum 0) -- | Create a new nonce generator in the ST monad. newIONonceGenerator :: IO (Some (NonceGenerator IO))-newIONonceGenerator = g <$> newIORef (toEnum 0)- where g r = Some $!- NonceGenerator {- freshNonce = do- i <- readIORef r- writeIORef r $! succ i- return $! Nonce i- }+newIONonceGenerator = Some . IONG <$> newIORef (toEnum 0) -- | Run a ST computation with a new nonce generator in the ST monad. withSTNonceGenerator :: (forall s . NonceGenerator (ST t) s -> (ST t) r) -> ST t r@@ -152,7 +160,4 @@ -- | A nonce generator that uses a globally-defined counter. globalNonceGenerator :: NonceGenerator IO GlobalNonceGenerator-globalNonceGenerator =- NonceGenerator- { freshNonce = Nonce <$> atomicModifyIORef' globalNonceIORef (\n -> (n+1, n))- }+globalNonceGenerator = IONG globalNonceIORef
+ submodules/parameterized-utils/src/Data/Parameterized/Peano.hs view
@@ -0,0 +1,285 @@+{-|++This defines a type 'Peano' and 'PeanoRepr' for representing a+type-level natural at runtime. These type-level numbers are defined+inductively instead of using GHC.TypeLits.++As a result, type-level computation defined recursively over these+numbers works more smoothly. (For example, see the type-level+function Repeatn below.)++Note: as in NatRepr, the runtime representation of these type-level+natural numbers is an Int.++-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE EmptyCase #-}+{-# LANGUAGE ExplicitNamespaces #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RoleAnnotations #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}++#if MIN_VERSION_base(4,9,0)+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}+#endif+#if __GLASGOW_HASKELL__ >= 805+{-# LANGUAGE NoStarIsType #-}+#endif+module Data.Parameterized.Peano+ ( Peano+ , Z , S+ , Plus, Minus, Mul, Le, Lt, Gt, Ge, Max, Min, Repeat+ , plusP, minusP, mulP, maxP, minP, repeatP+ , zeroP, succP, predP++ , KnownPeano+ , withKnownPeano++ , PeanoRepr, peanoValue+ , PeanoView(..), peanoView+ , viewRepr++ , somePeano+ , mkPeanoRepr+ , maxPeano+ , minPeano++ -- * Re-exports+ , TestEquality(..)+ , (:~:)(..)+ , Data.Parameterized.Some.Some++ ) where++import Data.Parameterized.Classes+import Data.Parameterized.DecidableEq+import Data.Parameterized.Some++import Data.Hashable+import Data.Constraint+import Data.Word++import Unsafe.Coerce(unsafeCoerce)++------------------------------------------------------------------------+-- ** Peano - a unary representation of natural numbers++data Peano = Z | S Peano+-- | Peano zero+type Z = 'Z+-- | Peano successor+type S = 'S++-- Peano numbers are more about *counting* than arithmetic.+-- They are most useful as iteration arguments and list indices+-- However, for completeness, we define a few standard+-- operations.++type family Plus (a :: Peano) (b :: Peano) :: Peano where+ Plus Z b = b+ Plus (S a) b = S (Plus a b)++type family Minus (a :: Peano) (b :: Peano) :: Peano where+ Minus Z b = Z+ Minus (S a) (S b) = Minus a b+ Minus a Z = a++type family Mul (a :: Peano) (b :: Peano) :: Peano where+ Mul Z b = Z+ Mul (S a) b = Plus a (Mul a b)++type family Le (a :: Peano) (b :: Peano) :: Bool where+ Le a a = 'True+ Le Z b = 'True+ Le a Z = 'False+ Le (S a) (S b) = Le a b++type family Lt (a :: Peano) (b :: Peano) :: Bool where+ Lt a b = Le (S a) b++type family Gt (a :: Peano) (b :: Peano) :: Bool where+ Gt a b = Lt b a++type family Ge (a :: Peano) (b :: Peano) :: Bool where+ Ge a b = Le b a++type family Max (a :: Peano) (b :: Peano) :: Peano where+ Max Z b = b+ Max a Z = a+ Max (S a) (S b) = S (Max a b)++type family Min (a :: Peano) (b :: Peano) :: Peano where+ Min Z b = Z+ Min a Z = Z+ Min (S a) (S b) = S (Min a b)++-- Apply a constructor 'f' n-times to an argument 's'+type family Repeat (m :: Peano) (f :: k -> k) (s :: k) :: k where+ Repeat Z f s = s+ Repeat (S m) f s = f (Repeat m f s)+++------------------------------------------------------------------------+-- ** Run time representation of Peano numbers++-- | The run time value, stored as an Word64+-- As these are unary numbers, we don't worry about overflow.+newtype PeanoRepr (n :: Peano) =+ PeanoRepr { peanoValue :: Word64 }++-- n is Phantom in the definition, but we don't want to allow coerce+type role PeanoRepr nominal++----------------------------------------------------------++-- | Because we have optimized the runtime representation,+-- we need to have a "view" that decomposes the representation+-- into the standard form.+data PeanoView (n :: Peano) where+ ZRepr :: PeanoView Z+ SRepr :: PeanoRepr n -> PeanoView (S n)++-- | Test whether a number is Zero or Successor+peanoView :: PeanoRepr n -> PeanoView n+peanoView (PeanoRepr i) =+ if i == 0 then unsafeCoerce ZRepr else unsafeCoerce (SRepr (PeanoRepr (i-1)))++-- | convert the view back to the runtime representation+viewRepr :: PeanoView n -> PeanoRepr n+viewRepr ZRepr = PeanoRepr 0+viewRepr (SRepr n) = PeanoRepr (peanoValue n + 1)++----------------------------------------------------------++instance Hashable (PeanoRepr n) where+ hashWithSalt i (PeanoRepr x) = hashWithSalt i x++instance Eq (PeanoRepr m) where+ _ == _ = True++instance TestEquality PeanoRepr where+ testEquality (PeanoRepr m) (PeanoRepr n)+ | m == n = Just (unsafeCoerce Refl)+ | otherwise = Nothing++instance DecidableEq PeanoRepr where+ decEq (PeanoRepr m) (PeanoRepr n)+ | m == n = Left $ unsafeCoerce Refl+ | otherwise = Right $+ \x -> seq x $ error "Impossible [DecidableEq on PeanoRepr]"++instance OrdF PeanoRepr where+ compareF (PeanoRepr m) (PeanoRepr n)+ | m < n = unsafeCoerce LTF+ | m == n = unsafeCoerce EQF+ | otherwise = unsafeCoerce GTF++instance PolyEq (PeanoRepr m) (PeanoRepr n) where+ polyEqF x y = (\Refl -> Refl) <$> testEquality x y++-- Display as digits, not in unary+instance Show (PeanoRepr p) where+ show p = show (peanoValue p)++instance ShowF PeanoRepr++instance HashableF PeanoRepr where+ hashWithSaltF = hashWithSalt++----------------------------------------------------------+-- * Implicit runtime Peano numbers++type KnownPeano = KnownRepr PeanoRepr++instance KnownRepr PeanoRepr Z where+ knownRepr = viewRepr ZRepr+instance (KnownRepr PeanoRepr n) => KnownRepr PeanoRepr (S n) where+ knownRepr = viewRepr (SRepr knownRepr)++newtype DI a = Don'tInstantiate (KnownPeano a => Dict (KnownPeano a))++peanoInstance :: forall a . PeanoRepr a -> Dict (KnownPeano a)+peanoInstance s = with_sing_i Dict+ where+ with_sing_i :: (KnownPeano a => Dict (KnownPeano a)) -> Dict (KnownPeano a)+ with_sing_i si = unsafeCoerce (Don'tInstantiate si) s++-- | convert an explicit number to an implicit number+withKnownPeano :: forall n r. PeanoRepr n -> (KnownPeano n => r) -> r+withKnownPeano si r = case peanoInstance si of+ Dict -> r++----------------------------------------------------------+-- * Operations on runtime numbers++-- | zero+zeroP :: PeanoRepr Z+zeroP = PeanoRepr 0++-- | Successor, Increment+succP :: PeanoRepr n -> PeanoRepr (S n)+succP (PeanoRepr i) = PeanoRepr (i+1)++-- | Get the predecessor (decrement)+predP :: PeanoRepr (S n) -> PeanoRepr n+predP (PeanoRepr i) = PeanoRepr (i-1)+++plusP :: PeanoRepr a -> PeanoRepr b -> PeanoRepr (Plus a b)+plusP (PeanoRepr a) (PeanoRepr b) = PeanoRepr (a + b)++minusP :: PeanoRepr a -> PeanoRepr b -> PeanoRepr (Minus a b)+minusP (PeanoRepr a) (PeanoRepr b) = PeanoRepr (a - b)++mulP :: PeanoRepr a -> PeanoRepr b -> PeanoRepr (Mul a b)+mulP (PeanoRepr a) (PeanoRepr b) = PeanoRepr (a * b)++maxP :: PeanoRepr a -> PeanoRepr b -> PeanoRepr (Max a b)+maxP (PeanoRepr a) (PeanoRepr b) = PeanoRepr (max a b)++minP :: PeanoRepr a -> PeanoRepr b -> PeanoRepr (Min a b)+minP (PeanoRepr a) (PeanoRepr b) = PeanoRepr (min a b)++repeatP :: PeanoRepr m -> (forall a. repr a -> repr (f a)) -> repr s -> repr (Repeat m f s)+repeatP n f s = case peanoView n of+ ZRepr -> s+ SRepr m -> f (repeatP m f s)++------------------------------------------------------------------------+-- * Some PeanoRepr++-- | Convert a Word64 to a PeanoRepr+mkPeanoRepr :: Word64 -> Some PeanoRepr+mkPeanoRepr n = Some (PeanoRepr n)++-- | Turn an @Integral@ value into a @PeanoRepr@. Returns @Nothing@+-- if the given value is negative.+somePeano :: Integral a => a -> Maybe (Some PeanoRepr)+somePeano x | x >= 0 = Just . Some . PeanoRepr $! fromIntegral x+somePeano _ = Nothing++-- | Return the maximum of two representations.+maxPeano :: PeanoRepr m -> PeanoRepr n -> Some PeanoRepr+maxPeano x y+ | peanoValue x >= peanoValue y = Some x+ | otherwise = Some y++-- | Return the minimum of two representations.+minPeano :: PeanoRepr m -> PeanoRepr n -> Some PeanoRepr+minPeano x y+ | peanoValue y >= peanoValue x = Some x+ | otherwise = Some y++------------------------------------------------------------------------+-- LocalWords: PeanoRepr withKnownPeano runtime Peano unary
+ submodules/parameterized-utils/src/Data/Parameterized/TestEquality.hs view
@@ -0,0 +1,36 @@+{-|+Copyright : (c) Galois, Inc 2014-2018+Maintainer : Langston Barrett <langston@galois.com++Utilities for working with "Data.Type.TestEquality".++NB: This module contains an orphan instance.+-}++{-# LANGUAGE GADTs #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeOperators #-}++import Data.Functor.Compose+import Data.Type.Equality++testEqualityComposeBare :: forall f g x y.+ (forall w z. f w -> f z -> Maybe (w :~: z))+ -> Compose f g x+ -> Compose f g y+ -> Maybe (x :~: y)+testEqualityComposeBare testEquality_ (Compose x) (Compose y) =+ case (testEquality_ x y :: Maybe (g x :~: g y)) of+ Just Refl -> Just (Refl :: x :~: y)+ Nothing -> Nothing++testEqualityCompose :: forall f g x y. (TestEquality f)+ => Compose f g x+ -> Compose f g y+ -> Maybe (x :~: y)+testEqualityCompose = testEqualityComposeBare testEquality++-- | The deduction (via generativity) that if @g x :~: g y@ then @x :~: y@.+instance (TestEquality f) => TestEquality (Compose f g) where+ testEquality = testEqualityCompose
submodules/parameterized-utils/src/Data/Parameterized/TraversableF.hs view
@@ -7,8 +7,10 @@ -- This module declares classes for working with structures that accept -- a single parametric type parameter. ------------------------------------------------------------------------+{-# LANGUAGE InstanceSigs #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE Trustworthy #-} module Data.Parameterized.TraversableF ( FunctorF(..)@@ -24,10 +26,13 @@ import Control.Applicative import Control.Monad.Identity import Data.Coerce+import Data.Functor.Compose (Compose(..)) import Data.Monoid import GHC.Exts (build) --- | A parameterized type that is a function on all instances.+import Data.Parameterized.TraversableFC++-- | A parameterized type that is a functor on all instances. class FunctorF m where fmapF :: (forall x . f x -> g x) -> m f -> m g @@ -37,7 +42,7 @@ ------------------------------------------------------------------------ -- FoldableF --- | This is a coercision used to avoid overhead associated+-- | This is a coercion used to avoid overhead associated -- with function composition. (#.) :: Coercible b c => (b -> c) -> (a -> b) -> (a -> c) (#.) _f = coerce@@ -112,5 +117,30 @@ -- | Map each element of a structure to an action, evaluate -- these actions from left to right, and ignore the results.-traverseF_ :: (FoldableF t, Applicative f) => (forall s . e s -> f ()) -> t e -> f ()+traverseF_ :: (FoldableF t, Applicative f) => (forall s . e s -> f a) -> t e -> f () traverseF_ f = foldrF (\e r -> f e *> r) (pure ())++------------------------------------------------------------------------+-- TraversableF (Compose s t)++instance ( FunctorF (s :: (k -> *) -> *)+ , FunctorFC (t :: (l -> *) -> (k -> *))+ ) =>+ FunctorF (Compose s t) where+ fmapF f (Compose v) = Compose $ fmapF (fmapFC f) v++instance ( TraversableF (s :: (k -> *) -> *)+ , TraversableFC (t :: (l -> *) -> (k -> *))+ ) =>+ FoldableF (Compose s t) where+ foldMapF = foldMapFDefault++-- | Traverse twice over: go under the @t@, under the @s@ and lift @m@ out.+instance ( TraversableF (s :: (k -> *) -> *)+ , TraversableFC (t :: (l -> *) -> (k -> *))+ ) =>+ TraversableF (Compose s t) where+ traverseF :: forall (f :: l -> *) (g :: l -> *) m. (Applicative m) =>+ (forall (u :: l). f u -> m (g u))+ -> Compose s t f -> m (Compose s t g)+ traverseF f (Compose v) = Compose <$> traverseF (traverseFC f) v
submodules/parameterized-utils/src/Data/Parameterized/TraversableFC.hs view
@@ -150,12 +150,12 @@ -- | Map each element of a structure to an action, evaluate -- these actions from left to right, and ignore the results.-traverseFC_ :: (FoldableFC t, Applicative m) => (forall x. f x -> m ()) -> (forall x. t f x -> m ())+traverseFC_ :: (FoldableFC t, Applicative m) => (forall x. f x -> m a) -> (forall x. t f x -> m ()) traverseFC_ f = foldrFC (\e r -> f e *> r) (pure ()) {-# INLINE traverseFC_ #-} -- | Map each element of a structure to an action, evaluate -- these actions from left to right, and ignore the results.-forMFC_ :: (FoldableFC t, Applicative m) => t f c -> (forall x. f x -> m ()) -> m ()+forMFC_ :: (FoldableFC t, Applicative m) => t f c -> (forall x. f x -> m a) -> m () forMFC_ v f = traverseFC_ f v {-# INLINE forMFC_ #-}
+ submodules/parameterized-utils/src/Data/Parameterized/Utils/Endian.hs view
@@ -0,0 +1,3 @@+module Data.Parameterized.Utils.Endian where++data Endian = LittleEndian | BigEndian deriving (Eq,Show,Ord)
+ submodules/parameterized-utils/src/Data/Parameterized/Vector.hs view
@@ -0,0 +1,507 @@+{-# Language GADTs, DataKinds, TypeOperators, BangPatterns #-}+{-# Language PatternGuards #-}+{-# Language TypeApplications, ScopedTypeVariables #-}+{-# Language Rank2Types, RoleAnnotations #-}+{-# Language CPP #-}+#if __GLASGOW_HASKELL__ >= 805+{-# Language NoStarIsType #-}+#endif+-- | A vector fixed-size vector of typed elements.+module Data.Parameterized.Vector+ ( Vector+ -- * Lists+ , fromList+ , toList++ -- * Length+ , length+ , nonEmpty+ , lengthInt++ -- * Indexing+ , elemAt+ , elemAtMaybe+ , elemAtUnsafe++ -- * Update+ , insertAt+ , insertAtMaybe++ -- * Sub sequences+ , uncons+ , slice+ , Data.Parameterized.Vector.take++ -- * Zipping+ , zipWith+ , zipWithM+ , zipWithM_+ , interleave++ -- * Reorder+ , shuffle+ , reverse+ , rotateL+ , rotateR+ , shiftL+ , shiftR++ -- * Construction+ , singleton+ , cons+ , snoc+ , generate+ , generateM++ -- * Splitting and joining+ -- ** General+ , joinWithM+ , joinWith+ , splitWith+ , splitWithA++ -- ** Vectors+ , split+ , join+ , append++ ) where++import qualified Data.Vector as Vector+import Data.Functor.Compose+import Data.Coerce+import Data.Vector.Mutable (MVector)+import qualified Data.Vector.Mutable as MVector+import Control.Monad.ST+import Data.Functor.Identity+import Data.Parameterized.NatRepr+import Data.Proxy+import Prelude hiding (length,reverse,zipWith)+import Numeric.Natural++import Data.Parameterized.Utils.Endian++-- | Fixed-size non-empty vectors.+data Vector n a where+ Vector :: (1 <= n) => !(Vector.Vector a) -> Vector n a++type role Vector nominal representational++instance Eq a => Eq (Vector n a) where+ (Vector x) == (Vector y) = x == y++instance Show a => Show (Vector n a) where+ show (Vector x) = show x++-- | Get the elements of the vector as a list, lowest index first.+toList :: Vector n a -> [a]+toList (Vector v) = Vector.toList v+{-# Inline toList #-}++-- | Length of the vector.+-- @O(1)@+length :: Vector n a -> NatRepr n+length (Vector xs) =+ activateNatReprCoercionBackdoor_IPromiseIKnowWhatIAmDoing $ \mk ->+ mk (fromIntegral (Vector.length xs) :: Natural)+{-# INLINE length #-}++-- | The length of the vector as an "Int".+lengthInt :: Vector n a -> Int+lengthInt (Vector xs) = Vector.length xs+{-# Inline lengthInt #-}++elemAt :: ((i+1) <= n) => NatRepr i -> Vector n a -> a+elemAt n (Vector xs) = xs Vector.! widthVal n++-- | Get the element at the given index.+-- @O(1)@+elemAtMaybe :: Int -> Vector n a -> Maybe a+elemAtMaybe n (Vector xs) = xs Vector.!? n+{-# INLINE elemAt #-}++-- | Get the element at the given index.+-- Raises an exception if the element is not in the vector's domain.+-- @O(1)@+elemAtUnsafe :: Int -> Vector n a -> a+elemAtUnsafe n (Vector xs) = xs Vector.! n+{-# INLINE elemAtUnsafe #-}+++-- | Insert an element at the given index.+-- @O(n)@.+insertAt :: ((i + 1) <= n) => NatRepr i -> a -> Vector n a -> Vector n a+insertAt n a (Vector xs) = Vector (Vector.unsafeUpd xs [(widthVal n,a)])++-- | Insert an element at the given index.+-- Return 'Nothing' if the element is outside the vector bounds.+-- @O(n)@.+insertAtMaybe :: Int -> a -> Vector n a -> Maybe (Vector n a)+insertAtMaybe n a (Vector xs)+ | 0 <= n && n < Vector.length xs = Just (Vector (Vector.unsafeUpd xs [(n,a)]))+ | otherwise = Nothing+++-- | Proof that the length of this vector is not 0.+nonEmpty :: Vector n a -> LeqProof 1 n+nonEmpty (Vector _) = LeqProof+{-# Inline nonEmpty #-}+++-- | Remove the first element of the vector, and return the rest, if any.+uncons :: forall n a. Vector n a -> (a, Either (n :~: 1) (Vector (n-1) a))+uncons v@(Vector xs) = (Vector.head xs, mbTail)+ where+ mbTail :: Either (n :~: 1) (Vector (n - 1) a)+ mbTail = case testStrictLeq (knownNat @1) (length v) of+ Left n2_leq_n ->+ do LeqProof <- return (leqSub2 n2_leq_n (leqRefl (knownNat @1)))+ return (Vector (Vector.tail xs))+ Right Refl -> Left Refl+{-# Inline uncons #-}+++--------------------------------------------------------------------------------++-- | Make a vector of the given length and element type.+-- Returns "Nothing" if the input list does not have the right number of+-- elements.+-- @O(n)@.+fromList :: (1 <= n) => NatRepr n -> [a] -> Maybe (Vector n a)+fromList n xs+ | widthVal n == Vector.length v = Just (Vector v)+ | otherwise = Nothing+ where+ v = Vector.fromList xs+{-# INLINE fromList #-}+++-- | Extract a subvector of the given vector.+slice :: (i + w <= n, 1 <= w) =>+ NatRepr i {- ^ Start index -} ->+ NatRepr w {- ^ Width of sub-vector -} ->+ Vector n a -> Vector w a+slice i w (Vector xs) = Vector (Vector.slice (widthVal i) (widthVal w) xs)+{-# INLINE slice #-}++-- | Take the front (lower-indexes) part of the vector.+take :: forall n x a. (1 <= n) => NatRepr n -> Vector (n + x) a -> Vector n a+take | LeqProof <- prf = slice (knownNat @0)+ where+ prf = leqAdd (leqRefl (Proxy @n)) (Proxy @x)++--------------------------------------------------------------------------------++instance Functor (Vector n) where+ fmap f (Vector xs) = Vector (Vector.map f xs)+ {-# Inline fmap #-}++instance Foldable (Vector n) where+ foldMap f (Vector xs) = foldMap f xs++instance Traversable (Vector n) where+ traverse f (Vector xs) = Vector <$> traverse f xs+ {-# Inline traverse #-}++-- | Zip two vectors, potentially changing types.+-- @O(n)@+zipWith :: (a -> b -> c) -> Vector n a -> Vector n b -> Vector n c+zipWith f (Vector xs) (Vector ys) = Vector (Vector.zipWith f xs ys)+{-# Inline zipWith #-}++zipWithM :: Monad m => (a -> b -> m c) ->+ Vector n a -> Vector n b -> m (Vector n c)+zipWithM f (Vector xs) (Vector ys) = Vector <$> Vector.zipWithM f xs ys+{-# Inline zipWithM #-}++zipWithM_ :: Monad m => (a -> b -> m ()) -> Vector n a -> Vector n b -> m ()+zipWithM_ f (Vector xs) (Vector ys) = Vector.zipWithM_ f xs ys+{-# Inline zipWithM_ #-}++{- | Interleave two vectors. The elements of the first vector are+at even indexes in the result, the elements of the second are at odd indexes. -}+interleave ::+ forall n a. (1 <= n) => Vector n a -> Vector n a -> Vector (2 * n) a+interleave (Vector xs) (Vector ys)+ | LeqProof <- leqMulPos (Proxy @2) (Proxy @n) = Vector zs+ where+ len = Vector.length xs + Vector.length ys+ zs = Vector.generate len (\i -> let v = if even i then xs else ys+ in v Vector.! (i `div` 2))+++--------------------------------------------------------------------------------++{- | Move the elements around, as specified by the given function.+ * Note: the reindexing function says where each of the elements+ in the new vector come from.+ * Note: it is OK for the same input element to end up in mulitple places+ in the result.+@O(n)@+-}+shuffle :: (Int -> Int) -> Vector n a -> Vector n a+shuffle f (Vector xs) = Vector ys+ where+ ys = Vector.generate (Vector.length xs) (\i -> xs Vector.! f i)+{-# Inline shuffle #-}++-- | Reverse the vector.+reverse :: forall a n. (1 <= n) => Vector n a -> Vector n a+reverse x = shuffle (\i -> lengthInt x - i - 1) x++-- | Rotate "left". The first element of the vector is on the "left", so+-- rotate left moves all elemnts toward the corresponding smaller index.+-- Elements that fall off the beginning end up at the end.+rotateL :: Int -> Vector n a -> Vector n a+rotateL !n xs = shuffle rotL xs+ where+ !len = lengthInt xs+ rotL i = (i + n) `mod` len -- `len` is known to be >= 1+{-# Inline rotateL #-}++-- | Rotate "right". The first element of the vector is on the "left", so+-- rotate right moves all elemnts toward the corresponding larger index.+-- Elements that fall off the end, end up at the beginning.+rotateR :: Int -> Vector n a -> Vector n a+rotateR !n xs = shuffle rotR xs+ where+ !len = lengthInt xs+ rotR i = (i - n) `mod` len -- `len` is known to be >= 1+{-# Inline rotateR #-}++{- | Move all elements towards smaller indexes.+Elements that fall off the front are ignored.+Empty slots are filled in with the given element.+@O(n)@. -}+shiftL :: Int -> a -> Vector n a -> Vector n a+shiftL !x a (Vector xs) = Vector ys+ where+ !len = Vector.length xs+ ys = Vector.generate len (\i -> let j = i + x+ in if j >= len then a else xs Vector.! j)+{-# Inline shiftL #-}++{- | Move all elements towards the larger indexes.+Elements that "fall" off the end are ignored.+Empty slots are filled in with the given element.+@O(n)@. -}+shiftR :: Int -> a -> Vector n a -> Vector n a+shiftR !x a (Vector xs) = Vector ys+ where+ !len = Vector.length xs+ ys = Vector.generate len (\i -> let j = i - x+ in if j < 0 then a else xs Vector.! j)+{-# Inline shiftR #-}++-------------------------------------------------------------------------------i++-- | Append two vectors. The first one is at lower indexes in the result.+append :: Vector m a -> Vector n a -> Vector (m + n) a+append v1@(Vector xs) v2@(Vector ys) =+ case leqAddPos (length v1) (length v2) of { LeqProof ->+ Vector (xs Vector.++ ys)+ }+{-# Inline append #-}++--------------------------------------------------------------------------------+-- Constructing Vectors++-- | Vector with exactly one element+singleton :: forall a. a -> Vector 1 a+singleton a = Vector (Vector.singleton a)++leqLen :: forall n a. Vector n a -> LeqProof 1 (n + 1)+leqLen v =+ let leqSucc :: forall f z. f z -> LeqProof z (z + 1)+ leqSucc fz = leqAdd (leqRefl fz :: LeqProof z z) (knownNat @1)+ in leqTrans (nonEmpty v :: LeqProof 1 n) (leqSucc (length v))++-- | Add an element to the head of a vector+cons :: forall n a. a -> Vector n a -> Vector (n+1) a+cons a v@(Vector x) = case leqLen v of LeqProof -> (Vector (Vector.cons a x))++-- | Add an element to the tail of a vector+snoc :: forall n a. Vector n a -> a -> Vector (n+1) a+snoc v@(Vector x) a = case leqLen v of LeqProof -> (Vector (Vector.snoc x a))++-- | This newtype wraps Vector so that we can curry it in the call to+-- @natRecBounded@. It adds 1 to the length so that the base case is+-- a @Vector@ of non-zero length.+newtype Vector' a n = MkVector' (Vector (n+1) a)++unVector' :: Vector' a n -> Vector (n+1) a+unVector' (MkVector' v) = v++snoc' :: forall a m. Vector' a m -> a -> Vector' a (m+1)+snoc' v = MkVector' . snoc (unVector' v)++generate' :: forall h a+ . NatRepr h+ -> (forall n. (n <= h) => NatRepr n -> a)+ -> Vector' a h+generate' h gen =+ case isZeroOrGT1 h of+ Left Refl -> base+ Right LeqProof ->+ case (minusPlusCancel h (knownNat @1) :: h - 1 + 1 :~: h) of { Refl ->+ natRecBounded (decNat h) (decNat h) base step+ }+ where base :: Vector' a 0+ base = MkVector' $ singleton (gen (knownNat @0))+ step :: forall m. (1 <= h, m <= h - 1)+ => NatRepr m -> Vector' a m -> Vector' a (m + 1)+ step m v =+ case minusPlusCancel h (knownNat @1) :: h - 1 + 1 :~: h of { Refl ->+ case (leqAdd2 (LeqProof :: LeqProof m (h-1))+ (LeqProof :: LeqProof 1 1) :: LeqProof (m+1) h) of { LeqProof ->+ snoc' v (gen (incNat m))+ }}++-- | Apply a function to each element in a range starting at zero;+-- return the a vector of values obtained.+-- cf. both @natFromZero@ and @Data.Vector.generate@+generate :: forall h a+ . NatRepr h+ -> (forall n. (n <= h) => NatRepr n -> a)+ -> Vector (h + 1) a+generate h gen = unVector' (generate' h gen)++-- | Since @Vector@ is traversable, we can pretty trivially sequence+-- @natFromZeroVec@ inside a monad.+generateM :: forall m h a. (Monad m)+ => NatRepr h+ -> (forall n. (n <= h) => NatRepr n -> m a)+ -> m (Vector (h + 1) a)+generateM h gen = sequence $ generate h gen++--------------------------------------------------------------------------------++coerceVec :: Coercible a b => Vector n a -> Vector n b+coerceVec = coerce++-- | Monadically join a vector of values, using the given function.+-- This functionality can sometimes be reproduced by creating a newtype+-- wrapper and using @joinWith@, this implementation is provided for+-- convenience.+joinWithM ::+ forall m f n w.+ (1 <= w, Monad m) =>+ (forall l. (1 <= l) => NatRepr l -> f w -> f l -> m (f (w + l)))+ {- ^ A function for appending contained elements.+ Earlier vector indexes are the first argument of the join function.+ Pass a different function to implmenet little/big endian behaviors -} ->+ NatRepr w -> Vector n (f w) -> m (f (n * w))++joinWithM jn w = fmap fst . go+ where+ go :: forall l. Vector l (f w) -> m (f (l * w), NatRepr (l * w))+ go exprs =+ case uncons exprs of+ (a, Left Refl) -> return (a, w)+ (a, Right rest) ->+ case nonEmpty rest of { LeqProof ->+ case leqMulPos (length rest) w of { LeqProof ->+ case nonEmpty exprs of { LeqProof ->+ case lemmaMul w (length exprs) of { Refl -> do+ -- @siddharthist: This could probably be written applicatively?+ (res, sz) <- go rest+ joined <- jn sz a res+ return (joined, addNat w sz)+ }}}}++-- | Join a vector of values, using the given function.+joinWith ::+ forall f n w.+ (1 <= w) =>+ (forall l. (1 <= l) => NatRepr l -> f w -> f l -> f (w + l))+ {- ^ A function for appending contained elements.+ Earlier vector indexes are the first argument of the join function.+ Pass a different function to implmenet little/big endian behaviors -} ->+ NatRepr w -> Vector n (f w) -> f (n * w)+joinWith jn w v = runIdentity $ joinWithM (\n x -> pure . (jn n x)) w v+{-# Inline joinWith #-}++-- | Split a bit-vector into a vector of bit-vectors.+-- If "LittleEndian", then less significant bits go into smaller indexes.+-- If "BigEndian", then less significant bits go into larger indexes.+splitWith :: forall f w n.+ (1 <= w, 1 <= n) =>+ Endian ->+ (forall i. (i + w <= n * w) =>+ NatRepr (n * w) -> NatRepr i -> f (n * w) -> f w)+ {- ^ A function for slicing out a chunk of length @w@, starting at @i@ -} ->+ NatRepr n -> NatRepr w -> f (n * w) -> Vector n (f w)+splitWith endian select n w val = Vector (Vector.create initializer)+ where+ len = widthVal n+ start :: Int+ next :: Int -> Int+ (start,next) = case endian of+ LittleEndian -> (0, succ)+ BigEndian -> (len - 1, pred)++ initializer :: forall s. ST s (MVector s (f w))+ initializer =+ do LeqProof <- return (leqMulPos n w)+ LeqProof <- return (leqMulMono n w)++ v <- MVector.new len+ let fill :: Int -> NatRepr i -> ST s ()+ fill loc i =+ let end = addNat i w in+ case testLeq end inLen of+ Just LeqProof ->+ do MVector.write v loc (select inLen i val)+ fill (next loc) end+ Nothing -> return ()+++ fill start (knownNat @0)+ return v++ inLen :: NatRepr (n * w)+ inLen = natMultiply n w+{-# Inline splitWith #-}++-- We can sneakily put our functor in the parameter "f" of @splitWith@ using the+-- @Compose@ newtype.+-- | An applicative version of @splitWith@.+splitWithA :: forall f g w n. (Applicative f, 1 <= w, 1 <= n) =>+ Endian ->+ (forall i. (i + w <= n * w) =>+ NatRepr (n * w) -> NatRepr i -> g (n * w) -> f (g w))+ {- ^ f function for slicing out f chunk of length @w@, starting at @i@ -} ->+ NatRepr n -> NatRepr w -> g (n * w) -> f (Vector n (g w))+splitWithA e select n w val = traverse getCompose $+ splitWith @(Compose f g) e select' n w $ Compose (pure val)+ where -- Wrap everything in Compose+ select' :: (forall i. (i + w <= n * w)+ => NatRepr (n * w) -> NatRepr i -> Compose f g (n * w) -> Compose f g w)+ -- Whatever we pass in as "val" is what's passed to select anyway,+ -- so there's no need to examine the argument. Just use "val" directly here.+ select' nw i _ = Compose $ select nw i val++newtype Vec a n = Vec (Vector n a)++vSlice :: (i + w <= l, 1 <= w) =>+ NatRepr w -> NatRepr l -> NatRepr i -> Vec a l -> Vec a w+vSlice w _ i (Vec xs) = Vec (slice i w xs)+{-# Inline vSlice #-}++-- | Append the two bit vectors. The first argument is+-- at the lower indexes of the resulting vector.+vAppend :: NatRepr n -> Vec a m -> Vec a n -> Vec a (m + n)+vAppend _ (Vec xs) (Vec ys) = Vec (append xs ys)+{-# Inline vAppend #-}++-- | Split a vector into a vector of vectors.+split :: (1 <= w, 1 <= n) =>+ NatRepr n -> NatRepr w -> Vector (n * w) a -> Vector n (Vector w a)+split n w xs = coerceVec (splitWith LittleEndian (vSlice w) n w (Vec xs))+{-# Inline split #-}++-- | Join a vector of vectors into a single vector.+join :: (1 <= w) => NatRepr w -> Vector n (Vector w a) -> Vector (n * w) a+join w xs = ys+ where Vec ys = joinWith vAppend w (coerceVec xs)+{-# Inline join #-}
submodules/parameterized-utils/test/Test/Context.hs view
@@ -16,6 +16,7 @@ import Data.Parameterized.TraversableFC import Data.Parameterized.Some +import qualified Data.Parameterized.Context as C import qualified Data.Parameterized.Context.Safe as S import qualified Data.Parameterized.Context.Unsafe as U @@ -167,4 +168,11 @@ case testEquality x y of Just Refl -> return $ vals1 == vals2 Nothing -> return $ vals1 /= vals2++ , testProperty "append_take" $ \vals1 vals2 -> ioProperty $ do+ Some x <- return $ mkUAsgn vals1+ Some y <- return $ mkUAsgn vals2+ let z = x U.<++> y+ let x' = C.take (U.size x) (U.size y) z+ return $ isJust $ testEquality x x' ]
+ submodules/parameterized-utils/test/Test/Vector.hs view
@@ -0,0 +1,67 @@+{-# Language DataKinds #-}+{-# Language ExplicitForAll #-}+{-# Language TypeOperators #-}+{-# Language TypeFamilies #-}+{-# Language FlexibleInstances #-}+{-# Language ScopedTypeVariables #-}+{-# Language StandaloneDeriving #-}+{-# Language CPP #-}+#if __GLASGOW_HASKELL__ >= 805+{-# Language NoStarIsType #-}+#endif+module Test.Vector+( vecTests+) where++import Test.Tasty+import Test.Tasty.QuickCheck (Arbitrary(..), Gen, testProperty)++import Data.Parameterized.NatRepr+import Data.Parameterized.Vector+import GHC.TypeLits+import Prelude hiding (reverse)++instance KnownNat n => Arbitrary (NatRepr n) where+ arbitrary = return knownNat++-- GHC thinks that this instances overlaps with the+-- "Arbitrary a => Arbitrary (Maybe a)" instance from QuickCheck, but it doesn't:+-- there is no "Arbitrary a => Arbitrary (Vector n a)".+--+-- While it might seem like this would just successfully generate a lot+-- of "Nothing", it does a pretty good job. Just try changing one of the tests!+instance {-# OVERLAPS #-} forall a n. (1 <= n, Arbitrary a, KnownNat n)+ => Arbitrary (Maybe (Vector n a)) where+ arbitrary = do+ n <- (arbitrary :: Gen (NatRepr n))+ l <- (arbitrary :: Gen [a])+ return $ fromList n l++instance Show (Int -> Ordering) where+ show _ = "unshowable"++-- We use @Ordering@ just because it's simple+vecTests :: IO TestTree+vecTests = testGroup "Vector" <$> return+ [ testProperty "reverse100" $+ \n v -> fromList (n :: NatRepr 100) (v :: [Ordering]) ==+ (reverse <$> (reverse <$> (fromList n v)))+ , testProperty "reverseSingleton" $+ \n v -> fromList (n :: NatRepr 1) (v :: [Ordering]) ==+ (reverse <$> (fromList n v))+ , testProperty "split-join" $+ \n w v -> (v :: Maybe (Vector (5 * 5) Ordering)) ==+ (join (n :: NatRepr 5) . split n (w :: NatRepr 5) <$> v)+ -- @cons@ is the same for vectors or lists+ , testProperty "cons" $+ \n v x -> (cons x <$> fromList (n :: NatRepr 20) (v :: [Ordering])) ==+ (fromList (incNat n) (x:v))+ -- @snoc@ is like appending to a list+ , testProperty "snoc" $+ \n v x -> (flip snoc x <$> fromList (n :: NatRepr 20) (v :: [Ordering])) ==+ (fromList (incNat n) (v ++ [x]))+ -- @generate@ is like mapping a function over indices+ , testProperty "generate" $+ \n f -> Just (generate (n :: NatRepr 55) ((f :: Int -> Ordering) . widthVal)) ==+ (fromList (incNat n) (map f [0..widthVal n]) :: Maybe (Vector 56 Ordering))+ ]
submodules/parameterized-utils/test/UnitTest.hs view
@@ -4,6 +4,7 @@ import qualified Test.Context import qualified Test.NatRepr+import qualified Test.Vector main :: IO () main = tests >>= defaultMainWithIngredients ingrs@@ -19,4 +20,5 @@ tests = testGroup "ParameterizedUtils" <$> sequence [ Test.Context.contextTests , Test.NatRepr.natTests+ , Test.Vector.vecTests ]