[download] []
 
(ns advent-of-code-2021.day8                                                                                                  
  (:require  [clojure.java.io :as io]                                                                                         
             [clojure.string :as str]) 
                                                                                                                              
;; part 1 and part 2                                                                                                          
(let [parse-values (fn [values]                                                                                               
                     (->> values                                                                                              
                          str/trim                                                                                            
                          (#(str/split % #"\s+"))                                                                             
                          seq                                                                                                 
                          (mapv (fn [v]                                                                                       
                                  (->> v                                                                                      
                                       seq                                                                                    
                                       (map (comp keyword str))                                                               
                                       ((partial into #{})))))))                                                              
                                                                                                                              
      ;; input is a mapping from signals (set of sets) to outputs (vector of sets).                                           
      ;; For entries with the same set of signals the four outputs are appended.                                              
      input (->> "day-8/input.txt"                                                                                            
                 io/resource                                                                                                  
                 slurp                                                                                                        
                 str/split-lines                                                                                              
                 (map #(str/split % #"[|]"))                                                                                  
                 (reduce (fn [m [signals-str output-str]]                                                                     
                           #_(assoc m signals-str output-str)                                                                 
                           (let [signals (into #{} (parse-values signals-str))                                                
                                 output (parse-values output-str)]                                                            
                             (update m signals (fn [v]                                                                        
                                                 (if v                                                                        
                                                   (into v output)                                                            
                                                   output))))) {}))                                                           
                                                                         

      ;; Frequencies                                                                                                          
      ;;   8  6  8  7  4  9  7   ;; Digit IDs                                                                                 
      digits {0 #{:a :b :c    :e :f :g}  ;; 8+6+8+4+9+7 = 42                                                                  
              1 #{      :c       :f   }  ;; 8+9         = 17                                                                  
              2 #{:a    :c :d :e    :g}  ;; 8+8+7+4+7   = 34                                                                  
              3 #{:a    :c :d    :f :g}  ;; 8+8+7+9+7   = 39                                                                  
              4 #{   :b :c :d    :f   }  ;; ...                                                                               
              5 #{:a :b    :d    :f :g}                                                                                       
              6 #{:a :b    :d :e :f :g}                                                                                       
              7 #{:a    :c       :f   }                                                                                       
              8 #{:a :b :c :d :e :f :g}                                                                                       
              9 #{:a :b :c :d    :f :g}}                                                                                      
                                                                                                                              
      calc-segment-frequencies (fn [signals]                                                                                  
                                 (frequencies (mapcat identity signals)))                                                     
                                                                                                                              
      digit-id (fn [signal segment-frequencies]                                                                               
                 (apply + (map segment-frequencies signal)))                                                                  
                                                                                                                              
      digit-id-to-digit  (let [segment-frequencies (calc-segment-frequencies (vals digits))]                                  
                           (into {} (map (juxt #(digit-id (val %) segment-frequencies) key) digits)))                         
                                                                                                                              
      count-unique-outputs (count (mapcat (fn [entry]                                                                         
                                            (filter (fn [v]                                                                   
                                                      (#{2 3 7 4} (count v))) (val entry))) input))                           
      output-to-sums  (fn [output]                                                                                            
                        (->> output                                                                                           
                             (partition 4)                                                                                    
                             (map (fn [nums]                                                                                  
                                    (reduce (fn [s d]                                                                         
                                              (+  (* s 10) d)) 0 nums))))) 
      sum-of-decoded-entry-outputs (->> input                                                                                 
                                        (reduce-kv (fn [r signals output]                                                     
                                                     (let [segment-frequencies (calc-segment-frequencies signals)]            
                                                       (into r (->> output                                                    
                                                                    (map (fn [signal] (-> signal                              
                                                                                          (digit-id segment-frequencies)      
                                                                                          digit-id-to-digit)))                
                                                                    output-to-sums))))                                        
                                                   [])                                                                        
                                        (apply +))]                                                                           
  {:part-1 count-unique-outputs                                                                                               
   :part-2 sum-of-decoded-entry-outputs})