rme-0.1: src/Data/RME/Vector.hs
{-# LANGUAGE BangPatterns, BlockArguments #-}
{- |
Module : Data.RME.Vector
Copyright : Galois, Inc. 2016
License : BSD3
Maintainer : huffman@galois.com
Stability : experimental
Portability : portable
Operations on big-endian vectors of RME formulas.
-}
module Data.RME.Vector
( RMEV
, eq, ule, ult, sle, slt
, neg, add, sub, mul
, udiv, urem, sdiv, srem
, pmul, pmod, pdiv
, shl, ashr, lshr, ror, rol
, integer
, popcount
, countLeadingZeros
, countTrailingZeros
) where
import Data.RME.Base (RME)
import qualified Data.RME.Base as RME
import qualified Data.Bits as Bits
import Data.Vector (Vector)
import qualified Data.Vector as V
type RMEV = Vector RME
-- | Constant integer literals.
integer :: Int -> Integer -> RMEV
integer width x = V.reverse (V.generate width (RME.constant . Bits.testBit x))
-- | Bitvector equality.
eq :: RMEV -> RMEV -> RME
eq x y = V.foldr RME.conj RME.true (V.zipWith RME.iff x y)
-- | Unsigned less-than-or-equal.
ule :: RMEV -> RMEV -> RME
ule xv yv = go (V.toList xv) (V.toList yv)
where
go (x : xs) (y : ys) =
let z = go xs ys
in RME.xor (RME.conj y z) (RME.conj (RME.compl x) (RME.xor y z))
go _ _ = RME.true
-- | Unsigned less-than.
ult :: RMEV -> RMEV -> RME
ult x y = RME.compl (ule y x)
swap_sign :: RMEV -> RMEV
swap_sign x
| V.null x = x
| otherwise = V.singleton (RME.compl (V.head x)) V.++ V.tail x
-- | Signed less-than-or-equal.
sle :: RMEV -> RMEV -> RME
sle x y = ule (swap_sign x) (swap_sign y)
-- | Signed less-than.
slt :: RMEV -> RMEV -> RME
slt x y = ult (swap_sign x) (swap_sign y)
-- | Big-endian bitvector increment with carry.
increment :: [RME] -> (RME, [RME])
increment [] = (RME.true, [])
increment (x : xs) = (RME.conj x c, RME.xor x c : ys)
where (c, ys) = increment xs
-- | Two's complement bitvector negation.
neg :: RMEV -> RMEV
neg x = V.fromList (snd (increment (map RME.compl (V.toList x))))
-- | 1-bit full adder.
full_adder :: RME -> RME -> RME -> (RME, RME)
full_adder a b c = (carry, RME.xor (RME.xor a b) c)
where carry = RME.xor (RME.conj a b) (RME.conj (RME.xor a b) c)
-- | Big-endian ripple-carry adder.
ripple_carry_adder :: [RME] -> [RME] -> RME -> (RME, [RME])
ripple_carry_adder [] _ c = (c, [])
ripple_carry_adder _ [] c = (c, [])
ripple_carry_adder (x : xs) (y : ys) c = (c'', z : zs)
where (c', zs) = ripple_carry_adder xs ys c
(c'', z) = full_adder x y c'
-- | Two's complement bitvector addition.
add :: RMEV -> RMEV -> RMEV
add x y =
V.fromList (snd (ripple_carry_adder (V.toList x) (V.toList y) RME.false))
-- | Two's complement bitvector subtraction.
sub :: RMEV -> RMEV -> RMEV
sub x y =
V.fromList (snd (ripple_carry_adder (V.toList x) (map RME.compl (V.toList y)) RME.true))
-- | Two's complement bitvector multiplication.
mul :: RMEV -> RMEV -> RMEV
mul x y = V.foldl f zero y
where
zero = V.replicate (V.length x) RME.false
f acc c = V.zipWith (RME.mux c) (add acc2 x) acc2
where acc2 = V.drop 1 (acc V.++ V.singleton RME.false)
-- | Unsigned bitvector division.
udiv :: RMEV -> RMEV -> RMEV
udiv x y = fst (udivrem x y)
-- | Unsigned bitvector remainder.
urem :: RMEV -> RMEV -> RMEV
urem x y = snd (udivrem x y)
-- | Signed bitvector division.
sdiv :: RMEV -> RMEV -> RMEV
sdiv x y = fst (sdivrem x y)
-- | Signed bitvector remainder.
srem :: RMEV -> RMEV -> RMEV
srem x y = snd (sdivrem x y)
udivrem :: RMEV -> RMEV -> (RMEV, RMEV)
udivrem dividend divisor = divStep 0 RME.false initial
where
n :: Int
n = V.length dividend
-- Given an n-bit dividend and divisor, 'initial' is the starting value of
-- the 2n-bit "remainder register" that carries both the quotient and remainder;
initial :: RMEV
initial = integer n 0 V.++ dividend
divStep :: Int -> RME -> RMEV -> (RMEV, RMEV)
divStep i p rr | i == n = (q `shiftL1` p, r)
where (r, q) = V.splitAt n rr
divStep i p rr = divStep (i+1) b (V.zipWith (RME.mux b) (V.fromList s V.++ q) rs)
where rs = rr `shiftL1` p
(r, q) = V.splitAt n rs
-- Subtract the divisor from the left half of the "remainder register"
(b, s) = ripple_carry_adder (V.toList r) (map RME.compl (V.toList divisor)) RME.true
shiftL1 :: RMEV -> RME -> RMEV
shiftL1 v e = V.tail v `V.snoc` e
-- Perform udivrem on the absolute value of the operands. Then, negate the
-- quotient if the signs of the operands differ and make the sign of a nonzero
-- remainder to match that of the dividend.
sdivrem :: RMEV -> RMEV -> (RMEV, RMEV)
sdivrem dividend divisor = (q',r')
where
sign1 = V.head dividend
sign2 = V.head divisor
signXor = RME.xor sign1 sign2
negWhen x c = V.zipWith (RME.mux c) (neg x) x
dividend' = negWhen dividend sign1
divisor' = negWhen divisor sign2
(q, r) = udivrem dividend' divisor'
q' = negWhen q signXor
r' = negWhen r sign1
popcount :: RMEV -> RMEV
popcount bits = if l == 0 then V.empty else (V.replicate (l-w-1) RME.false) <> pcnt
where
l = V.length bits
w = Bits.countTrailingZeros l -- log_2 rounded down, w+1 is enough bits to hold popcount
zs = V.replicate w RME.false
pcnt = foldr1 add xs -- length is w+1
xs = [ zs <> V.singleton b | b <- V.toList bits ]
countTrailingZeros :: RMEV -> RMEV
countTrailingZeros bits = countLeadingZeros (V.reverse bits)
-- Big endian convention means its easier to count leading zeros
countLeadingZeros :: RMEV -> RMEV
countLeadingZeros bits = if l == 0 then V.empty else (V.replicate (l-w-1) RME.false) <> (go 0 (V.toList bits))
where
l = V.length bits
w = Bits.countTrailingZeros l -- log_2 rounded down, w+1 is enough bits to hold count
go :: Integer -> [RME] -> Vector RME
go !i [] = integer (w+1) i
go !i (b:bs) = V.zipWith (RME.mux b) (integer (w+1) i) (go (i+1) bs)
-- | Polynomial multiplication. Note that the algorithm works the same
-- no matter which endianness convention is used. Result length is
-- @max 0 (m+n-1)@, where @m@ and @n@ are the lengths of the inputs.
pmul :: RMEV -> RMEV -> RMEV
pmul x y = V.generate (max 0 (m + n - 1)) coeff
where
m = V.length x
n = V.length y
coeff k = foldr RME.xor RME.false
[ RME.conj (x V.! i) (y V.! j) | i <- [0 .. k], let j = k - i, i < m, j < n ]
-- | Polynomial mod with symbolic modulus. Return value has length one
-- less than the length of the modulus.
-- This implementation is optimized for the (common) case where the modulus
-- is concrete.
pmod :: RMEV -> RMEV -> RMEV
pmod x y = findmsb (V.toList y)
where
findmsb :: [RME] -> RMEV
findmsb [] = V.replicate (V.length y - 1) RME.false -- division by zero
findmsb (c : cs)
| c == RME.true = usemask cs
| c == RME.false = findmsb cs
| otherwise = V.zipWith (RME.mux c) (usemask cs) (findmsb cs)
usemask :: [RME] -> RMEV
usemask m = zext (V.fromList (go (V.length x - 1) p0 z0)) (V.length y - 1)
where
zext v r = V.replicate (r - V.length v) RME.false V.++ v
msize = length m
p0 = replicate (msize - 1) RME.false ++ [RME.true]
z0 = replicate msize RME.false
next :: [RME] -> [RME]
next [] = []
next (b : bs) =
let m' = map (RME.conj b) m
bs' = bs ++ [RME.false]
in zipWith RME.xor m' bs'
go :: Int -> [RME] -> [RME] -> [RME]
go i p acc
| i < 0 = acc
| otherwise =
let px = map (RME.conj (x V.! i)) p
acc' = zipWith RME.xor px acc
p' = next p
in go (i-1) p' acc'
-- | Polynomial division. Return value has length
-- equal to the first argument.
pdiv :: RMEV -> RMEV -> RMEV
pdiv x y = fst (pdivmod x y)
-- Polynomial div/mod: resulting lengths are as in Cryptol.
-- TODO: probably this function should be disentangled to only compute
-- division, given that we have a separate polynomial modulus algorithm.
pdivmod :: RMEV -> RMEV -> (RMEV, RMEV)
pdivmod x y = findmsb (V.toList y)
where
findmsb :: [RME] -> (RMEV, RMEV)
findmsb (c : cs) = muxPair c (usemask cs) (findmsb cs)
findmsb [] = (x, V.replicate (V.length y - 1) RME.false) -- division by zero
usemask :: [RME] -> (RMEV, RMEV)
usemask mask = (q, r)
where
(qs, rs) = pdivmod_helper (V.toList x) mask
z = RME.false
qs' = map (const z) rs ++ qs
rs' = replicate (V.length y - 1 - length rs) z ++ rs
q = V.fromList qs'
r = V.fromList rs'
muxPair :: RME -> (RMEV, RMEV) -> (RMEV, RMEV) -> (RMEV, RMEV)
muxPair c a b
| c == RME.true = a
| c == RME.false = b
| otherwise = (V.zipWith (RME.mux c) (fst a) (fst b), V.zipWith (RME.mux c) (snd a) (snd b))
-- Divide ds by (1 : mask), giving quotient and remainder. All
-- arguments and results are big-endian. Remainder has the same length
-- as mask (but limited by length ds); total length of quotient ++
-- remainder = length ds.
pdivmod_helper :: [RME] -> [RME] -> ([RME], [RME])
pdivmod_helper ds mask = go (length ds - length mask) ds
where
go :: Int -> [RME] -> ([RME], [RME])
go n cs | n <= 0 = ([], cs)
go _ [] = error "Data.AIG.Operations.pdiv: impossible"
go n (c : cs) = (c : qs, rs)
where cs' = mux_add c cs mask
(qs, rs) = go (n - 1) cs'
mux_add :: RME -> [RME] -> [RME] -> [RME]
mux_add c (x : xs) (y : ys) = RME.mux c (RME.xor x y) x : mux_add c xs ys
mux_add _ [] (_ : _ ) = error "pdiv: impossible"
mux_add _ xs [] = xs
-- | Helper for building shift and rotate operations.
-- The callback function is called with: the first argument,
-- the index being filled in the result, and the arithmetic
-- value of the second argument.
bitOp :: (RMEV -> Integer -> Integer -> RME) -> RMEV -> RMEV -> RMEV
bitOp f x y = V.generate w \i -> pick (toInteger i) 0 y'
where
y' = V.toList y
w = length x
pick i j [] = f x i j
pick i j (b:bs) = RME.mux b (pick i (1+2*j) bs) (pick i (2*j) bs)
-- | Bitwise logical left shift. Shifts the bits in the first bit-vector
-- by the unsigned, arithmetic value in the second bit-vector filling
-- in with false bits.
shl :: RMEV -> RMEV -> RMEV
shl = bitOp \x i j ->
let w = length x in
if i + j >= toInteger w then RME.false else x V.! fromInteger (i+j)
-- | Arithmetic logical right shift. Shifts the bits in the first bit-vector
-- by the unsigned, arithmetic value in the second bit-vector filling
-- in with bits matching the first bit (which is treated as a sign bit).
ashr :: RMEV -> RMEV -> RMEV
ashr = bitOp \x i j ->
if i < j then V.head x else x V.! fromInteger (i-j)
-- | Bitwise logical right shift. Shifts the bits in the first bit-vector
-- by the unsigned, arithmetic value in the second bit-vector filling
-- in with false bits.
lshr :: RMEV -> RMEV -> RMEV
lshr = bitOp \x i j ->
if i < j then RME.false else x V.! fromInteger (i-j)
-- | Bitwise left rotation. Rotates the bits in the first bit-vector
-- by the unsigned, arithmetic value in the second bit-vector.
rol :: RMEV -> RMEV -> RMEV
rol = bitOp \x i j ->
let w = length x in
x V.! fromInteger ((i + j) `mod` toInteger w)
-- | Bitwise right rotation. Rotates the bits in the first bit-vector
-- by the unsigned, arithmetic value in the second bit-vector.
ror :: RMEV -> RMEV -> RMEV
ror = bitOp \x i j ->
let w = length x in
x V.! fromInteger ((i - j) `mod` toInteger w)