module Mux where

import Data.List

type Bit             =  Bool
  
mux                  :: [Bit] -> [[Bit]] -> [Bit]
mux sel xs           =  map (tree (||))
                     $  transpose
                     $  zipWith (\s x -> map (s &&) x) sel xs

tree                 :: (a -> a -> a) -> [a] -> a
tree f [x]           =  x
tree f (x:y:ys)      =  tree f (ys ++ [f x y])

decode               :: [Bit] -> [Bit]
decode []            =  [True] 
decode [x]           =  [not x,x]
decode (x:xs)        =  concatMap (\y -> [not x && y,x && y]) rest
  where
    rest             =  decode xs
  
binaryMux            :: [Bit] -> [[Bit]] -> [Bit]
binaryMux sel xs     =  mux (decode sel) xs

num                  :: [Bool] -> Int
num []               =  0
num (a:as)           =  (if a then 1 else 0) + 2 * num as

encode as            =  enc (as ++ replicate n False)
  where
    n                =  2 ^ ulog2 (length as) - length as

enc [_]              =  []
enc as               =  zipWith (||) (enc ls) (enc rs) ++ [tree (||) rs]
  where
    (ls, rs)         =  splitAt (length as `div` 2) as

oneHot []            =  False
oneHot (a:as)        =  if a then not (or as) else oneHot as

log2 n               =  if n == 1 then 0 else 1 + log2 (n `div` 2)

ulog2 n              =  log2 (2*n - 1)

-- Properties

infixr 0 -->
False --> _ = True
True --> x = x

prop_encode as = oneHot as --> (num (encode as) == n)
  where
    n = length (takeWhile not as)

prop_mux (sel, xs) =
      oneHot sel
  &&  length sel == length xs
  &&  all ((== length (head xs)) . length) xs
  --> mux sel xs == xs !! n
  where
    n = length (takeWhile not sel)

prop_encDec as = encode (decode as) == as
