deriving Repr, BEq, Ord, Inhabited
| Literal : Nat → Nat → Packet
| Container : Nat → Nat → List Packet → Packet
partial def flat {α:Type} : List (List α) → List α
| (x::xs)::ys => x::(flat $ xs::ys)
def h2b : Char → List Bin
| '0' => [b0, b0, b0, b0]
| '1' => [b0, b0, b0, b1]
| '2' => [b0, b0, b1, b0]
| '3' => [b0, b0, b1, b1]
| '4' => [b0, b1, b0, b0]
| '5' => [b0, b1, b0, b1]
| '6' => [b0, b1, b1, b0]
| '7' => [b0, b1, b1, b1]
| '8' => [b1, b0, b0, b0]
| '9' => [b1, b0, b0, b1]
| 'A' => [b1, b0, b1, b0]
| 'B' => [b1, b0, b1, b1]
| 'C' => [b1, b1, b0, b0]
| 'D' => [b1, b1, b0, b1]
| 'E' => [b1, b1, b1, b0]
| 'F' => [b1, b1, b1, b1]
def hexaToBinary (x:String) : List Bin :=
flat $ x.toList.map (h2b)
def bin2dec (x:List Bin) : Nat := do
let add : Nat := v * (2 ^ e)
partial def parseLiteral (bread:Nat) (acc:List Bin) : List Bin → (Nat × Nat)
| b0::rs => ( bin2dec (acc ++ rs.take 4), bread+5)
| b1::rs => parseLiteral (bread+5) (acc ++ rs.take 4) (rs.drop 4)
partial def decodeMultiplePackets (f:List Bin → (Packet × List Bin)) : List Bin → List Packet
p :: decodeMultiplePackets f ps
unsafe def decodePackets (bin:List Bin) : (Packet × List Bin) := do
let version := bin2dec $ remaining.take 3
remaining := remaining.drop 3
let packetType := bin2dec $ remaining.take 3
remaining := remaining.drop 3
if packetType == 4 then do
let (literalContent, bread) := parseLiteral 0 [] remaining
(Literal version literalContent, remaining.drop bread)
let lengthType := remaining.head!
remaining := remaining.tail!
let nbits := bin2dec $ remaining.take 15
remaining := remaining.drop 15
let v := (remaining.take nbits)
remaining := remaining.drop nbits
(Container version packetType $ decodeMultiplePackets decodePackets v, remaining)
let npackets := bin2dec $ remaining.take 11
remaining := remaining.drop 11
let (p, rest) := decodePackets remaining
(Container version packetType $ ps, remaining)
unsafe def decode (x:String) : Packet :=
(decodePackets $ hexaToBinary x).fst
partial def totalVersions : Packet → Nat
| (Container v _ xs) => v + (xs.map totalVersions).foldr (·+·) 0
unsafe def main (args : List String) : IO UInt32 :=
let input ← IO.FS.lines "input.txt"
IO.println $ totalVersions $ decode encoded