Read / write Haskell Integer in two additional views

I need to read and write integers in a way that is compatible with what the Java BigInteger class does with it :

Returns an array of bytes containing a two-component representation of this BigInteger. The byte array will be in byte byte order: the most significant byte is in the null element. The array will contain the minimum number of bytes required to represent this BigInteger, including at least one signed bit, which is equal to (ceil ((this.bitLength () + 1) / 8)).

Unfortunately, this excludes what it offers Data.Binary. Is there something effective to convert ByteStringIntegerafter this convention somewhere in the libraries? If not, how can this be done?

Based on the answer of Thomas M. Dubuison (and the subsequent discussion), I am currently

i2bs :: Integer -> B.ByteString
i2bs x
   | x == 0 = B.singleton 0
   | x < 0 = i2bs $ 2 ^ (8 * bytes) + x
   | otherwise = B.reverse $ B.unfoldr go x
   where
      bytes = (integerLogBase 2 (abs x) + 1) `quot` 8 + 1
      go i = if i == 0 then Nothing
                       else Just (fromIntegral i, i `shiftR` 8)

integerLogBase :: Integer -> Integer -> Int
integerLogBase b i =
     if i < b then
        0
     else
        -- Try squaring the base first to cut down the number of divisions.
        let l = 2 * integerLogBase (b*b) i
            doDiv :: Integer -> Int -> Int
            doDiv i l = if i < b then l else doDiv (i `div` b) (l+1)
        in  doDiv (i `div` (b^l)) l

In more detail than I had hoped, the function is still lacking bs2i.

+5
source share
3 answers

Just steal subroutines to i2bsand bs2ifrom crypto api and give them a little modification:

import Data.ByteString as B

-- |@i2bs bitLen i@ converts @i@ to a 'ByteString'
i2bs :: Integer -> B.ByteString
i2bs = B.reverse . B.unfoldr (\i' -> if i' == 0 then Nothing
                                                else Just (fromIntegral i', i' `shiftR` 8))


-- |@bs2i bs@ converts the 'ByteString' @bs@ to an 'Integer' (inverse of 'i2bs')
bs2i :: B.ByteString -> Integer
bs2i = B.foldl' (\i b -> (i `shiftL` 8) + fromIntegral b) 0 . B.reverse

You can make this a bit more efficient by pre-determining the bit size and using the original i2bsone to build the byte sequence in order (saving you the cost of the reverse).

(EDIT: , Java, , ).

+6

, , . , :

bs2i :: B.ByteString -> Integer
bs2i b
   | sign = go b - 2 ^ (B.length b * 8)
   | otherwise = go b
   where
      go = B.foldl' (\i b -> (i `shiftL` 8) + fromIntegral b) 0
      sign = B.index b 0 > 127

i2bs :: Integer -> B.ByteString
i2bs x
   | x == 0 = B.singleton 0
   | x < 0 = i2bs $ 2 ^ (8 * bytes) + x
   | otherwise = B.reverse $ B.unfoldr go x
   where
      bytes = (integerLogBase 2 (abs x) + 1) `quot` 8 + 1
      go i = if i == 0 then Nothing
                       else Just (fromIntegral i, i `shiftR` 8)

integerLogBase :: Integer -> Integer -> Int
integerLogBase b i =
     if i < b then
        0
     else
        -- Try squaring the base first to cut down the number of divisions.
        let l = 2 * integerLogBase (b*b) i
            doDiv :: Integer -> Int -> Int
            doDiv i l = if i < b then l else doDiv (i `div` b) (l+1)
        in  doDiv (i `div` (b^l)) l

, - - , .: -)

+1

, . , , .

i2bs :: Integer -> B.ByteString
i2bs x = B.reverse . B.unfoldr (fmap go) . Just $ changeSign x
  where
    changeSign :: Num a => a -> a
    changeSign | x < 0     = subtract 1 . negate
               | otherwise = id
    go :: Integer -> (Word8, Maybe Integer)
    go x = ( b, i )
      where
        b = changeSign (fromInteger x)
        i | x >= 128  = Just (x `shiftR` 8 )
          | otherwise = Nothing

bs2i :: B.ByteString -> Integer
bs2i xs = changeSign (B.foldl' go 0 xs)
  where
    changeSign :: Num a => a -> a
    changeSign | B.index xs 0 >= 128 = subtract 1 . negate
               | otherwise           = id
    go :: Integer -> Word8 -> Integer
    go i b = (i `shiftL` 8) + fromIntegral (changeSign b)
0

All Articles