164 lines
3.4 KiB
Nim
164 lines
3.4 KiB
Nim
import os, bitops, sugar
|
|
import std/[strutils, sequtils]
|
|
|
|
type
|
|
ReadResult =
|
|
tuple[value: int, bitsRead: int]
|
|
|
|
ParseResult = int
|
|
|
|
PacketType = enum
|
|
Sum
|
|
Product
|
|
Minimum
|
|
Maximum
|
|
Literal
|
|
GreaterThan
|
|
LessThan
|
|
Equal
|
|
|
|
|
|
proc hexToBin(hex: char): int {.inline.} =
|
|
case hex:
|
|
of '0': 0b0000
|
|
of '1': 0b0001
|
|
of '2': 0b0010
|
|
of '3': 0b0011
|
|
of '4': 0b0100
|
|
of '5': 0b0101
|
|
of '6': 0b0110
|
|
of '7': 0b0111
|
|
of '8': 0b1000
|
|
of '9': 0b1001
|
|
of 'A', 'a': 0b1010
|
|
of 'B', 'b': 0b1011
|
|
of 'C', 'c': 0b1100
|
|
of 'D', 'd': 0b1101
|
|
of 'E', 'e': 0b1110
|
|
of 'F', 'f': 0b1111
|
|
else: 0
|
|
|
|
template readBits(packet: string, numBits: int): ReadResult =
|
|
let bytesToRead = (((bitPos mod 4) + numBits) div 4) + 1
|
|
var skipBits = bitPos mod 4
|
|
var numBitsToRead = numBits
|
|
var res: ReadResult
|
|
|
|
var i = 0
|
|
while numBitsToRead > 0 and i < bytesToRead and ((bitPos div 4) + i) < packet.len:
|
|
var bin = packet[(bitPos div 4) + i].hexToBin
|
|
|
|
var currentBit = 4
|
|
|
|
case skipBits:
|
|
of 1: currentBit = 3
|
|
of 2: currentBit = 2
|
|
of 3: currentBit = 1
|
|
else: discard
|
|
|
|
skipBits = 0
|
|
|
|
while currentBit > 0 and numBitsToRead > 0:
|
|
let currentBVal = bin.testBit(currentBit - 1).int
|
|
res.value += currentBVal shl (numBitsToRead - 1)
|
|
res.bitsRead += 1
|
|
currentBit -= 1
|
|
numBitsToRead -= 1
|
|
|
|
i.inc
|
|
|
|
bitPos += res.bitsRead
|
|
res
|
|
|
|
template readLiteral(packet: string): ReadResult =
|
|
var
|
|
indicator = packet.readBits(1)
|
|
nextSection: ReadResult
|
|
res: ReadResult
|
|
|
|
res.bitsRead += indicator.bitsRead
|
|
|
|
while indicator.value == 1:
|
|
nextSection = packet.readBits(4)
|
|
|
|
res.value = res.value shl 4
|
|
res.value += nextSection.value
|
|
res.bitsRead += nextSection.bitsRead
|
|
|
|
indicator = packet.readBits(1)
|
|
res.bitsRead += indicator.bitsRead
|
|
|
|
# read the last line
|
|
nextSection = packet.readBits(4)
|
|
|
|
res.value = res.value shl 4
|
|
res.value += nextSection.value
|
|
res.bitsRead += nextSection.bitsRead
|
|
|
|
res
|
|
|
|
proc parsePacket(packet: string, bitPos: var int, verSum: var int): ParseResult =
|
|
let
|
|
version = packet.readBits(3)
|
|
packetType = packet.readBits(3)
|
|
|
|
verSum += version.value
|
|
|
|
var operation: (int, int) -> int
|
|
|
|
case packetType.value.PacketType:
|
|
of Literal:
|
|
# Read Literal value
|
|
return packet.readLiteral().value
|
|
of Sum:
|
|
operation = (x, y: int) => x + y
|
|
of Product:
|
|
operation = (x, y: int) => x * y
|
|
of Minimum:
|
|
operation = (x, y: int) => min(x, y)
|
|
of Maximum:
|
|
operation = (x, y: int) => max(x, y)
|
|
of GreaterThan:
|
|
operation = (x, y: int) => (x > y).int
|
|
of LessThan:
|
|
operation = (x, y: int) => (x < y).int
|
|
of Equal:
|
|
operation = (x, y: int) => (x == y).int
|
|
|
|
let lengthTypeId = packet.readBits(1)
|
|
var acc: seq[int]
|
|
|
|
if lengthTypeId.value == 0:
|
|
# 15 bit mode
|
|
let subPacketLen = packet.readBits(15).value
|
|
var
|
|
bitsRead = 0
|
|
initialBitPos = bitPos
|
|
|
|
while bitsRead < subPacketLen:
|
|
acc.add packet.parsePacket(bitPos, verSum)
|
|
bitsRead = bitPos - initialBitPos
|
|
|
|
else:
|
|
# 11 bit mode
|
|
let numSubPackets = packet.readBits(11).value
|
|
for i in 0 ..< numSubPackets:
|
|
acc.add packet.parsePacket(bitPos, verSum)
|
|
|
|
return acc.foldl(operation(a, b))
|
|
|
|
proc main() =
|
|
let
|
|
fileName = paramStr(1)
|
|
var packet: string
|
|
try:
|
|
packet = fileName.readFile().strip()
|
|
except IOError:
|
|
packet = paramStr(1)
|
|
|
|
var bitPos = 0
|
|
var verSum = 0
|
|
|
|
echo packet.parsePacket(bitPos, verSum)
|
|
|
|
main()
|