package net.sinceat.aoc2020.day10

fun main() {
    println(differences(parseFile("testinput.txt")))
    println(differences(parseFile("input.txt")))

    println(with(Cached()) { parseFile("testinput.txt").combinations() })
    println(
        with(Cached()) {
            val result = parseFile("input.txt").combinations()
            result
        }
    )
}

fun parseFile(fileName: String): List<Int> {
    return ClassLoader.getSystemResourceAsStream(fileName)!!.bufferedReader().useLines { lines ->
        lines.filter(String::isNotBlank).map(String::toInt).sorted().toList()
    }
}

fun differences(input: List<Int>): Int {
    var oneDiffs = 0
    var threeDiffs = 1
    var lastJoltage = 0
    for (adapterJoltage in input) {
        when (adapterJoltage - lastJoltage) {
            1 -> oneDiffs++
            3 -> threeDiffs++
        }
        lastJoltage = adapterJoltage
    }
    return oneDiffs * threeDiffs
}

class Cached {

    private val cache = mutableMapOf<Pair<List<Int>, Int>, Long>()

    fun List<Int>.combinations(last: Int = 0): Long {
        return cache.computeIfAbsent(Pair(this, last)) {
            if (isEmpty()) {
                return@computeIfAbsent 0
            }
            val head = first()
            val diff = head - last
            if (diff > 3) {
                return@computeIfAbsent 0
            }
            val tail = subList(1, size)
            if (tail.isEmpty()) {
                return@computeIfAbsent 1
            }
            return@computeIfAbsent tail.combinations(last) + tail.combinations(head)
        }
    }
}

Compiled from "App.kt"
public final class net.sinceat.aoc2020.day10.AppKt {
  public static final void main();
    Code:
       0: ldc           #8                  // String testinput.txt
       2: invokestatic  #12                 // Method parseFile:(Ljava/lang/String;)Ljava/util/List;
       5: invokestatic  #16                 // Method differences:(Ljava/util/List;)I
       8: istore_0
       9: getstatic     #22                 // Field java/lang/System.out:Ljava/io/PrintStream;
      12: iload_0
      13: invokevirtual #28                 // Method java/io/PrintStream.println:(I)V
      16: ldc           #30                 // String input.txt
      18: invokestatic  #12                 // Method parseFile:(Ljava/lang/String;)Ljava/util/List;
      21: invokestatic  #16                 // Method differences:(Ljava/util/List;)I
      24: istore_0
      25: getstatic     #22                 // Field java/lang/System.out:Ljava/io/PrintStream;
      28: iload_0
      29: invokevirtual #28                 // Method java/io/PrintStream.println:(I)V
      32: new           #32                 // class net/sinceat/aoc2020/day10/Cached
      35: dup
      36: invokespecial #35                 // Method net/sinceat/aoc2020/day10/Cached."<init>":()V
      39: astore_1
      40: iconst_0
      41: istore_2
      42: aload_1
      43: ldc           #8                  // String testinput.txt
      45: invokestatic  #12                 // Method parseFile:(Ljava/lang/String;)Ljava/util/List;
      48: iconst_0
      49: iconst_1
      50: aconst_null
      51: invokestatic  #39                 // Method net/sinceat/aoc2020/day10/Cached.combinations$default:(Lnet/sinceat/aoc2020/day10/Cached;Ljava/util/List;IILjava/lang/Object;)J
      54: lstore_0
      55: getstatic     #22                 // Field java/lang/System.out:Ljava/io/PrintStream;
      58: lload_0
      59: invokevirtual #42                 // Method java/io/PrintStream.println:(J)V
      62: new           #32                 // class net/sinceat/aoc2020/day10/Cached
      65: dup
      66: invokespecial #35                 // Method net/sinceat/aoc2020/day10/Cached."<init>":()V
      69: astore_1
      70: iconst_0
      71: istore_2
      72: aload_1
      73: ldc           #30                 // String input.txt
      75: invokestatic  #12                 // Method parseFile:(Ljava/lang/String;)Ljava/util/List;
      78: iconst_0
      79: iconst_1
      80: aconst_null
      81: invokestatic  #39                 // Method net/sinceat/aoc2020/day10/Cached.combinations$default:(Lnet/sinceat/aoc2020/day10/Cached;Ljava/util/List;IILjava/lang/Object;)J
      84: lstore_3
      85: lload_3
      86: lstore_0
      87: getstatic     #22                 // Field java/lang/System.out:Ljava/io/PrintStream;
      90: lload_0
      91: invokevirtual #42                 // Method java/io/PrintStream.println:(J)V
      94: return

  public static final java.util.List<java.lang.Integer> parseFile(java.lang.String);
    Code:
       0: aload_0
       1: ldc           #56                 // String fileName
       3: invokestatic  #62                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: invokestatic  #68                 // Method java/lang/ClassLoader.getSystemResourceAsStream:(Ljava/lang/String;)Ljava/io/InputStream;
      10: dup
      11: invokestatic  #72                 // Method kotlin/jvm/internal/Intrinsics.checkNotNull:(Ljava/lang/Object;)V
      14: astore_1
      15: getstatic     #78                 // Field kotlin/text/Charsets.UTF_8:Ljava/nio/charset/Charset;
      18: astore_2
      19: new           #80                 // class java/io/InputStreamReader
      22: dup
      23: aload_1
      24: aload_2
      25: invokespecial #83                 // Method java/io/InputStreamReader."<init>":(Ljava/io/InputStream;Ljava/nio/charset/Charset;)V
      28: checkcast     #85                 // class java/io/Reader
      31: astore_3
      32: sipush        8192
      35: istore        4
      37: aload_3
      38: instanceof    #87                 // class java/io/BufferedReader
      41: ifeq          51
      44: aload_3
      45: checkcast     #87                 // class java/io/BufferedReader
      48: goto          61
      51: new           #87                 // class java/io/BufferedReader
      54: dup
      55: aload_3
      56: iload         4
      58: invokespecial #90                 // Method java/io/BufferedReader."<init>":(Ljava/io/Reader;I)V
      61: checkcast     #85                 // class java/io/Reader
      64: astore_1
      65: nop
      66: iconst_0
      67: istore_2
      68: aload_1
      69: astore_3
      70: sipush        8192
      73: istore        4
      75: aload_3
      76: instanceof    #87                 // class java/io/BufferedReader
      79: ifeq          89
      82: aload_3
      83: checkcast     #87                 // class java/io/BufferedReader
      86: goto          99
      89: new           #87                 // class java/io/BufferedReader
      92: dup
      93: aload_3
      94: iload         4
      96: invokespecial #90                 // Method java/io/BufferedReader."<init>":(Ljava/io/Reader;I)V
      99: checkcast     #92                 // class java/io/Closeable
     102: astore_3
     103: aconst_null
     104: astore        4
     106: nop
     107: aload_3
     108: checkcast     #87                 // class java/io/BufferedReader
     111: astore        5
     113: iconst_0
     114: istore        6
     116: aload         5
     118: invokestatic  #98                 // Method kotlin/io/TextStreamsKt.lineSequence:(Ljava/io/BufferedReader;)Lkotlin/sequences/Sequence;
     121: astore        7
     123: iconst_0
     124: istore        8
     126: aload         7
     128: getstatic     #104                // Field net/sinceat/aoc2020/day10/AppKt$parseFile$1$1.INSTANCE:Lnet/sinceat/aoc2020/day10/AppKt$parseFile$1$1;
     131: checkcast     #106                // class kotlin/jvm/functions/Function1
     134: invokestatic  #112                // Method kotlin/sequences/SequencesKt.filter:(Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence;
     137: getstatic     #117                // Field net/sinceat/aoc2020/day10/AppKt$parseFile$1$2.INSTANCE:Lnet/sinceat/aoc2020/day10/AppKt$parseFile$1$2;
     140: checkcast     #106                // class kotlin/jvm/functions/Function1
     143: invokestatic  #120                // Method kotlin/sequences/SequencesKt.map:(Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence;
     146: invokestatic  #124                // Method kotlin/sequences/SequencesKt.sorted:(Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence;
     149: invokestatic  #128                // Method kotlin/sequences/SequencesKt.toList:(Lkotlin/sequences/Sequence;)Ljava/util/List;
     152: astore        5
     154: aload_3
     155: aload         4
     157: invokestatic  #134                // Method kotlin/io/CloseableKt.closeFinally:(Ljava/io/Closeable;Ljava/lang/Throwable;)V
     160: aload         5
     162: goto          185
     165: astore        6
     167: aload         6
     169: astore        4
     171: aload         6
     173: athrow
     174: astore        6
     176: aload_3
     177: aload         4
     179: invokestatic  #134                // Method kotlin/io/CloseableKt.closeFinally:(Ljava/io/Closeable;Ljava/lang/Throwable;)V
     182: aload         6
     184: athrow
     185: nop
     186: areturn
    Exception table:
       from    to  target type
         106   154   165   Class java/lang/Throwable
         106   154   174   any
         165   174   174   any
         174   176   174   any

  public static final int differences(java.util.List<java.lang.Integer>);
    Code:
       0: aload_0
       1: ldc           #157                // String input
       3: invokestatic  #62                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: iconst_0
       7: istore_1
       8: iconst_1
       9: istore_2
      10: iconst_0
      11: istore_3
      12: aload_0
      13: invokeinterface #161,  1          // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
      18: astore        4
      20: aload         4
      22: invokeinterface #167,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
      27: ifeq          91
      30: aload         4
      32: invokeinterface #171,  1          // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      37: checkcast     #173                // class java/lang/Number
      40: invokevirtual #177                // Method java/lang/Number.intValue:()I
      43: istore        5
      45: iload         5
      47: iload_3
      48: isub
      49: tableswitch   { // 1 to 3
                     1: 76
                     2: 85
                     3: 82
               default: 85
          }
      76: iinc          1, 1
      79: goto          85
      82: iinc          2, 1
      85: iload         5
      87: istore_3
      88: goto          20
      91: iload_1
      92: iload_2
      93: imul
      94: ireturn

  public static void main(java.lang.String[]);
    Code:
       0: invokestatic  #185                // Method main:()V
       3: return
}

Compiled from "App.kt"
final class net.sinceat.aoc2020.day10.AppKt$parseFile$1$1 extends kotlin.jvm.internal.FunctionReferenceImpl implements kotlin.jvm.functions.Function1<java.lang.String, java.lang.Boolean> {
  public static final net.sinceat.aoc2020.day10.AppKt$parseFile$1$1 INSTANCE;

  net.sinceat.aoc2020.day10.AppKt$parseFile$1$1();
    Code:
       0: aload_0
       1: iconst_1
       2: ldc           #11                 // class kotlin/text/StringsKt
       4: ldc           #13                 // String isNotBlank
       6: ldc           #15                 // String isNotBlank(Ljava/lang/CharSequence;)Z
       8: iconst_1
       9: invokespecial #18                 // Method kotlin/jvm/internal/FunctionReferenceImpl."<init>":(ILjava/lang/Class;Ljava/lang/String;Ljava/lang/String;I)V
      12: return

  public final java.lang.Boolean invoke(java.lang.String);
    Code:
       0: aload_1
       1: ldc           #24                 // String p0
       3: invokestatic  #30                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_1
       7: checkcast     #32                 // class java/lang/CharSequence
      10: invokestatic  #36                 // Method kotlin/text/StringsKt.isBlank:(Ljava/lang/CharSequence;)Z
      13: ifne          20
      16: iconst_1
      17: goto          21
      20: iconst_0
      21: invokestatic  #42                 // Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
      24: areturn

  public java.lang.Object invoke(java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: checkcast     #46                 // class java/lang/String
       5: invokevirtual #48                 // Method invoke:(Ljava/lang/String;)Ljava/lang/Boolean;
       8: areturn

  static {};
    Code:
       0: new           #2                  // class net/sinceat/aoc2020/day10/AppKt$parseFile$1$1
       3: dup
       4: invokespecial #53                 // Method "<init>":()V
       7: putstatic     #56                 // Field INSTANCE:Lnet/sinceat/aoc2020/day10/AppKt$parseFile$1$1;
      10: return
}

Compiled from "App.kt"
final class net.sinceat.aoc2020.day10.AppKt$parseFile$1$2 extends kotlin.jvm.internal.FunctionReferenceImpl implements kotlin.jvm.functions.Function1<java.lang.String, java.lang.Integer> {
  public static final net.sinceat.aoc2020.day10.AppKt$parseFile$1$2 INSTANCE;

  net.sinceat.aoc2020.day10.AppKt$parseFile$1$2();
    Code:
       0: aload_0
       1: iconst_1
       2: ldc           #11                 // class kotlin/text/StringsKt
       4: ldc           #13                 // String toInt
       6: ldc           #15                 // String toInt(Ljava/lang/String;)I
       8: iconst_1
       9: invokespecial #18                 // Method kotlin/jvm/internal/FunctionReferenceImpl."<init>":(ILjava/lang/Class;Ljava/lang/String;Ljava/lang/String;I)V
      12: return

  public final java.lang.Integer invoke(java.lang.String);
    Code:
       0: aload_1
       1: ldc           #24                 // String p0
       3: invokestatic  #30                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_1
       7: invokestatic  #36                 // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I
      10: invokestatic  #40                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      13: areturn

  public java.lang.Object invoke(java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: checkcast     #44                 // class java/lang/String
       5: invokevirtual #46                 // Method invoke:(Ljava/lang/String;)Ljava/lang/Integer;
       8: areturn

  static {};
    Code:
       0: new           #2                  // class net/sinceat/aoc2020/day10/AppKt$parseFile$1$2
       3: dup
       4: invokespecial #51                 // Method "<init>":()V
       7: putstatic     #54                 // Field INSTANCE:Lnet/sinceat/aoc2020/day10/AppKt$parseFile$1$2;
      10: return
}