import Lava
import Lava.Patterns
import Lava.Arithmetic





type Bit = Signal Bool   -- convenient abbreviation



-- Zero detect   Exercise ------------------------------------------------------
zero_detect as = inv nz
  where
    nz = nz_detect0 as

nz_detect0 [] = low
nz_detect0 (a:as) = out
  where
    out = or2(a,out2)
    out2 = nz_detect0 as

zero_detect1 = lin or2 ->- inv

nz_detect1 []  = low
nz_detect1 [a] = a
nz_detect1 as  = out
  where
    (as1,as2) = halveList as
    out1 = nz_detect1 as1
    out2 = nz_detect1 as2
    out  = or2(out1,out2)

nz_detect2 [] = low
nz_detect2 [a] = a
nz_detect2 as = circ as
  where
    circ = halveList ->- (nz_detect2 -|- nz_detect2) ->- or2


-- capturing the pattern as a reusable combinator
binTree :: ((a,a) -> a) -> [a] -> a
binTree c []  = error "binTree of empty list"
binTree c [a] = a
binTree c as  = circ as
  where
    circ = halveList ->- (binTree c -|- binTree c) ->- c

nz_detect3 = binTree or2

-- Note that gates are also generic (called andl, xorl etc. (l for list)
-- (but generated netlist uses 2 input gates
nz_detect4 = orl



-- Simple delay analysis -------------------------------------------------------
-- Depth computations

ldepth :: (Signal Int, Signal Int) -> Signal Int
ldepth (a,b) = max a b + 1

dtstTree n = simulate (binTree ldepth) (replicate n 0)

dtstT n = map dtstTree [1..n]

-- from Lecture 2
red :: ((a,b) -> a) -> (a, [b]) -> a
red f (a,[])      = a
red f (a, (b:bs)) = red f (f(a,b), bs)

lin f (a:as) = red f (a,as)
lin _ []     = error "lin: empty list"

dtstLin n = simulate (lin ldepth) (replicate n 0)

dtstL n = map dtstLin [1..n]






-- modelling delay in a full adder ---------------------------------------------

fAddI (a1s, a2s, a3s, a1c, a2c, a3c) (a1,(a2,a3)) = (s,cout)
  where
    s    =  maximum [a1s+a1, a2s+a2, a3s+a3]
    cout =  maximum [a1c+a1, a2c+a2, a3c+a3]

fI = fAddI (20,20,10,10,10,10)

-- from first lecture but generalising the type!
rcAdder2 :: ((a,(a,a)) -> (a,a)) -> (a,([a],[a])) -> ([a], a)
rcAdder2 fadd (c0, (as, bs)) = (sum, cOut)
  where
    (sum, cOut) = row fadd (c0, zipp (as,bs))


rcdeltst1 = simulate (rcAdder2 fI)
                     (0 :: Signal Int, (replicate 10 0, replicate 10 0))


-- For circuits without feedback, one can also just compute delays
-- directly in Haskell as below. One shouldn't try to mix the two approaches :)

-- rcdeltst = rcAdder2 fI (0, (replicate 10 0, replicate 10 0))

-- This general approach is called Non Standard Intrpretation -- replacing
-- circuit components by others that do analyses, and then running (simulating)
-- the circuit to get analysis results.