Part 1

I first thought about using integers, with two bitmasks - one that will be OR’d (to overwrite 1’s) and the other that will be AND’d (to overwrite 0’s), but then I have to truncate to 36 bits. So it’s probably easier (for now) to just deal with the string representations, taking advantage of the bitstring() builtin.

tok = split(line)
if tok[1] == "mask"
    mask = tok[3]
else
    nstr = bitstring(parse(Int64, tok[3]))[64-36+1:end]
    fstr = join([mask[x] != 'X' ? mask[x] : nstr[x] for x in 1:36])
    mem[addr] = parse(Int, fstr, base=2)
end

Part 2

Conceptually simple: for each X generate two addresses, and should be straightforward to implement recursively. I start by moving the part1-specific logic out of readinput() into a new function, so I can reuse the reading/parsing logic. The recursive function can undoubtably be improved, but it works.

function maskaddr(addr, mask)
    if length(addr) == 0 || length(mask) == 0
        [""]
    else
        f(x) = map(y->join([x, y]), maskaddr(addr[2:end], mask[2:end]))
        if mask[1] == '0'
            f(addr[1])
        elseif mask[1] == '1'
            f('1')
        else
            [f('0'); f('1')]
        end
    end
end
@test maskaddr("101010","X1001X") == ["011010","011011","111010","111011"]

Extra Credit

Common Lisp

A fairly straightforward re-implementation of the Julia solution, although it required some more involved parsing due to the limited standard library, notably no split function to tokenize strings or regex support1. I wrote a naive tokenizer function as an exercise, and extracted the address with a simple function that extracts and parses the digit characters from the string:

(defun parse-address (str)
  (let ((start (position-if #'digit-char-p str)))
    (parse-integer (subseq str start (position-if-not #'digit-char-p str :start start)))))

I used loop to simultaneously iterate over the value (represented in binary in a string) and mask:

(defun masked-value (mask value)
  (concatenate
   'string
   (loop for nb across (to-binary value)
         for mb across mask
         collect (if (char= mb #\X) nb mb))))

I wrote a couple helper functions to convert to and from binary string representation:

(defun to-binary (n)
  (format nil "~36,'0B" n))

(defun from-binary (str)
  (parse-integer str :radix 2))

  1. There’s no shortage of libraries available for string and regex in Quicklisp (or even uiop:string-split which is included in SBCL), but I try to limit myself to base language plus standard library for these problems. ↩︎


Next: Advent of Code 2020 Day 15
/Posts
/Photos
#adventofcode (25)
#apl (5)
#baking (3)
#biggreenegg (9)
#bike (1)
#chia (5)
#data-visualization (8)
#development (48)
#devops (4)
#docker (1)
#electronics (7)
#elixir (1)
#engineering (9)
#food (13)
#golang (1)
#home-improvement (3)
#julia (19)
#keto (1)
#keyboards (4)
#lisp (8)
#meta (1)
#monitoring (10)
#photos (13)
#races (2)
#racket (1)
#recipes (6)
#run (2)
#rust (1)
#swim (1)
#woodworking (12)