Commits

Sergey Astanin  committed 2057f73

Replace partial bitSize with total finiteBitSize and bitSizeMaybe (GHC 7.8)

API changes:

- `Codec.Binary.Gray.Bits` functions now work only on `FiniteBits` types
- `Codec.Binary.Gray.List.toList'` now works only on `FiniteBits` types
- `Codec.Binary.Gray.List.toList` now returns an empty list on negative
values of unbounded types (on negative `Integer`s).

  • Participants
  • Parent commits 3f9cfc7

Comments (0)

Files changed (4)

File Codec/Binary/Gray/Bits.hs

     ) where
 
 import Data.Bits
-    ( Bits, testBit, setBit, clearBit, bitSize
+    ( FiniteBits, testBit, setBit, clearBit, finiteBitSize
     , shiftL, shiftR, complement, xor, (.&.), (.|.), isSigned)
 
 import qualified Codec.Binary.Gray.List as L
 
 -- | Right shift without extension of the sign bit (reset it to zero).
-shiftR' :: (Bits a, Num a) => a -> Int -> a
+shiftR' :: (FiniteBits a, Num a) => a -> Int -> a
 shiftR' n 0    = n
 shiftR' n s
   | isSigned n && signum n == -1 =
-      let n' = clearBit (shiftR n 1) (bitSize n - 1)
+      let n' = clearBit (shiftR n 1) (finiteBitSize n - 1)
       in  shiftR' n' (s-1)
   | otherwise  = shiftR n s
 
 -- | Convert an integer number from binary to Gray code.
---
--- 'gray' is undefined for negative numbers of types that do not have
--- fixed bitsize, e.g. for negative 'Integer's.
-gray :: (Bits a, Num a) => a -> a
+gray :: (FiniteBits a, Num a) => a -> a
 gray n = n `xor` (shiftR' n 1)
 
 -- | Convert an integer number from Gray code to binary.
---
--- 'binary' is undefined for types that do not have fixed bitsize,
--- e.g. for 'Integer'.
-binary :: (Bits a, Num a) => a -> a
+binary :: (FiniteBits a, Num a) => a -> a
 binary 0 = 0
 binary n =
   binary' mask0 n (copyMSB n)
   where
-    sz = bitSize n - 1
+    sz = finiteBitSize n - 1
     mask0 = let m = setBit 0 sz in  (m, m)
     copyMSB n = (setBit 0 sz) .&. n
 
   | complement maskReady == 0 = nbin
   | otherwise =
      let
-       sz = bitSize ngray - 1
+       sz = finiteBitSize ngray - 1
        nReady = maskReady .&. nbin
        maskReady' = setBit (shiftR maskReady 1) sz
        maskLast' = shiftR' maskLast 1
 
 -- | Render binary code as a string of @0@s and @1@s.
 -- For example, @(42::Int8)@ is formatted as @101010@.
-showBits :: (Bits a, Num a) => a -> String
+showBits :: (FiniteBits a, Num a) => a -> String
 showBits = L.showBits . L.toList

File Codec/Binary/Gray/List.hs

     , showBits
     ) where
 
-import Data.Bits (Bits, testBit, bitSize, shiftR, isSigned)
+import Data.Bits (FiniteBits, Bits, testBit, finiteBitSize, bitSizeMaybe, shiftR, isSigned)
 
 boolXOR :: Bool -> Bool -> Bool
 boolXOR p q = (p && not q) || (not p && q)
 -- | Convert a number to a list of bits in usual binary encoding (most
 -- significant bit last). Truncates unset major bits.
 --
--- This function is undefined for negative numbers of types that do not
--- have fixed bitsize, like 'Integer'.
+-- The function may be also applied to unbounded integral types (like
+-- 'Integer'): it will return a list of bits for positive values, and
+-- an empty list for negative values or zero.
 toList :: (Bits b, Num b) => b -> [Bool]
 toList 0 = []
-toList i
-  | isSigned i && signum i == (-1) =
-      let b = map not . toList $ negate i - 1
-      in  b ++ (take (bitSize i - length b) $ repeat True) -- pad major bits
-  | otherwise        =
+toList i =
+  let mbSize = bitSizeMaybe i
+      isNegative = isSigned i && signum i == (-1)
+  in  case (mbSize, isNegative) of
+        (Just _, False) -> positiveToList i
+        (Just size, True) -> negativeToList size i
+        (Nothing, False) -> positiveToList i
+        (Nothing, True) -> []
+  where
+    positiveToList i =
       let rest = toList $ shiftR i 1  -- works only for positive i
       in  (testBit i 0 : rest)
+    negativeToList bsize i =
+        let b = map not . toList $ negate i - 1
+        in  b ++ (take (bsize - length b) $ repeat True)
+        --    ^^^ pad major bits
 
 -- | Convert a number to a list of bits in usual binary encoding (most
 -- significant bit last).
 --
 -- Like 'toList', but returns all unset major bits too. So the length
--- of the output is always the same length as @bitSize i@.
-toList' :: (Bits b, Num b) => b -> [Bool]
-toList' i = map (testBit i) [0..bitSize i - 1]
+-- of the output is always the same length as @finiteBitSize i@.
+toList' :: (FiniteBits b, Num b) => b -> [Bool]
+toList' i = map (testBit i) [0..finiteBitSize i - 1]
 
 -- | Convert a list of bits in binary encoding to a number.
 fromList :: (Bits b, Num b) => [Bool] -> b

File Codec/Binary/Gray_props.hs

 import qualified Codec.Binary.Gray.Bits as B
 import qualified Codec.Binary.Gray.List as L
 
-import Data.Bits (testBit, bitSize, Bits)
+import Data.Bits (testBit, finiteBitSize, Bits, FiniteBits)
 import Data.Function (on)
+import Data.Word (Word)
 
 ---
 --- Properties of list-based functions
 prop_lists_correct_bits_Int =
   label "toList is correct [Int]" $
   forAll (arbitrary :: Gen Int) $ \i ->
-      let bts = map (testBit i) [0..(bitSize i)-1]
+      let bts = map (testBit i) [0..(finiteBitSize i)-1]
           padded = (L.toList i) ++ (repeat False)
       in  all id $ zipWith (==) bts padded
 
            else go (d+1) xs ys
 
 ---
---- Properties of functions for Bits types
+--- Properties of functions for FiniteBits types
 ---
 prop_bits_id = label "binary . gray == gray . binary == id" $
   forAll (arbitrary :: Gen Int) $ \i ->
   forAll (arbitrary :: Gen Int) $ \i ->
       (hammingBits `on` B.gray) i (i+1) == 1
 
-prop_bits_gray_succ_Integer = label "hamming x (x+1) == 1 [Integer]" $
-  forAll (arbitrary :: Gen (NonNegative Integer)) $ \(NonNegative i) ->
-      (hammingBits `on` B.gray) i (i+1) == 1
+prop_bits_gray_succ_Word = label "hamming x (x+1) == 1 [Word]" $
+  forAll (arbitrary :: Gen Word) $ \w ->
+      (hammingBits `on` B.gray) w (w+1) == 1
 
 hammingBits :: (Bits a, Num a) => a -> a -> Int
 hammingBits = hamming `on` L.toList
   prop_bits_id .&.
   prop_bits_same_as_lists .&.
   prop_bits_gray_succ_Int .&.
-  prop_bits_gray_succ_Integer
+  prop_bits_gray_succ_Word
 
 all_props =
   prop_lists .&. prop_bits

File gray-code.cabal

 -- The package version. See the Haskell package versioning policy
 -- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for
 -- standards guiding when and how versions should be incremented.
-Version:             0.2.2
+Version:             0.3
 
 -- A short (one-line) description of the package.
 Synopsis:            Gray code encoder/decoder.
    .
    This package allows to convert numbers to one of the possible Gray
    codes and back. Two binary representations of a number are supported:
-   @[Bool]@ and types of @Bits@ type class.
-   @Bits@ is the default implementation.
+   @[Bool]@ and types of @FiniteBits@ type class.
+   @FiniteBits@ is the default implementation.
 
 -- URL for the project homepage or repository.
 Homepage:            http://bitbucket.org/astanin/hs-gray-code
 
   -- Packages needed in order to build this package.
   Build-depends:
-     base >= 3 && < 5
+     base >= 4.7 && < 5
 
   -- Modules not exported by this package.
   -- Other-modules: