============================= REDUCERON MEMO 27 Rotation and one-hot addition Matthew N, 25 June 2009 ============================= Bit-vector rotation and one-hot addition are two useful operations in the implementation of the Reduceron's wide stack. The former operation rotates a sequence of bits by a given number of places, and the latter sums two one-hot numbers to give a resulting one-hot number. We define these two operations only to learn that they are one and the same! In the process we find an interesting property of each operation which by equivalence also holds, somewhat surprisingly, for the other. This memo is literate Haskell. > import List (transpose) Unary numbers ------------- A one-hot non-negative integer n is a list of booleans whose (n+1)th element is True and whose other elements are all False. For example, the one-hot numbers of width 4, in increasing order, beginning with 0, are: [True , False, False, False], [False, True , False, False], [False, False, True , False], [False, False, False, True ]. The negative one-hot numbers are defined such that for all non-zero integers n, negate n = reverse n. Rotation -------- The function rot, rot :: [Bool] -> [Bool] -> [Bool] is to be defined such that rot n xs rotates the list of bits xs to the right by n places where n is a one-hot number. It can be assumed that n and xs have the same length. Unary addition -------------- The function add, add :: [Bool] -> [Bool] -> [Bool] is to be defined such that add n m returns the one-hot representation of (i+j) `mod` w, where i and j are the integers represented by n and m, and w is the width of both n and m. Challenge --------- Please feel free to define these operations for yourself. To ensure that the functions are realisable as digital circuits, case analysis on boolean values should be avoided; only standard boolean connectives such as not, && and || should be used to process the boolean-valued inputs. Solution: Rotation ------------------ To rotate a bit-vector xs to the right by n places, we iteratively compute all the right-rotations of xs and select nth one. > left [] = [] > left (x:xs) = xs ++ [x] > right = reverse . left . reverse > rights xs = take (length xs) (iterate right xs) > dot xs ys = or (zipWith (&&) xs ys) > sel n xs = map (dot n) (transpose xs) > rot n xs = sel n (rights xs) Solution: Addition ------------------ To compute the nth bit of the one-hot sum, we find all the combinations of pairs of input bits that add to n, and return the sum of the products of this list of pairs. > split [] = [] > split (x:xs) = ([x], xs) : [(x:y, z) | (y, z) <- split xs] > tac (xs, ys) = reverse xs ++ reverse ys > add a b = map (dot a) (map tac (split b)) Equivalence ----------- After defining these two functions it becomes apparant that if map tac . split = transpose . rights then the two are equivalent, which is indeed the case. Observation 1: The add operation, by its specification, is commutative, and if it is equivalent to rot, then rot must also be commutative. Observation 2: The add operation, by its specification, only needs to rotate one-hot numbers, but if it is equivalent to rot, then it must be able to rotate arbitrary bit-vectors too. These two facts were quite surprising to learn! Array rotation -------------- The following function, used to implement the wide stack, rotates a list of bit-vectors to the right by n places, where n is a given one-hot number. > rotate :: [Bool] -> [[Bool]] -> [[Bool]] > rotate n = transpose . map (add n) . transpose