adventofcode/2016/day09/main.go
2016-12-16 16:21:15 -06:00

125 lines
2.9 KiB
Go

package main
import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
"log"
"os"
"regexp"
"strconv"
)
var re *regexp.Regexp
func main() {
var input []byte
var noDecompress bool
var printSize bool
if len(os.Args) > 1 {
for i := 1; i < len(os.Args); i++ {
if os.Args[i][0] != '-' {
// Assume it's a filename
// Read from the given filename
input = fileToByteSlice(os.Args[1])
} else {
switch os.Args[i] {
case "-size":
printSize = true
case "-dry":
// -dry implies printSize
printSize = true
noDecompress = true
}
}
}
}
if len(input) == 0 {
// Must be stdin
input = stdinToByteSlice()
}
input = bytes.TrimSpace(input)
// re matches compression markers and submatches the values we care about
re = regexp.MustCompile(`\((\d*)x(\d*)\)`)
if noDecompress {
cnt := depthDecompressLength(input)
fmt.Println("Total Decompressed Bytes:", cnt)
os.Exit(0)
}
output := depthDecompress(input)
fmt.Println(string(output))
if printSize {
fmt.Println("Total Decompressed Bytes:", len(output))
}
}
// depthDecompressLength counts how many uncompressed bytes are
// in the byte slice by unravelling the compression levels
// recursively
func depthDecompressLength(cmp []byte) uint64 {
mrkParts := re.FindStringSubmatch(string(cmp))
if len(mrkParts) < 3 {
// No compressionmarker, just return cmp length
return uint64(len(cmp))
}
marker, mrkBytes, mrkDupe := []byte(mrkParts[0]), atoi(mrkParts[1]), atoi(mrkParts[2])
mrkPos := bytes.Index(cmp, marker)
if mrkPos > 0 {
cmp = cmp[mrkPos:]
}
recurBytes := bytes.TrimPrefix(cmp, marker)[:mrkBytes]
remainder := bytes.TrimPrefix(cmp, append(marker, recurBytes...))
return uint64(mrkPos) + (depthDecompressLength(recurBytes) * uint64(mrkDupe)) + depthDecompressLength(remainder)
}
func depthDecompress(cmp []byte) []byte {
mrkParts := re.FindStringSubmatch(string(cmp))
if len(mrkParts) < 3 {
// No compression marker, just return cmp length
return cmp
}
marker, mrkBytes, mrkDupe := []byte(mrkParts[0]), atoi(mrkParts[1]), atoi(mrkParts[2])
mrkPos := bytes.Index(cmp, marker)
var ret []byte
if mrkPos > 0 {
ret = cmp[:mrkPos]
cmp = cmp[mrkPos:]
}
recurBytes := bytes.TrimPrefix(cmp, marker)[:mrkBytes]
remainder := bytes.TrimPrefix(cmp, append(marker, recurBytes...))
ret = append(ret, bytes.Repeat(depthDecompress(recurBytes), mrkDupe)...)
return append(ret, depthDecompress(remainder)...)
}
func fileToByteSlice(fn string) []byte {
var c []byte
var err error
c, err = ioutil.ReadFile(fn)
if err != nil {
fmt.Println("Unable to read file: " + fn)
os.Exit(1)
}
return c
}
func stdinToByteSlice() []byte {
var input string
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
input += scanner.Text()
}
return []byte(input)
}
func atoi(i string) int {
var ret int
var err error
if ret, err = strconv.Atoi(i); err != nil {
log.Fatal("Invalid Atoi")
}
return ret
}