packages feed

OddWord 1.0.1.1 → 1.0.2.0

raw patch · 9 files changed

+397/−246 lines, 9 filesdep +hspecdep ~QuickCheckdep ~basePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: hspec

Dependency ranges changed: QuickCheck, base

API changes (from Hackage documentation)

- Data.Word.Odd: instance (GHC.Num.Num a, Data.Bits.FiniteBits a, Data.Word.Odd.TypeNum n) => Data.Bits.FiniteBits (Data.Word.Odd.OddWord a n)
- Data.Word.Odd: instance GHC.TypeNats.KnownNat a => Data.Word.Odd.TypeNum (Data.Word.Odd.TypeLits.Lit a)
+ Data.Word.Odd: class Bits a => FiniteBitsBase a
+ Data.Word.Odd: instance (GHC.Num.Num a, Data.Word.Odd.FiniteBitsBase a, Data.Word.Odd.TypeNum n) => Data.Bits.FiniteBits (Data.Word.Odd.OddWord a n)
+ Data.Word.Odd: instance Data.Word.Odd.FiniteBitsBase GHC.Integer.Type.Integer
+ Data.Word.Odd: instance Data.Word.Odd.FiniteBitsBase GHC.Word.Word16
+ Data.Word.Odd: instance Data.Word.Odd.FiniteBitsBase GHC.Word.Word32
+ Data.Word.Odd: instance Data.Word.Odd.FiniteBitsBase GHC.Word.Word64
+ Data.Word.Odd: instance Data.Word.Odd.FiniteBitsBase GHC.Word.Word8
+ Data.Word.Odd: instance Data.Word.Odd.ZNatValue 'Data.Word.Odd.IsZ
+ Data.Word.Odd: instance Data.Word.Odd.ZNatValue (Data.Word.Odd.ToZNat (n GHC.TypeNats.- 1)) => Data.Word.Odd.ZNatValue ('Data.Word.Odd.NonZ n)
+ Data.Word.Odd: instance Data.Word.Odd.ZNatValue (Data.Word.Odd.ToZNat (n GHC.TypeNats.- 16)) => Data.Word.Odd.ZNatValue ('Data.Word.Odd.NonZ4 n)
+ Data.Word.Odd: instance Data.Word.Odd.ZNatValue (Data.Word.Odd.ToZNat (n GHC.TypeNats.- 256)) => Data.Word.Odd.ZNatValue ('Data.Word.Odd.NonZ8 n)
+ Data.Word.Odd: instance Data.Word.Odd.ZNatValue (Data.Word.Odd.ToZNat (n GHC.TypeNats.- 4096)) => Data.Word.Odd.ZNatValue ('Data.Word.Odd.NonZ12 n)
+ Data.Word.Odd: instance Data.Word.Odd.ZNatValue (Data.Word.Odd.ToZNat n) => Data.Word.Odd.TypeNum (Data.Word.Odd.Lit n)
+ Data.Word.Odd: subWordClz :: FiniteBitsBase a => Int -> a -> Int
+ Data.Word.Odd: subWordCtz :: FiniteBitsBase a => Int -> a -> Int

Files

CHANGELOG view
@@ -1,5 +1,15 @@ OddWord - Release History +release-1.0.2.0 - 2018.04.11++  * Added FiniteBitsBase to support FiniteBits atop non-finite base types.+  * Added explicit Typeable instances.+  * Removed Cabal flag for type-level literals in favour of always enabling.+  * Fixed performance impact of using type-level literals.+  * Fixed undefined behaviour in test suite.+  * Changed minimum supported GHC version to 7.10 (base 4.8).+  * Changed test suite to use hspec.+ release-1.0.1.1 - 2018.04.05    * Added criterion benchmark
OddWord.cabal view
@@ -1,5 +1,5 @@ name:               OddWord-version:            1.0.1.1+version:            1.0.2.0 license:            BSD3 license-file:       LICENSE copyright:          (c) 2011-2018 Robin KAY@@ -17,52 +17,40 @@     exposes a subset of its bits as a new narrower word type. Includes     predefined type synonyms for all the odd sized words up to 63 bits. -Flag TypeLitsSupport-    description: Enable support for GHC type-level naturals.-    default: True- Library     hs-source-dirs:   src     exposed-modules:  Data.Word.Odd     default-language: Haskell2010-    other-extensions: ScopedTypeVariables CPP+    other-extensions:+        ScopedTypeVariables CPP+        DeriveDataTypeable DataKinds KindSignatures+        TypeFamilies TypeOperators UndecidableInstances     build-depends:-        base >= 4.5 && < 5-    if flag(TypeLitsSupport) && impl(ghc >= 7.8)-        cpp-options: -DTYPE_LITS-        other-modules: Data.Word.Odd.TypeLits+        base >= 4.8 && < 5  Test-Suite oddword-tests     type:             exitcode-stdio-1.0     hs-source-dirs:   test     main-is:          Main.hs     default-language: Haskell2010-    other-extensions: ScopedTypeVariables CPP+    other-modules: Equiv Props+    other-extensions:+        ScopedTypeVariables DataKinds KindSignatures     build-depends:-        base       >= 4.5 && < 5,-        QuickCheck >= 2.4 && < 2.12,+        base       >= 4.8 && < 5,+        hspec      >= 2.5 && < 2.6,+        QuickCheck >= 2.11 && < 2.12,         OddWord -Test-Suite oddword-tests-typelits-    type:             exitcode-stdio-1.0-    hs-source-dirs:   test-    main-is:          TypeLitsTest.hs-    default-language: Haskell2010-    if flag(TypeLitsSupport) && impl(ghc >= 7.8)-        buildable: True-        build-depends:-            base       >= 4.5 && < 5,-            OddWord-    else-        buildable: False- Benchmark oddword-bench     type:             exitcode-stdio-1.0     hs-source-dirs:   bench     main-is:          Main.hs     default-language: Haskell2010+    other-extensions:+        ScopedTypeVariables DataKinds     build-depends:-        base       >= 4.5 && < 5,+        base       >= 4.8 && < 5,         criterion  >= 1.4 && < 1.5,         OddWord 
bench/Main.hs view
@@ -1,5 +1,9 @@+{-# LANGUAGE ScopedTypeVariables, DataKinds #-}+ module Main where +import Data.Proxy+import Data.Typeable import Data.Word import Data.Word.Odd import Criterion.Main@@ -7,13 +11,20 @@ testAddMul :: (Num a) => a -> a testAddMul n = 2*n*n + 3*n + 4 +testEquals :: (Eq a, Num a) => a -> Bool+testEquals n = n == 7 || n == 11 || n == 13++benchNum :: forall a. (Eq a, Num a, Typeable a) => a -> Benchmark+benchNum x =+    bgroup (show $ typeRep (Proxy :: Proxy a)) [+        bench "addMul" $ whnf testAddMul x,+        bench "equals" $ whnf testEquals x+    ]+ main :: IO () main =     defaultMain [-        bgroup "Word" [-            bench "addMul" $ whnf testAddMul (1::Word)-        ],-        bgroup "Word20" [-            bench "addMul" $ whnf testAddMul (1::Word20)-        ]+        benchNum (1::Word),+        benchNum (1::Word20),+        benchNum (1::(OddWord Word32 (Lit 20)))     ]
src/Data/Word/Odd.hs view
@@ -1,4 +1,6 @@-{-# LANGUAGE Haskell2010, ScopedTypeVariables, CPP #-}+{-# LANGUAGE Haskell2010, ScopedTypeVariables, CPP,+             DeriveDataTypeable, DataKinds, KindSignatures,+             TypeFamilies, TypeOperators, UndecidableInstances #-}  module Data.Word.Odd (     -- * Odd Word Wrapper@@ -8,10 +10,13 @@     TypeNum,     One,     Zero,-#ifdef TYPE_LITS     Lit,-#endif +    -- * Finite Bits+    FiniteBitsBase(+        subWordClz,+        subWordCtz),+     -- * Predefined Odd Words     Word1, Word2, Word3, Word4, Word5, Word6, Word7,     Word9,  Word10, Word11, Word12, Word13, Word14, Word15,@@ -24,14 +29,11 @@ ) where  import Data.Bits+import Data.Proxy import Data.Word import Data.Function--#ifdef TYPE_LITS-import Data.Word.Odd.TypeLits-import Data.Proxy+import Data.Typeable import GHC.TypeLits-#endif  -- | 'OddWord' provides a range of unsigned integer word types with a length in -- bits encoded at the type level. The first type parameter @a@ must supply an@@ -58,7 +60,7 @@ -- greater than that of the underlying integer type. The behaviour is also -- undefined if the specified length is equal to that of the underlying integer -- type and that type is also signed.-newtype OddWord a n = OW {unOW :: a} deriving (Eq, Ord)+newtype OddWord a n = OW {unOW :: a} deriving (Eq, Ord, Typeable)  data TypeNumBuilder a = TypeNumBuilder Int Int @@ -71,12 +73,16 @@  -- | Represents a type-level number with a leading one bit followed by the -- string of digits specified by @a@.-data One a+data One a deriving Typeable  -- | Represents a type-level number with a placeholder zero bit followed by the -- string of digits specified by @a@.-data Zero a+data Zero a deriving Typeable +-- | Converts a native GHC type-level natural into one usable by this library.+-- This requires the @DataKinds@ extension.+data Lit :: Nat -> * deriving Typeable+ instance TypeNum () where     typeNum = TypeNumBuilder 0 0 @@ -88,11 +94,110 @@     typeNum = let (TypeNumBuilder n m) = (typeNum :: TypeNumBuilder a)               in TypeNumBuilder (n) (m+1) -#ifdef TYPE_LITS-instance (KnownNat a) => TypeNum (Lit a) where-    typeNum = TypeNumBuilder (fromIntegral $ natVal (Proxy :: Proxy a)) 0+-- | Provides a more efficient mechanism for converting 'Nat'-kinded types into+-- small integers than 'KnownNat'.+#if MIN_VERSION_base(4,11,0)+-- Decomposes Nats in log2(n) recursions, one bit at a time.+data ZNat = IsZ | NonZE Nat | NonZO Nat++type family ToZNatImpl (n::Nat) (lsb::Nat) where+    ToZNatImpl 0 0 = IsZ+    ToZNatImpl n 0 = NonZE n+    ToZNatImpl n 1 = NonZO n++type ToZNat n = ToZNatImpl n (Mod n 2)++class ZNatValue (n::ZNat) where+    znatIntVal :: proxy n -> Int++instance ZNatValue IsZ where+    znatIntVal _ = 0+    {-# INLINE znatIntVal #-}++instance ZNatValue (ToZNat (Div n 2)) => ZNatValue (NonZE n) where+    znatIntVal _ = 2 * (znatIntVal (Proxy :: Proxy (ToZNat (Div n 2))))+    {-# INLINE znatIntVal #-}++instance ZNatValue (ToZNat (Div n 2)) => ZNatValue (NonZO n) where+    znatIntVal _ = 1 + 2 * (znatIntVal (Proxy :: Proxy (ToZNat (Div n 2))))+    {-# INLINE znatIntVal #-}+#else+-- For older GHCs that don't support Div and Mod, decomposes Nats in+-- 16*log16(n) recursions for values of n below 2^16.+data ZNat = IsZ | NonZ Nat | NonZ4 Nat | NonZ8 Nat | NonZ12 Nat++-- Regarding u, v, and w, GHC 7.10 doesn't like wildcards in type families.+type family ToZNatImpl+        (n::Nat) (nz4::Ordering) (nz8::Ordering) (nz12::Ordering) where+    ToZNatImpl 0 LT LT LT = IsZ+    ToZNatImpl n LT LT LT = NonZ n+    ToZNatImpl n u  LT LT = NonZ4 n+    ToZNatImpl n u  v  LT = NonZ8 n+    ToZNatImpl n u  v  w  = NonZ12 n++type ToZNat n = ToZNatImpl n (CmpNat n 16) (CmpNat n 256) (CmpNat n 4096)++class ZNatValue (n::ZNat) where+    znatIntVal :: proxy n -> Int++instance ZNatValue IsZ where+    znatIntVal _ = 0+    {-# INLINE znatIntVal #-}++instance ZNatValue (ToZNat (n - 1)) => ZNatValue (NonZ n) where+    znatIntVal _ = 1 + (znatIntVal (Proxy :: Proxy (ToZNat (n - 1))))+    {-# INLINE znatIntVal #-}++instance ZNatValue (ToZNat (n - 16)) => ZNatValue (NonZ4 n) where+    znatIntVal _ = 16 + (znatIntVal (Proxy :: Proxy (ToZNat (n - 16))))+    {-# INLINE znatIntVal #-}++instance ZNatValue (ToZNat (n - 256)) => ZNatValue (NonZ8 n) where+    znatIntVal _ = 256 + (znatIntVal (Proxy :: Proxy (ToZNat (n - 256))))+    {-# INLINE znatIntVal #-}++instance ZNatValue (ToZNat (n - 4096)) => ZNatValue (NonZ12 n) where+    znatIntVal _ = 4096 + (znatIntVal (Proxy :: Proxy (ToZNat (n - 4096))))+    {-# INLINE znatIntVal #-} #endif +instance (ZNatValue (ToZNat n)) => TypeNum (Lit n) where+    typeNum = TypeNumBuilder+        (fromIntegral $ znatIntVal (Proxy :: Proxy (ToZNat n))) 0++-- | Required to implement 'FiniteBits' for an 'OddWord' based on type @a@.+class Bits a => FiniteBitsBase a where+    -- | Count the leading zeros on a @w@-bit wide word.+    subWordClz :: Int -> a -> Int+    subWordClz w x = (w-1) - worker (w-1)+        where worker i | i < 0       = i+                       | testBit x i = i+                       | otherwise   = worker (i-1)+    -- | Count the trailing zeros on a @w@-bit wide word.+    subWordCtz :: Int -> a -> Int+    subWordCtz w x = worker 0+        where worker i | i >= w      = i+                       | testBit x i = i+                       | otherwise   = worker (i+1)++instance FiniteBitsBase Word8 where+    subWordClz w x = countLeadingZeros x + w - finiteBitSize x+    subWordCtz w x = min (countTrailingZeros x) w++instance FiniteBitsBase Word16 where+    subWordClz w x = countLeadingZeros x + w - finiteBitSize x+    subWordCtz w x = min (countTrailingZeros x) w++instance FiniteBitsBase Word32 where+    subWordClz w x = countLeadingZeros x + w - finiteBitSize x+    subWordCtz w x = min (countTrailingZeros x) w++instance FiniteBitsBase Word64 where+    subWordClz w x = countLeadingZeros x + w - finiteBitSize x+    subWordCtz w x = min (countTrailingZeros x) w++instance FiniteBitsBase Integer where+ -- | Wraps both parts of a homogenous pair with the OddWord constructor. pairOW :: (a, a) -> (OddWord a n, OddWord a n) pairOW = uncurry ((,) `on` OW)@@ -170,7 +275,7 @@     (OW l) .&. (OW r) = OW $ l .&. r     (OW l) .|. (OW r) = OW $ l .|. r     xor (OW l) (OW r) = OW $ xor l r-    complement (OW x) = maskOW $ complement x+    complement x = x `xor` owMask     bit n | n < fromTypeNum (typeNum :: TypeNumBuilder n)           = OW $ bit n           | otherwise = OW 0@@ -183,9 +288,7 @@                            | otherwise = OW x     testBit (OW x) n = testBit x n     bitSize _ = fromTypeNum (typeNum :: TypeNumBuilder n)-#if MIN_VERSION_base(4,7,0)     bitSizeMaybe _ = Just $ fromTypeNum (typeNum :: TypeNumBuilder n)-#endif     isSigned _ = False      shiftL (OW x) n = maskOW $ shiftL x n     shiftR (OW x) n = OW $ shiftR x n@@ -199,16 +302,12 @@               w  = fromTypeNum (typeNum :: TypeNumBuilder n)     popCount (OW x) = popCount x -#if MIN_VERSION_base(4,7,0)-instance (Num a, FiniteBits a, TypeNum n) => FiniteBits (OddWord a n) where+instance (Num a, FiniteBitsBase a, TypeNum n) => FiniteBits (OddWord a n) where     finiteBitSize _ = fromTypeNum (typeNum :: TypeNumBuilder n) -#if MIN_VERSION_base(4,8,0)-    countLeadingZeros (OW x) = countLeadingZeros x +-        fromTypeNum (typeNum :: TypeNumBuilder n) - finiteBitSize x-    countTrailingZeros (OW x) = min (countTrailingZeros x) $-        fromTypeNum (typeNum :: TypeNumBuilder n)-#endif-#endif+    countLeadingZeros (OW x) =+        subWordClz (fromTypeNum (typeNum :: TypeNumBuilder n)) x+    countTrailingZeros (OW x) =+        subWordCtz (fromTypeNum (typeNum :: TypeNumBuilder n)) x  -- -- Predefined Odd Words
− src/Data/Word/Odd/TypeLits.hs
@@ -1,9 +0,0 @@-{-# LANGUAGE Haskell2010, DataKinds, KindSignatures #-}--module Data.Word.Odd.TypeLits where--import GHC.TypeLits---- | Converts a native GHC type-level natural into one usable by this library.--- This requires GHC 7.8 or later and the @DataKinds@ extension.-data Lit :: Nat -> *
+ test/Equiv.hs view
@@ -0,0 +1,141 @@+{-# LANGUAGE Haskell2010, ScopedTypeVariables, DataKinds, KindSignatures #-}++module Equiv where++import Prelude hiding (catch)+import Test.QuickCheck hiding ((.&.))+import Test.QuickCheck.Gen+import Data.Bits+import Data.Maybe+import Data.Proxy+import Data.Word+import Data.Word.Odd+import Control.Applicative+import Control.Exception+import System.IO.Unsafe+import GHC.TypeLits++-- | Represents a range of unary functions which can be applied to a word.+data UFunc (n::Nat)+    = Add   Integer | Mul   Integer | Sub   Integer | SubR  Integer+    | Div   Integer | Mod   Integer | Quot  Integer | Rem   Integer+    | DivR  Integer | ModR  Integer | QuotR Integer | RemR  Integer+    | Neg           | Abs           | Inv           | AddDigit+    | From  Integer | And   Integer | Or    Integer | Xor   Integer+    | TstB  Int     | ClrB  Int     | SetB  Int     | InvB  Int+    | FromB Int     | Shift Int     | Rot   Int     | PopCnt+    | CntLZ         | CntTZ+    | AdjEnum Int Integer+    deriving Show++instance KnownNat n => Arbitrary (UFunc n) where+    arbitrary = oneof+        [Add   <$> choose (0, upper)+        ,Mul   <$> choose (0, upper)+        ,Sub   <$> choose (0, upper)+        ,SubR  <$> choose (0, upper)+        ,Div   <$> choose (0, upper)+        ,Mod   <$> choose (0, upper)+        ,Quot  <$> choose (0, upper)+        ,Rem   <$> choose (0, upper)+        ,DivR  <$> choose (0, upper)+        ,ModR  <$> choose (0, upper)+        ,QuotR <$> choose (0, upper)+        ,RemR  <$> choose (0, upper)+        ,return Neg+        ,return Abs+        ,return Inv+        ,return AddDigit+        ,From  <$> arbitrary+        ,And   <$> choose (0, upper)+        ,Or    <$> choose (0, upper)+        ,Xor   <$> choose (0, upper)+        ,TstB  <$> choose (0, 2*width)+        ,ClrB  <$> choose (0, 2*width)+        ,SetB  <$> choose (0, 2*width)+        ,InvB  <$> choose (0, 2*width)+        ,FromB <$> choose (0, 2*width)+        ,Shift <$> choose (0, 2*width)+        ,Rot   <$> choose (0, 2*width)+        ,return PopCnt+        ,return CntLZ+        ,return CntTZ+        ,AdjEnum <$> arbitrary <*> choose (0, upper)+        ]+        where width = fromIntegral $ natVal (Proxy :: Proxy n) +              upper = shiftL 1 width - 1++-- | Total wrapper for 'div'.+safeDiv :: (Integral a, Bounded a) => a -> a -> a+safeDiv d 0 = maxBound+safeDiv d n = div d n++-- | Total wrapper for 'mod'.+safeMod :: (Integral a) => a -> a -> a+safeMod d 0 = 0+safeMod d n = mod d n++-- | Total wrapper for 'quot'.+safeQuot :: (Integral a, Bounded a) => a -> a -> a+safeQuot d 0 = maxBound+safeQuot d n = quot d n++-- | Total wrapper for 'rem'.+safeRem :: (Integral a) => a -> a -> a+safeRem d 0 = 0+safeRem d n = rem d n++-- | Total wrapper for 'toEnum'.+safeToEnum :: (Enum a) => a -> Int -> a+safeToEnum def x =+    unsafePerformIO (evaluate (toEnum x) `catch` \(ErrorCall _) -> return def)++-- | Interpreter for executing 'UFunc' values.+fromUFunc :: (Integral a, Bounded a, Enum a, FiniteBits a, Read a, Show a) =>+    UFunc n -> a -> a+fromUFunc (Add   i) x = x + (fromInteger i)+fromUFunc (Mul   i) x = x * (fromInteger i)+fromUFunc (Sub   i) x = x - (fromInteger i)+fromUFunc (SubR  i) x = (fromInteger i) - x+fromUFunc (Div   i) x = safeDiv  x (fromInteger i)+fromUFunc (Mod   i) x = safeMod  x (fromInteger i)+fromUFunc (Quot  i) x = safeQuot x (fromInteger i)+fromUFunc (Rem   i) x = safeRem  x (fromInteger i)+fromUFunc (DivR  i) x = safeDiv  (fromInteger i) x+fromUFunc (ModR  i) x = safeMod  (fromInteger i) x+fromUFunc (QuotR i) x = safeQuot (fromInteger i) x+fromUFunc (RemR  i) x = safeRem  (fromInteger i) x+fromUFunc  Neg      x = negate x+fromUFunc  Abs      x = abs x+fromUFunc  Inv      x = complement x+fromUFunc (From i)  _ = fromInteger i+fromUFunc  AddDigit x = read . ('1':) $ show x+fromUFunc (And   i) x = x .&. (fromInteger i)+fromUFunc (Or    i) x = x .|. (fromInteger i)+fromUFunc (Xor   i) x = xor x (fromInteger i)+fromUFunc (TstB  n) x = fromIntegral $ fromEnum $ testBit x n+fromUFunc (ClrB  n) x = clearBit x n+fromUFunc (SetB  n) x = setBit x n+fromUFunc (InvB  n) x = complementBit x n+fromUFunc (FromB n) _ = bit n+fromUFunc (Shift n) x = shift x n+fromUFunc (Rot   n) x = rotate x n+fromUFunc  PopCnt   x = fromIntegral $ popCount x+fromUFunc  CntLZ    x = fromIntegral $ countLeadingZeros x+fromUFunc  CntTZ    x = fromIntegral $ countTrailingZeros x+fromUFunc (AdjEnum i def) x = safeToEnum (fromIntegral def) . (+i) $ fromEnum x++-- | Checks that computations using real and simulated words produce the same+-- result for a series of 'UFunc's.+verifyEquivalence :: forall n a b.+    (KnownNat n,+     Integral a, Bounded a, Enum a, FiniteBits a, Read a, Show a,+     Integral b, Bounded b, Enum b, FiniteBits b, Read b, Show b) =>+    Proxy n -> Proxy a -> Proxy b -> Property+verifyEquivalence width _ _ = property $ \(us :: [UFunc n]) ->+    let refFn = foldr (.) id $ map fromUFunc us :: a -> a+        tstFn = foldr (.) id $ map fromUFunc us :: b -> b+    in toInteger (refFn 0) == toInteger (tstFn 0)++-- | A 16-bit word backed by something else.+type TestWord16 a = OddWord a (One (Zero (Zero (Zero (Zero ())))))
test/Main.hs view
@@ -1,147 +1,18 @@-{-# LANGUAGE Haskell2010, ScopedTypeVariables, CPP #-}+{-# LANGUAGE Haskell2010, ScopedTypeVariables, DataKinds #-}  module Main where -import Prelude hiding (catch)-import System.Exit-import Test.QuickCheck hiding ((.&.))-import Test.QuickCheck.Gen import Data.Bits import Data.Maybe import Data.Word import Data.Word.Odd-import Control.Applicative-import Control.Exception-import System.IO.Unsafe---- | Represents a range of unary functions which can be applied to a word.-data UFunc-    = Add   Integer | Mul   Integer | Sub   Integer | SubR  Integer-    | Div   Integer | Mod   Integer | Quot  Integer | Rem   Integer-    | DivR  Integer | ModR  Integer | QuotR Integer | RemR  Integer-    | Neg           | Abs           | Inv           | AddDigit-    | From  Integer | And   Integer | Or    Integer | Xor   Integer-    | TstB  Int     | ClrB  Int     | SetB  Int     | InvB  Int-    | FromB Int     | Shift Int     | Rot   Int     | PopCnt-#if MIN_VERSION_base(4,8,0)-    | CntLZ         | CntTZ-#endif-    | AdjEnum Int Integer-    deriving Show--instance Arbitrary (UFunc) where-    arbitrary = oneof-        [Add   <$> choose (0, 0xffff)-        ,Mul   <$> choose (0, 0xffff)-        ,Sub   <$> choose (0, 0xffff)-        ,SubR  <$> choose (0, 0xffff)-        ,Div   <$> choose (0, 0xffff)-        ,Mod   <$> choose (0, 0xffff)-        ,Quot  <$> choose (0, 0xffff)-        ,Rem   <$> choose (0, 0xffff)-        ,DivR  <$> choose (0, 0xffff)-        ,ModR  <$> choose (0, 0xffff)-        ,QuotR <$> choose (0, 0xffff)-        ,RemR  <$> choose (0, 0xffff)-        ,return Neg-        ,return Abs-        ,return Inv-        ,return AddDigit-        ,From  <$> arbitrary-        ,And   <$> choose (0, 0xffff)-        ,Or    <$> choose (0, 0xffff)-        ,Xor   <$> choose (0, 0xffff)-        ,TstB  <$> choose (-32, 32)-        ,ClrB  <$> choose (-32, 32)-        ,SetB  <$> choose (-32, 32)-        ,InvB  <$> choose (-32, 32)-        ,FromB <$> choose (-32, 32)-        ,Shift <$> choose (-32, 32)-        ,Rot   <$> choose (-32, 32)-        ,return PopCnt-#if MIN_VERSION_base(4,8,0)-        ,return CntLZ-        ,return CntTZ-#endif-        ,AdjEnum <$> choose (-0x1ffff, 0x1ffff) <*> choose (0, 0xffff)-        ]---- | Total wrapper for 'div'.-safeDiv :: (Integral a, Bounded a) => a -> a -> a-safeDiv d 0 = maxBound-safeDiv d n = div d n---- | Total wrapper for 'mod'.-safeMod :: (Integral a) => a -> a -> a-safeMod d 0 = 0-safeMod d n = mod d n---- | Total wrapper for 'quot'.-safeQuot :: (Integral a, Bounded a) => a -> a -> a-safeQuot d 0 = maxBound-safeQuot d n = quot d n---- | Total wrapper for 'rem'.-safeRem :: (Integral a) => a -> a -> a-safeRem d 0 = 0-safeRem d n = rem d n---- | Total wrapper for 'toEnum'.-safeToEnum :: (Enum a) => a -> Int -> a-safeToEnum def x =-    unsafePerformIO (evaluate (toEnum x) `catch` \(ErrorCall _) -> return def)---- | Interpreter for executing 'UFunc' values.-#if MIN_VERSION_base(4,7,0)-fromUFunc :: (Integral a, Bounded a, Enum a, FiniteBits a, Read a, Show a) =>-#else-fromUFunc :: (Integral a, Bounded a, Enum a, Bits a, Read a, Show a) =>-#endif-    UFunc -> a -> a-fromUFunc (Add   i) x = x + (fromInteger i)-fromUFunc (Mul   i) x = x * (fromInteger i)-fromUFunc (Sub   i) x = x - (fromInteger i)-fromUFunc (SubR  i) x = (fromInteger i) - x-fromUFunc (Div   i) x = safeDiv  x (fromInteger i)-fromUFunc (Mod   i) x = safeMod  x (fromInteger i)-fromUFunc (Quot  i) x = safeQuot x (fromInteger i)-fromUFunc (Rem   i) x = safeRem  x (fromInteger i)-fromUFunc (DivR  i) x = safeDiv  (fromInteger i) x-fromUFunc (ModR  i) x = safeMod  (fromInteger i) x-fromUFunc (QuotR i) x = safeQuot (fromInteger i) x-fromUFunc (RemR  i) x = safeRem  (fromInteger i) x-fromUFunc  Neg      x = negate x-fromUFunc  Abs      x = abs x-fromUFunc  Inv      x = complement x-fromUFunc (From i)  _ = fromInteger i-fromUFunc  AddDigit x = read . ('1':) $ show x-fromUFunc (And   i) x = x .&. (fromInteger i)-fromUFunc (Or    i) x = x .|. (fromInteger i)-fromUFunc (Xor   i) x = xor x (fromInteger i)-fromUFunc (TstB  n) x = fromIntegral $ fromEnum $ testBit x n-fromUFunc (ClrB  n) x = clearBit x n-fromUFunc (SetB  n) x = setBit x n-fromUFunc (InvB  n) x = complementBit x n-fromUFunc (FromB n) _ = bit n-fromUFunc (Shift n) x = shift x n-fromUFunc (Rot   n) x = rotate x n-fromUFunc  PopCnt   x = fromIntegral $ popCount x-#if MIN_VERSION_base(4,8,0)-fromUFunc  CntLZ    x = fromIntegral $ countLeadingZeros x-fromUFunc  CntTZ    x = fromIntegral $ countTrailingZeros x-#endif-fromUFunc (AdjEnum i def) x = safeToEnum (fromIntegral def) . (+i) $ fromEnum x---- | A 16-bit word underlied by a 32-bit word.-type TestWord16 = OddWord Word32 (One (Zero (Zero (Zero (Zero ())))))+import Data.Proxy+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck --- | Checks that computations using real and simulated 16-bit words produce--- the same result for a series of 'UFunc's.-verifyTestWord16 :: [UFunc] -> Bool-verifyTestWord16 us =-    let refFn = foldr (.) id $ map fromUFunc us :: Word16 -> Word16-        tstFn = foldr (.) id $ map fromUFunc us :: TestWord16 -> TestWord16-    in toInteger (refFn 0) == toInteger (tstFn 0)+import Equiv+import Props  -- | List of bit lengths for all words up to 63-bits. preDefWordLengths :: [Int]@@ -167,22 +38,49 @@     bits (0 :: Word55), bits (0 :: Word56), bits (0 :: Word57),     bits (0 :: Word58), bits (0 :: Word59), bits (0 :: Word60),     bits (0 :: Word61), bits (0 :: Word62), bits (0 :: Word63)]-#if MIN_VERSION_base(4,8,0)-    where bits n = fromMaybe 0 $ bitSizeMaybe n-#else-    where bits n = bitSize n-#endif+    where bits n = finiteBitSize n +typeLitWordLengths :: [Int]+typeLitWordLengths = [+    finiteBitSize (0 :: OddWord Word8 (Lit 1)),+    finiteBitSize (0 :: OddWord Word8 (Lit 2)),+    finiteBitSize (0 :: OddWord Word8 (Lit 3)),+    finiteBitSize (0 :: OddWord Word8 (Lit 4))]++checkWordLengths :: [Int] -> Expectation+checkWordLengths =+    flip shouldBe [] .+    map fst . filter snd . map (\t -> (t,uncurry (/=) t)) .  zip [1..]+ main :: IO ()-main = do-    -- Verify lengths of predefined odd word synonyms-    mapM_ (\(u,v) -> putStrLn (-        showString "Error: Word" . shows u . showString " has length " $-            shows v ".") >> exitFailure) $-        map fst $ filter snd $ map (\t -> (t,uncurry (/=) t)) $-        zip [1..] preDefWordLengths-    -- Verify correctness of operations on a test word type-    r <- quickCheckWithResult stdArgs {maxSuccess = 1000} verifyTestWord16-    case r of-        Success _ _ _ -> exitSuccess-        _             -> exitFailure+main = hspec $ do+    describe "Predefined odd word synonyms" $+        it "have the correct length" $+            checkWordLengths preDefWordLengths+    describe "Odd words with type literals" $+        it "have the correct length" $+            checkWordLengths typeLitWordLengths+    modifyMaxSuccess (const 5000) $ describe "Word16" $ do+        it "is equivalent to TestWord16 Word16" $+            verifyEquivalence (Proxy :: Proxy 16)+                (Proxy :: Proxy Word16) (Proxy :: Proxy (TestWord16 Word16))+        it "is equivalent to TestWord16 Word32" $+            verifyEquivalence (Proxy :: Proxy 16)+                (Proxy :: Proxy Word16) (Proxy :: Proxy (TestWord16 Word32))+        it "is equivalent to TestWord16 Word64" $+            verifyEquivalence (Proxy :: Proxy 16)+                (Proxy :: Proxy Word16) (Proxy :: Proxy (TestWord16 Word64))+        it "is equivalent to TestWord16 Integer" $+            verifyEquivalence (Proxy :: Proxy 16)+                (Proxy :: Proxy Word16) (Proxy :: Proxy (TestWord16 Integer))+    describe "Word20" $ do+        it "is in bounds" $ +            propInBounds (Proxy :: Proxy 20) (Proxy :: Proxy Word20)+        it "can rotate left and right" $ +            propRotateRL (Proxy :: Proxy Word20)+    describe "Word2000" $ do+        it "is in bounds" $ +            propInBounds (Proxy :: Proxy 2000)+                         (Proxy :: Proxy (OddWord Integer (Lit 2000)))+        it "can rotate left and right" $ +            propRotateRL (Proxy :: Proxy (OddWord Integer (Lit 2000)))
+ test/Props.hs view
@@ -0,0 +1,38 @@+{-# LANGUAGE Haskell2010, ScopedTypeVariables, DataKinds #-}++module Props where++import Test.QuickCheck hiding ((.&.))+import Test.QuickCheck.Gen+import Data.Bits+import Data.Proxy+import Data.Word+import Data.Word.Odd+import GHC.TypeLits++import Equiv++genOddWord :: forall a n.+    (Integral a, Bits a, TypeNum n) =>+    Proxy (OddWord a n) -> Gen (OddWord a n)+genOddWord _ =+    fmap (fromIntegral :: Integer -> OddWord a n) $ choose (+        fromIntegral (minBound::OddWord a n),+        fromIntegral (maxBound::OddWord a n))++propRotateRL :: (Integral a, FiniteBitsBase a, TypeNum n) =>+    Proxy (OddWord a n) -> Property+propRotateRL proxy =+    property $ do+        x <- genOddWord proxy+        i <- choose (0, 2*finiteBitSize x)+        return $ (==) x $ flip rotateL i $ rotateR x i++propInBounds :: forall n a.+    (KnownNat n, Integral a, Bounded a, Enum a, FiniteBits a, Read a, Show a) =>+    Proxy n -> Proxy a -> Property+propInBounds _ _ = property $ \(us :: [UFunc n]) ->+    let tstFn = foldr (.) id $ map fromUFunc us :: a -> a+        value = toInteger $ tstFn 0+    in value >= toInteger (minBound :: a) &&+       value <= toInteger (maxBound :: a)
− test/TypeLitsTest.hs
@@ -1,25 +0,0 @@-{-# LANGUAGE Haskell2010, ScopedTypeVariables, DataKinds #-}--module Main where--import System.Exit-import Data.Bits-import Data.Word-import Data.Word.Odd-import GHC.TypeLits--typeLitWordLengths :: [Int]-typeLitWordLengths = [-    finiteBitSize (0 :: OddWord Word8 (Lit 1)),-    finiteBitSize (0 :: OddWord Word8 (Lit 2)),-    finiteBitSize (0 :: OddWord Word8 (Lit 3)),-    finiteBitSize (0 :: OddWord Word8 (Lit 4))]--main :: IO ()-main = do-    -- Verify lengths of odd words defined using GHC type-level literals-    mapM_ (\(u,v) -> putStrLn (-        showString "Error: Word" . shows u . showString " has length " $-            shows v ".") >> exitFailure) $-        map fst $ filter snd $ map (\t -> (t,uncurry (/=) t)) $-        zip [1..] typeLitWordLengths