1

I have two lazy bytestrings that were generated randomly using the DRBG package. I want to interpret these bytestrings as numbers and be able to perform addition on them. So something like a function with signature ByteString -> ByteString -> ByteString.

In JavaScript, I was using the math-buffer package How can I do this in Haskell?

ljedrz
  • 20,316
  • 4
  • 69
  • 97
Hudon
  • 1,636
  • 18
  • 28
  • Your desire is unclear. What does `[255,0] + [1,0]` equal? `[0,1]` for little endian 16 bit number? `[1,0,0]` for a big endian unbounded size number? `[0,0]` for 2 8 bit numbers? If it is the first then you could resurrect the [NumLazyByteString package](http://hackage.haskell.org/package/NumLazyByteString-0.0.0.1/docs/Data-ByteString-Lazy-Num.html) – Thomas M. DuBuisson Oct 02 '16 at 15:38
  • @ThomasM.DuBuisson I get to define how I want my number represented, basically. My constraints are: I'm generating a random bytestring of 256 bits in length. I need the concept of addition and subtraction, such that when I add "a + b = c", I can get "c - b = a". How I decode a number out of the bytestring is up to me, as long as those operations work in a consistent way – Hudon Oct 02 '16 at 16:49
  • Ok, in that case just make `a + _ = a` and `x - _ = x` (aka `const`). When you don't need commutativity you can make the operations much faster. – Thomas M. DuBuisson Oct 02 '16 at 18:03
  • To be more precise: `c - a = b` would need to be true as well, so say: `for all a, b : if a + b = c then c - a = b and c - b = a` – Hudon Oct 02 '16 at 21:11
  • And if you want full context: I'm implementing the FairExchange protocol defined here https://crypto.stanford.edu/~xb/fc12/bitcoin.pdf in section 7. Alice has secrets `a` and Bob has secrets `b` and knows Alice's `a`s. Bob publishes `a + b` (publicly) and Alice is the only one that can then figure out Bob's `b`s (by doing `a + b - a`). I'm considering not using `genBytes` but instead using your `crandom` function to return `Integer`s – Hudon Oct 02 '16 at 21:48
  • ok... I ended up using `crandomRs` to get signed 256-bit Integers and just doing the math on that instead of dealing with bytestrings... – Hudon Oct 02 '16 at 22:47
  • @Hudson. Sounds good. An alternative would be use of xor. `(+) = (-) = B.pack . B.zipWith xor` – Thomas M. DuBuisson Oct 03 '16 at 02:46

2 Answers2

1
  1. Solution 1

It seems that there is no math-buffer-like packages in Haskell, so you would be forced to implement these byte-wise operations yourself and use math-buffer for inspiration. So you would unpack your bytestring to a list of Word8s and then implement addition of a base-256 number, like so:

byteSum :: [Word8] -> [Word8] -> [Word8]
byteSum = byteSum' 0
  where
    byteSum' 0 [] [] = []
    byteSum' 1 [] [] = [1]
    byteSum' carry (x:xs) (y:ys) =
        let v = x + y + carry
        in v : byteSum' (if v < x || v < y then 1 else 0) xs ys

main = do
    let bs1 = ...
    let bs2 = ...
    let sum = pack $ byteSum (unpack bs1) (unpack bs2)

Note this is assuming little-endianness (least significant byte first).

  1. Solution 2

Alternatively, assume this bytestring is a big-endian unsigned integer, convert from ByteString -> Integer and let the Integer to the math, then convert it back:

bstoi :: ByteString -> Integer
bstoi = fromDigits 0 . unpack
  where
    fromDigits n [] = n
    fromDigits n (x:xs) = fromDigits (n * 256 + (toInteger x)) xs

itobs :: Integer -> ByteString
itobs = pack . reverse . toDigits
  where
    toDigits 0 = []
    toDigits x = (fromIntegral $ x `mod` 256) : toDigits (x `div` 256)

main = do
    let bs1 = ...
    let bs2 = ...
    let sum = itobs $ (bstoi bs1) + (bstoi bs2)
Hudon
  • 1,636
  • 18
  • 28
  • Going through lists doesn't seem likely to be terrifically efficient. – dfeuer Oct 01 '16 at 19:41
  • Sure, feel free to give a better answer! I'm trying to opt for simplicity but if there's a simple and more performant solution I'll take it – Hudon Oct 01 '16 at 21:07
  • I don't know about simple. The simplest little-endian approach that seems likely to be reasonable is to index into the bytestrings manually while filling the result. For big-endian ones, a lot of extra effort should be able to make it scream: dig into the bytestring representation to get at the pointers, cast them to `Word#` pointers, and then perform word-wise addition, handling carrying manually with bitwise operations. I think that gets it down to around three arithmetic instructions per 8 bytes. It's also possible that someone has already written a library for this stuff somewhere. – dfeuer Oct 01 '16 at 21:26
  • nice, I'll look into this if I need to speed things up, thank you! It could be either LE or BE (it's my bytestring and it's initially random so I can decide), but I will be doing addition and subtraction so as long as I'm consistent with one I'm good – Hudon Oct 01 '16 at 22:43
  • For little endian (x64) you can take 64 bits out and perform 64 biit addition instead 8 8bit additions. – Luka Rahne Oct 01 '16 at 22:51
0

import qualified Data.ByteString.Lazy as BL

pizdec bl = BL.pack . BL.zipWith (+) bl

maru
  • 11
  • 2