(defun read-data (path)
(with-open-file (stream path)
(loop for line = (read-line stream nil)
while line collect line)))
(defparameter *input-source*
(cadr *posix-argv*))
;; Useful in testing
;; (defparameter *input-source* "example_input")
;; (defparameter *input-source* "input")
(defparameter *data*
(coerce (read-data *input-source*) 'vector))
(defun most-common (data column)
(loop for row across data
counting (char= #\0 (aref row column)) into zeroes
counting (char= #\1 (aref row column)) into ones
finally (return (if (> zeroes ones) #\0 #\1))))
(defun least-common (data column)
(invert-bit (most-common data column)))
(defun invert-bit (bit)
(if (char= bit #\0) #\1 #\0))
(defun invert (bits)
(map 'string #'invert-bit bits))
(defun gamma-rate (data)
(loop for column from 0 below (length (aref data 0))
collect (most-common data column)))
(defun parse-bitfield (bits)
(parse-integer (coerce bits 'string) :radix 2))
(defun part1 (data)
(let ((gamma-bits (gamma-rate data)))
(* (parse-bitfield gamma-bits)
(parse-bitfield (invert gamma-bits)))))
(defun with-bit-at-position (data position bit)
(remove-if-not (lambda (row) (char= (aref row position) bit))
data))
(defun part2-filter (data &key (most-common-f #'most-common))
(loop for d = data then (with-bit-at-position d column (funcall most-common-f d column))
for column from 0 below (length (aref data 0))
until (= 1 (length d))
finally (return (aref d 0))))
(defun part2 (data)
(let ((oxygen (part2-filter data))
(co2 (part2-filter data :most-common-f #'least-common)))
(* (parse-bitfield oxygen)
(parse-bitfield co2))))
(format t "Part 1: ~A~%" (part1 *data*))
(format t "Part 2: ~A~%" (part2 *data*))