package io.github.tomplum.aoc.extensions

import kotlin.math.pow

/**
 * Returns the product of all of the integers in the given list.
 */
fun List<Int>.product(): Int = if (isNotEmpty()) reduce { product, next -> product * next } else 0
fun List<Long>.product(): Long = if (isNotEmpty()) reduce { product, next -> product * next } else 0

/**
 * Converts the [IntArray] into its decimal equivalent.
 * This assumes the array contains only 1s and 0s.
 * @return The decimal representation.
 */
fun IntArray.toDecimal(): Long = reversed()
    .mapIndexed { i, bit -> if (bit == 1) 2.0.pow(i) else 0 }
    .map { integer -> integer.toLong() }
    .sum()

/**
 * For two sets A and B, the Cartesian product of A and B is denoted by A×B and defined as:
 * A×B = { (a,b) | aϵA and bϵB }
 *
 * Put simply, the Cartesian Product is the multiplication of two sets to form the set of all ordered pairs.
 * This function returns the cartesian product of itself and the given set, meaning A and B are [this] and [other].
 *
 * @see cartesianProductQuadratic for a variant that returns the product of itself.
 */
fun <S, T> List<S>.cartesianProduct(other: List<T>): List<Pair<S, T>> = this.flatMap { value ->
    List(other.size){ i -> Pair(value, other[i]) }
}

/**
 * For two sets A and B, the Cartesian product of A and B is denoted by A×B and defined as:
 * A×B = { (a,b) | aϵA and bϵB }
 *
 * Put simply, the Cartesian Product is the multiplication of two sets to form the set of all ordered pairs.
 * This function returns the cartesian product of itself, meaning both A and B are simply [this].
 *
 * @see cartesianProductCubic for a variant that accepts another set.
 */
fun <T> List<T>.cartesianProductQuadratic(): List<Pair<T, T>> = this.flatMap { value ->
    List(this.size){ i -> Pair(value, this[i]) }
}

/**
 * For three sets A, B and C, the Cartesian product of A, B and C is denoted by A×B×C and defined as:
 * A×B×C = { (p, q, r) | pϵA and qϵB and rϵC }
 *
 * Put simply, the Cartesian Product is the multiplication of three sets to form the set of all ordered pairs.
 * This function returns the cartesian product of itself and the given sets, meaning that A, B & C are all [this].
 *
 * @see cartesianProductQuadratic for a variation that simply finds the product of itself.
 */
fun <T> List<T>.cartesianProductCubic(): List<Triple<T, T, T>> = cartesianProduct(this, this, this).map { set ->
    Triple(set[0], set[1], set[2])
}

/**
 * For three sets A, B and C, the Cartesian product of A, B and C is denoted by A×B×C and defined as:
 * A×B×C = { (p, q, r) | pϵA and qϵB and rϵC }
 *
 * Put simply, the Cartesian Product is the multiplication of three sets to form the set of all ordered pairs.
 * This function returns the cartesian product of itself and the given sets, meaning both A, B and C are [this],
 * [second] and [third] respectively.
 */
fun <T> List<T>.cartesianProductCubic(second: List<T>, third: List<T>): List<Triple<T, T, T>> =
        cartesianProduct(this, second, third).map { set -> Triple(set[0], set[1], set[2]) }

/**
 * Finds the Cartesian Product of any number of given [sets].
 */
private fun <T> cartesianProduct(vararg sets: List<T>): List<List<T>> = sets.fold(listOf(listOf())) { acc, set ->
    acc.flatMap { list -> set.map { element -> list + element } }
}

Compiled from "CollectionExtensions.kt"
public final class io.github.tomplum.aoc.extensions.CollectionExtensionsKt {
  public static final int product(java.util.List<java.lang.Integer>);
    Code:
       0: aload_0
       1: ldc           #10                 // String <this>
       3: invokestatic  #16                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: checkcast     #18                 // class java/util/Collection
      10: invokeinterface #22,  1           // InterfaceMethod java/util/Collection.isEmpty:()Z
      15: ifne          22
      18: iconst_1
      19: goto          23
      22: iconst_0
      23: ifeq          127
      26: aload_0
      27: checkcast     #24                 // class java/lang/Iterable
      30: astore_1
      31: iconst_0
      32: istore_2
      33: aload_1
      34: invokeinterface #28,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
      39: astore_3
      40: aload_3
      41: invokeinterface #33,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      46: ifne          59
      49: new           #35                 // class java/lang/UnsupportedOperationException
      52: dup
      53: ldc           #37                 // String Empty collection can\'t be reduced.
      55: invokespecial #41                 // Method java/lang/UnsupportedOperationException."<init>":(Ljava/lang/String;)V
      58: athrow
      59: aload_3
      60: invokeinterface #45,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      65: astore        4
      67: aload_3
      68: invokeinterface #33,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      73: ifeq          116
      76: aload         4
      78: aload_3
      79: invokeinterface #45,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      84: checkcast     #47                 // class java/lang/Number
      87: invokevirtual #51                 // Method java/lang/Number.intValue:()I
      90: istore        5
      92: checkcast     #47                 // class java/lang/Number
      95: invokevirtual #51                 // Method java/lang/Number.intValue:()I
      98: istore        6
     100: iconst_0
     101: istore        7
     103: iload         6
     105: iload         5
     107: imul
     108: invokestatic  #57                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
     111: astore        4
     113: goto          67
     116: aload         4
     118: checkcast     #47                 // class java/lang/Number
     121: invokevirtual #51                 // Method java/lang/Number.intValue:()I
     124: goto          128
     127: iconst_0
     128: ireturn

  public static final long product(java.util.List<java.lang.Long>);
    Code:
       0: aload_0
       1: ldc           #10                 // String <this>
       3: invokestatic  #16                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: checkcast     #18                 // class java/util/Collection
      10: invokeinterface #22,  1           // InterfaceMethod java/util/Collection.isEmpty:()Z
      15: ifne          22
      18: iconst_1
      19: goto          23
      22: iconst_0
      23: ifeq          127
      26: aload_0
      27: checkcast     #24                 // class java/lang/Iterable
      30: astore_1
      31: iconst_0
      32: istore_2
      33: aload_1
      34: invokeinterface #28,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
      39: astore_3
      40: aload_3
      41: invokeinterface #33,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      46: ifne          59
      49: new           #35                 // class java/lang/UnsupportedOperationException
      52: dup
      53: ldc           #37                 // String Empty collection can\'t be reduced.
      55: invokespecial #41                 // Method java/lang/UnsupportedOperationException."<init>":(Ljava/lang/String;)V
      58: athrow
      59: aload_3
      60: invokeinterface #45,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      65: astore        4
      67: aload_3
      68: invokeinterface #33,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      73: ifeq          116
      76: aload         4
      78: aload_3
      79: invokeinterface #45,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      84: checkcast     #47                 // class java/lang/Number
      87: invokevirtual #76                 // Method java/lang/Number.longValue:()J
      90: lstore        5
      92: checkcast     #47                 // class java/lang/Number
      95: invokevirtual #76                 // Method java/lang/Number.longValue:()J
      98: lstore        7
     100: iconst_0
     101: istore        9
     103: lload         7
     105: lload         5
     107: lmul
     108: invokestatic  #81                 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
     111: astore        4
     113: goto          67
     116: aload         4
     118: checkcast     #47                 // class java/lang/Number
     121: invokevirtual #76                 // Method java/lang/Number.longValue:()J
     124: goto          128
     127: lconst_0
     128: lreturn

  public static final long toDecimal(int[]);
    Code:
       0: aload_0
       1: ldc           #10                 // String <this>
       3: invokestatic  #16                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: invokestatic  #91                 // Method kotlin/collections/ArraysKt.reversed:([I)Ljava/util/List;
      10: checkcast     #24                 // class java/lang/Iterable
      13: astore_1
      14: nop
      15: iconst_0
      16: istore_2
      17: aload_1
      18: astore_3
      19: new           #93                 // class java/util/ArrayList
      22: dup
      23: aload_1
      24: bipush        10
      26: invokestatic  #99                 // Method kotlin/collections/CollectionsKt.collectionSizeOrDefault:(Ljava/lang/Iterable;I)I
      29: invokespecial #102                // Method java/util/ArrayList."<init>":(I)V
      32: checkcast     #18                 // class java/util/Collection
      35: astore        4
      37: iconst_0
      38: istore        5
      40: iconst_0
      41: istore        6
      43: aload_3
      44: invokeinterface #28,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
      49: astore        7
      51: aload         7
      53: invokeinterface #33,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      58: ifeq          143
      61: aload         7
      63: invokeinterface #45,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      68: astore        8
      70: aload         4
      72: iload         6
      74: iinc          6, 1
      77: istore        9
      79: iload         9
      81: ifge          87
      84: invokestatic  #106                // Method kotlin/collections/CollectionsKt.throwIndexOverflow:()V
      87: iload         9
      89: aload         8
      91: checkcast     #47                 // class java/lang/Number
      94: invokevirtual #51                 // Method java/lang/Number.intValue:()I
      97: istore        10
      99: istore        11
     101: astore        13
     103: iconst_0
     104: istore        12
     106: iload         10
     108: iconst_1
     109: if_icmpne     127
     112: ldc2_w        #107                // double 2.0d
     115: iload         11
     117: i2d
     118: invokestatic  #114                // Method java/lang/Math.pow:(DD)D
     121: invokestatic  #119                // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
     124: goto          131
     127: iconst_0
     128: invokestatic  #57                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
     131: aload         13
     133: swap
     134: invokeinterface #123,  2          // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     139: pop
     140: goto          51
     143: aload         4
     145: checkcast     #70                 // class java/util/List
     148: nop
     149: checkcast     #24                 // class java/lang/Iterable
     152: astore_1
     153: nop
     154: iconst_0
     155: istore_2
     156: aload_1
     157: astore_3
     158: new           #93                 // class java/util/ArrayList
     161: dup
     162: aload_1
     163: bipush        10
     165: invokestatic  #99                 // Method kotlin/collections/CollectionsKt.collectionSizeOrDefault:(Ljava/lang/Iterable;I)I
     168: invokespecial #102                // Method java/util/ArrayList."<init>":(I)V
     171: checkcast     #18                 // class java/util/Collection
     174: astore        4
     176: iconst_0
     177: istore        5
     179: aload_3
     180: invokeinterface #28,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
     185: astore        6
     187: aload         6
     189: invokeinterface #33,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
     194: ifeq          240
     197: aload         6
     199: invokeinterface #45,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     204: astore        7
     206: aload         4
     208: aload         7
     210: astore        8
     212: astore        13
     214: iconst_0
     215: istore        9
     217: aload         8
     219: checkcast     #47                 // class java/lang/Number
     222: invokevirtual #76                 // Method java/lang/Number.longValue:()J
     225: invokestatic  #81                 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
     228: aload         13
     230: swap
     231: invokeinterface #123,  2          // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     236: pop
     237: goto          187
     240: aload         4
     242: checkcast     #70                 // class java/util/List
     245: nop
     246: checkcast     #24                 // class java/lang/Iterable
     249: invokestatic  #127                // Method kotlin/collections/CollectionsKt.sumOfLong:(Ljava/lang/Iterable;)J
     252: lreturn

  public static final <S, T> java.util.List<kotlin.Pair<S, T>> cartesianProduct(java.util.List<? extends S>, java.util.List<? extends T>);
    Code:
       0: aload_0
       1: ldc           #10                 // String <this>
       3: invokestatic  #16                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_1
       7: ldc           #152                // String other
       9: invokestatic  #16                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
      12: aload_0
      13: checkcast     #24                 // class java/lang/Iterable
      16: astore_2
      17: iconst_0
      18: istore_3
      19: aload_2
      20: astore        4
      22: new           #93                 // class java/util/ArrayList
      25: dup
      26: invokespecial #154                // Method java/util/ArrayList."<init>":()V
      29: checkcast     #18                 // class java/util/Collection
      32: astore        5
      34: iconst_0
      35: istore        6
      37: aload         4
      39: invokeinterface #28,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
      44: astore        7
      46: aload         7
      48: invokeinterface #33,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      53: ifeq          168
      56: aload         7
      58: invokeinterface #45,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      63: astore        8
      65: aload         8
      67: astore        9
      69: iconst_0
      70: istore        10
      72: aload_1
      73: invokeinterface #157,  1          // InterfaceMethod java/util/List.size:()I
      78: istore        11
      80: new           #93                 // class java/util/ArrayList
      83: dup
      84: iload         11
      86: invokespecial #102                // Method java/util/ArrayList."<init>":(I)V
      89: astore        12
      91: iconst_0
      92: istore        13
      94: iload         13
      96: iload         11
      98: if_icmpge     146
     101: iload         13
     103: istore        14
     105: aload         12
     107: iload         14
     109: istore        15
     111: astore        16
     113: iconst_0
     114: istore        17
     116: new           #159                // class kotlin/Pair
     119: dup
     120: aload         9
     122: aload_1
     123: iload         15
     125: invokeinterface #163,  2          // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
     130: invokespecial #166                // Method kotlin/Pair."<init>":(Ljava/lang/Object;Ljava/lang/Object;)V
     133: aload         16
     135: swap
     136: invokevirtual #167                // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
     139: pop
     140: iinc          13, 1
     143: goto          94
     146: aload         12
     148: checkcast     #70                 // class java/util/List
     151: checkcast     #24                 // class java/lang/Iterable
     154: nop
     155: astore        9
     157: aload         5
     159: aload         9
     161: invokestatic  #171                // Method kotlin/collections/CollectionsKt.addAll:(Ljava/util/Collection;Ljava/lang/Iterable;)Z
     164: pop
     165: goto          46
     168: aload         5
     170: checkcast     #70                 // class java/util/List
     173: nop
     174: areturn

  public static final <T> java.util.List<kotlin.Pair<T, T>> cartesianProductQuadratic(java.util.List<? extends T>);
    Code:
       0: aload_0
       1: ldc           #10                 // String <this>
       3: invokestatic  #16                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: checkcast     #24                 // class java/lang/Iterable
      10: astore_1
      11: iconst_0
      12: istore_2
      13: aload_1
      14: astore_3
      15: new           #93                 // class java/util/ArrayList
      18: dup
      19: invokespecial #154                // Method java/util/ArrayList."<init>":()V
      22: checkcast     #18                 // class java/util/Collection
      25: astore        4
      27: iconst_0
      28: istore        5
      30: aload_3
      31: invokeinterface #28,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
      36: astore        6
      38: aload         6
      40: invokeinterface #33,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      45: ifeq          160
      48: aload         6
      50: invokeinterface #45,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      55: astore        7
      57: aload         7
      59: astore        8
      61: iconst_0
      62: istore        9
      64: aload_0
      65: invokeinterface #157,  1          // InterfaceMethod java/util/List.size:()I
      70: istore        10
      72: new           #93                 // class java/util/ArrayList
      75: dup
      76: iload         10
      78: invokespecial #102                // Method java/util/ArrayList."<init>":(I)V
      81: astore        11
      83: iconst_0
      84: istore        12
      86: iload         12
      88: iload         10
      90: if_icmpge     138
      93: iload         12
      95: istore        13
      97: aload         11
      99: iload         13
     101: istore        14
     103: astore        15
     105: iconst_0
     106: istore        16
     108: new           #159                // class kotlin/Pair
     111: dup
     112: aload         8
     114: aload_0
     115: iload         14
     117: invokeinterface #163,  2          // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
     122: invokespecial #166                // Method kotlin/Pair."<init>":(Ljava/lang/Object;Ljava/lang/Object;)V
     125: aload         15
     127: swap
     128: invokevirtual #167                // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
     131: pop
     132: iinc          12, 1
     135: goto          86
     138: aload         11
     140: checkcast     #70                 // class java/util/List
     143: checkcast     #24                 // class java/lang/Iterable
     146: nop
     147: astore        8
     149: aload         4
     151: aload         8
     153: invokestatic  #171                // Method kotlin/collections/CollectionsKt.addAll:(Ljava/util/Collection;Ljava/lang/Iterable;)Z
     156: pop
     157: goto          38
     160: aload         4
     162: checkcast     #70                 // class java/util/List
     165: nop
     166: areturn

  public static final <T> java.util.List<kotlin.Triple<T, T, T>> cartesianProductCubic(java.util.List<? extends T>);
    Code:
       0: aload_0
       1: ldc           #10                 // String <this>
       3: invokestatic  #16                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: iconst_3
       7: anewarray     #70                 // class java/util/List
      10: astore_1
      11: aload_1
      12: iconst_0
      13: aload_0
      14: aastore
      15: aload_1
      16: iconst_1
      17: aload_0
      18: aastore
      19: aload_1
      20: iconst_2
      21: aload_0
      22: aastore
      23: aload_1
      24: invokestatic  #192                // Method cartesianProduct:([Ljava/util/List;)Ljava/util/List;
      27: checkcast     #24                 // class java/lang/Iterable
      30: astore_1
      31: iconst_0
      32: istore_2
      33: aload_1
      34: astore_3
      35: new           #93                 // class java/util/ArrayList
      38: dup
      39: aload_1
      40: bipush        10
      42: invokestatic  #99                 // Method kotlin/collections/CollectionsKt.collectionSizeOrDefault:(Ljava/lang/Iterable;I)I
      45: invokespecial #102                // Method java/util/ArrayList."<init>":(I)V
      48: checkcast     #18                 // class java/util/Collection
      51: astore        4
      53: iconst_0
      54: istore        5
      56: aload_3
      57: invokeinterface #28,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
      62: astore        6
      64: aload         6
      66: invokeinterface #33,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      71: ifeq          140
      74: aload         6
      76: invokeinterface #45,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      81: astore        7
      83: aload         4
      85: aload         7
      87: checkcast     #70                 // class java/util/List
      90: astore        8
      92: astore        10
      94: iconst_0
      95: istore        9
      97: new           #194                // class kotlin/Triple
     100: dup
     101: aload         8
     103: iconst_0
     104: invokeinterface #163,  2          // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
     109: aload         8
     111: iconst_1
     112: invokeinterface #163,  2          // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
     117: aload         8
     119: iconst_2
     120: invokeinterface #163,  2          // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
     125: invokespecial #197                // Method kotlin/Triple."<init>":(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
     128: aload         10
     130: swap
     131: invokeinterface #123,  2          // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     136: pop
     137: goto          64
     140: aload         4
     142: checkcast     #70                 // class java/util/List
     145: nop
     146: areturn

  public static final <T> java.util.List<kotlin.Triple<T, T, T>> cartesianProductCubic(java.util.List<? extends T>, java.util.List<? extends T>, java.util.List<? extends T>);
    Code:
       0: aload_0
       1: ldc           #10                 // String <this>
       3: invokestatic  #16                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_1
       7: ldc           #204                // String second
       9: invokestatic  #16                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
      12: aload_2
      13: ldc           #206                // String third
      15: invokestatic  #16                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
      18: iconst_3
      19: anewarray     #70                 // class java/util/List
      22: astore_3
      23: aload_3
      24: iconst_0
      25: aload_0
      26: aastore
      27: aload_3
      28: iconst_1
      29: aload_1
      30: aastore
      31: aload_3
      32: iconst_2
      33: aload_2
      34: aastore
      35: aload_3
      36: invokestatic  #192                // Method cartesianProduct:([Ljava/util/List;)Ljava/util/List;
      39: checkcast     #24                 // class java/lang/Iterable
      42: astore_3
      43: iconst_0
      44: istore        4
      46: aload_3
      47: astore        5
      49: new           #93                 // class java/util/ArrayList
      52: dup
      53: aload_3
      54: bipush        10
      56: invokestatic  #99                 // Method kotlin/collections/CollectionsKt.collectionSizeOrDefault:(Ljava/lang/Iterable;I)I
      59: invokespecial #102                // Method java/util/ArrayList."<init>":(I)V
      62: checkcast     #18                 // class java/util/Collection
      65: astore        6
      67: iconst_0
      68: istore        7
      70: aload         5
      72: invokeinterface #28,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
      77: astore        8
      79: aload         8
      81: invokeinterface #33,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      86: ifeq          155
      89: aload         8
      91: invokeinterface #45,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      96: astore        9
      98: aload         6
     100: aload         9
     102: checkcast     #70                 // class java/util/List
     105: astore        10
     107: astore        12
     109: iconst_0
     110: istore        11
     112: new           #194                // class kotlin/Triple
     115: dup
     116: aload         10
     118: iconst_0
     119: invokeinterface #163,  2          // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
     124: aload         10
     126: iconst_1
     127: invokeinterface #163,  2          // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
     132: aload         10
     134: iconst_2
     135: invokeinterface #163,  2          // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
     140: invokespecial #197                // Method kotlin/Triple."<init>":(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
     143: aload         12
     145: swap
     146: invokeinterface #123,  2          // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     151: pop
     152: goto          79
     155: aload         6
     157: checkcast     #70                 // class java/util/List
     160: nop
     161: areturn

  private static final <T> java.util.List<java.util.List<T>> cartesianProduct(java.util.List<? extends T>...);
    Code:
       0: aload_0
       1: astore_1
       2: invokestatic  #212                // Method kotlin/collections/CollectionsKt.emptyList:()Ljava/util/List;
       5: invokestatic  #216                // Method kotlin/collections/CollectionsKt.listOf:(Ljava/lang/Object;)Ljava/util/List;
       8: astore_2
       9: iconst_0
      10: istore_3
      11: aload_2
      12: astore        4
      14: iconst_0
      15: istore        5
      17: aload_1
      18: arraylength
      19: istore        6
      21: iload         5
      23: iload         6
      25: if_icmpge     247
      28: aload_1
      29: iload         5
      31: aaload
      32: astore        7
      34: aload         4
      36: aload         7
      38: astore        8
      40: astore        9
      42: iconst_0
      43: istore        10
      45: aload         9
      47: checkcast     #24                 // class java/lang/Iterable
      50: astore        11
      52: iconst_0
      53: istore        12
      55: aload         11
      57: astore        13
      59: new           #93                 // class java/util/ArrayList
      62: dup
      63: invokespecial #154                // Method java/util/ArrayList."<init>":()V
      66: checkcast     #18                 // class java/util/Collection
      69: astore        14
      71: iconst_0
      72: istore        15
      74: aload         13
      76: invokeinterface #28,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
      81: astore        16
      83: aload         16
      85: invokeinterface #33,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      90: ifeq          232
      93: aload         16
      95: invokeinterface #45,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     100: astore        17
     102: aload         17
     104: checkcast     #70                 // class java/util/List
     107: astore        18
     109: iconst_0
     110: istore        19
     112: aload         8
     114: checkcast     #24                 // class java/lang/Iterable
     117: astore        20
     119: iconst_0
     120: istore        21
     122: aload         20
     124: astore        22
     126: new           #93                 // class java/util/ArrayList
     129: dup
     130: aload         20
     132: bipush        10
     134: invokestatic  #99                 // Method kotlin/collections/CollectionsKt.collectionSizeOrDefault:(Ljava/lang/Iterable;I)I
     137: invokespecial #102                // Method java/util/ArrayList."<init>":(I)V
     140: checkcast     #18                 // class java/util/Collection
     143: astore        23
     145: iconst_0
     146: istore        24
     148: aload         22
     150: invokeinterface #28,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
     155: astore        25
     157: aload         25
     159: invokeinterface #33,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
     164: ifeq          209
     167: aload         25
     169: invokeinterface #45,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     174: astore        26
     176: aload         23
     178: aload         26
     180: astore        27
     182: astore        28
     184: iconst_0
     185: istore        29
     187: aload         18
     189: checkcast     #18                 // class java/util/Collection
     192: aload         27
     194: invokestatic  #220                // Method kotlin/collections/CollectionsKt.plus:(Ljava/util/Collection;Ljava/lang/Object;)Ljava/util/List;
     197: aload         28
     199: swap
     200: invokeinterface #123,  2          // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     205: pop
     206: goto          157
     209: aload         23
     211: checkcast     #70                 // class java/util/List
     214: nop
     215: checkcast     #24                 // class java/lang/Iterable
     218: nop
     219: astore        18
     221: aload         14
     223: aload         18
     225: invokestatic  #171                // Method kotlin/collections/CollectionsKt.addAll:(Ljava/util/Collection;Ljava/lang/Iterable;)Z
     228: pop
     229: goto          83
     232: aload         14
     234: checkcast     #70                 // class java/util/List
     237: nop
     238: nop
     239: astore        4
     241: iinc          5, 1
     244: goto          21
     247: aload         4
     249: areturn
}


package io.github.tomplum.aoc.ferry.docking.program

/**
 * A bit-mask consisting of either 0, 1 or X.
 *
 * An X indicates a 'Floating Bit' that is in a state of super-position and is therefore
 * simultaneously a 0 and a 1. This means that every [mask] with a floating-bit has
 * 2^N permutations wheres N is the number of floating-bits.
 *
 * @param mask The string of bits.
 */
data class BitMask(private val mask: String) {

    /**
     * Masks the given [value] with the [mask] bits.
     * Any floating-bits (X) are ignored.
     *
     * E.g;
     * [value]:  000000000000000000000000000000001011  (decimal 11)
     * [mask]:   XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X
     * result:   000000000000000000000000000001001001  (decimal 73)
     *
     * @return The binary representation of the masked [value].
     */
    fun applyTo(value: Int): String {
        return mask.zip(value.to36bit()) { mask, bit ->
            when (mask) {
                '0', '1' -> mask
                else -> bit
            }
        }.joinToString("")
    }

    /**
     * Masks the given [value] with the [mask] bits.
     * Floating-bits are taken into account and so all mask permutations are applied.
     *
     * E.g;
     *
     * First, [applyIgnoreFloating] masks the [value] and ignores floating-bits.
     * [value]: 000000000000000000000000000000101010  (decimal 42)
     * [mask]:  000000000000000000000000000000X1001X
     * result:  000000000000000000000000000000X1101X
     *
     * Then, [getFloatingPermutations] generates all combinations of masks.
     * 000000000000000000000000000000011010  (decimal 26)
     * 000000000000000000000000000000011011  (decimal 27)
     * 000000000000000000000000000000111010  (decimal 58)
     * 000000000000000000000000000000111011  (decimal 59)
     *
     * @return The binary representations of all the masked values.
     */
    fun applyFloatingTo(value: Int): List<String> {
        val floatingMask = applyIgnoreFloating(value)
        return getFloatingPermutations(floatingMask, mutableListOf())
    }

    /**
     * Masks the given [value] with only literal floating and 1 bits.
     * 0 bits are ignored.
     *
     * E.g;
     * [value]: 000000000000000000000000000000101010  (decimal 42)
     * [mask]:  000000000000000000000000000000X1001X
     * result:  000000000000000000000000000000X1101X
     *
     * @return The masked value with floating bits intact.
     */
    private fun applyIgnoreFloating(value: Int): String {
        return mask.zip(value.to36bit()) { mask, bit ->
            when (mask) {
                'X', '1' -> mask
                else -> bit
            }
        }.joinToString("")
    }

    /**
     * Generates all the combinations of masks by resolving the floating-bits state of
     * super-position. For a [mask] with N floating-bits, 2^N masks will be produced.
     * @param mask The mask containing 'X' floating-bit values.
     * @param masks The list to collect the mask combinations in.
     * @return A list of all mask combinations.
     */
    private fun getFloatingPermutations(mask: String, masks: MutableList<String>): List<String> {
        if (mask.contains('X')) {
            val firstCandidate = mask.replaceFirst('X', '0')
            if (!masks.contains(firstCandidate)) {
                getFloatingPermutations(firstCandidate, masks)
            }

            val secondCandidate = mask.replaceFirst('X', '1')
            if (!masks.contains(secondCandidate)) {
                getFloatingPermutations(secondCandidate, masks)
            }
        }
        if (!mask.contains('X')) masks.add(mask)
        return masks
    }

    /**
     * Pads a binary string with 0s until it is 36 bits in length.
     * @return A 36-bit representation of the given binary string.
     */
    private fun Int.to36bit(): String = toString(2).padStart(36, '0')
}

Compiled from "BitMask.kt"
public final class io.github.tomplum.aoc.ferry.docking.program.BitMask {
  private final java.lang.String mask;

  public io.github.tomplum.aoc.ferry.docking.program.BitMask(java.lang.String);
    Code:
       0: aload_1
       1: ldc           #9                  // String mask
       3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: invokespecial #18                 // Method java/lang/Object."<init>":()V
      10: aload_0
      11: aload_1
      12: putfield      #21                 // Field mask:Ljava/lang/String;
      15: return

  public final java.lang.String applyTo(int);
    Code:
       0: aload_0
       1: getfield      #21                 // Field mask:Ljava/lang/String;
       4: checkcast     #27                 // class java/lang/CharSequence
       7: astore_2
       8: aload_0
       9: iload_1
      10: invokespecial #30                 // Method to36bit:(I)Ljava/lang/String;
      13: checkcast     #27                 // class java/lang/CharSequence
      16: astore_3
      17: iconst_0
      18: istore        4
      20: aload_2
      21: invokeinterface #34,  1           // InterfaceMethod java/lang/CharSequence.length:()I
      26: aload_3
      27: invokeinterface #34,  1           // InterfaceMethod java/lang/CharSequence.length:()I
      32: invokestatic  #40                 // Method java/lang/Math.min:(II)I
      35: istore        5
      37: new           #42                 // class java/util/ArrayList
      40: dup
      41: iload         5
      43: invokespecial #45                 // Method java/util/ArrayList."<init>":(I)V
      46: astore        6
      48: iconst_0
      49: istore        7
      51: iload         7
      53: iload         5
      55: if_icmpge     132
      58: aload         6
      60: aload_2
      61: iload         7
      63: invokeinterface #49,  2           // InterfaceMethod java/lang/CharSequence.charAt:(I)C
      68: aload_3
      69: iload         7
      71: invokeinterface #49,  2           // InterfaceMethod java/lang/CharSequence.charAt:(I)C
      76: istore        8
      78: istore        9
      80: astore        11
      82: iconst_0
      83: istore        10
      85: iload         9
      87: tableswitch   { // 48 to 49
                    48: 108
                    49: 108
               default: 113
          }
     108: iload         9
     110: goto          115
     113: iload         8
     115: nop
     116: invokestatic  #55                 // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
     119: aload         11
     121: swap
     122: invokevirtual #59                 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
     125: pop
     126: iinc          7, 1
     129: goto          51
     132: aload         6
     134: checkcast     #61                 // class java/util/List
     137: checkcast     #63                 // class java/lang/Iterable
     140: ldc           #65                 // String
     142: checkcast     #27                 // class java/lang/CharSequence
     145: aconst_null
     146: aconst_null
     147: iconst_0
     148: aconst_null
     149: aconst_null
     150: bipush        62
     152: aconst_null
     153: invokestatic  #71                 // Method kotlin/collections/CollectionsKt.joinToString$default:(Ljava/lang/Iterable;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;ILjava/lang/CharSequence;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/lang/String;
     156: areturn

  public final java.util.List<java.lang.String> applyFloatingTo(int);
    Code:
       0: aload_0
       1: iload_1
       2: invokespecial #90                 // Method applyIgnoreFloating:(I)Ljava/lang/String;
       5: astore_2
       6: aload_0
       7: aload_2
       8: new           #42                 // class java/util/ArrayList
      11: dup
      12: invokespecial #91                 // Method java/util/ArrayList."<init>":()V
      15: checkcast     #61                 // class java/util/List
      18: invokespecial #95                 // Method getFloatingPermutations:(Ljava/lang/String;Ljava/util/List;)Ljava/util/List;
      21: areturn

  private final java.lang.String applyIgnoreFloating(int);
    Code:
       0: aload_0
       1: getfield      #21                 // Field mask:Ljava/lang/String;
       4: checkcast     #27                 // class java/lang/CharSequence
       7: astore_2
       8: aload_0
       9: iload_1
      10: invokespecial #30                 // Method to36bit:(I)Ljava/lang/String;
      13: checkcast     #27                 // class java/lang/CharSequence
      16: astore_3
      17: iconst_0
      18: istore        4
      20: aload_2
      21: invokeinterface #34,  1           // InterfaceMethod java/lang/CharSequence.length:()I
      26: aload_3
      27: invokeinterface #34,  1           // InterfaceMethod java/lang/CharSequence.length:()I
      32: invokestatic  #40                 // Method java/lang/Math.min:(II)I
      35: istore        5
      37: new           #42                 // class java/util/ArrayList
      40: dup
      41: iload         5
      43: invokespecial #45                 // Method java/util/ArrayList."<init>":(I)V
      46: astore        6
      48: iconst_0
      49: istore        7
      51: iload         7
      53: iload         5
      55: if_icmpge     136
      58: aload         6
      60: aload_2
      61: iload         7
      63: invokeinterface #49,  2           // InterfaceMethod java/lang/CharSequence.charAt:(I)C
      68: aload_3
      69: iload         7
      71: invokeinterface #49,  2           // InterfaceMethod java/lang/CharSequence.charAt:(I)C
      76: istore        8
      78: istore        9
      80: astore        11
      82: iconst_0
      83: istore        10
      85: iload         9
      87: lookupswitch  { // 2
                    49: 112
                    88: 112
               default: 117
          }
     112: iload         9
     114: goto          119
     117: iload         8
     119: nop
     120: invokestatic  #55                 // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
     123: aload         11
     125: swap
     126: invokevirtual #59                 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
     129: pop
     130: iinc          7, 1
     133: goto          51
     136: aload         6
     138: checkcast     #61                 // class java/util/List
     141: checkcast     #63                 // class java/lang/Iterable
     144: ldc           #65                 // String
     146: checkcast     #27                 // class java/lang/CharSequence
     149: aconst_null
     150: aconst_null
     151: iconst_0
     152: aconst_null
     153: aconst_null
     154: bipush        62
     156: aconst_null
     157: invokestatic  #71                 // Method kotlin/collections/CollectionsKt.joinToString$default:(Ljava/lang/Iterable;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;ILjava/lang/CharSequence;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/lang/String;
     160: areturn

  private final java.util.List<java.lang.String> getFloatingPermutations(java.lang.String, java.util.List<java.lang.String>);
    Code:
       0: aload_1
       1: checkcast     #27                 // class java/lang/CharSequence
       4: bipush        88
       6: iconst_0
       7: iconst_2
       8: aconst_null
       9: invokestatic  #104                // Method kotlin/text/StringsKt.contains$default:(Ljava/lang/CharSequence;CZILjava/lang/Object;)Z
      12: ifeq          76
      15: aload_1
      16: bipush        88
      18: bipush        48
      20: iconst_0
      21: iconst_4
      22: aconst_null
      23: invokestatic  #108                // Method kotlin/text/StringsKt.replaceFirst$default:(Ljava/lang/String;CCZILjava/lang/Object;)Ljava/lang/String;
      26: astore_3
      27: aload_2
      28: aload_3
      29: invokeinterface #111,  2          // InterfaceMethod java/util/List.contains:(Ljava/lang/Object;)Z
      34: ifne          44
      37: aload_0
      38: aload_3
      39: aload_2
      40: invokespecial #95                 // Method getFloatingPermutations:(Ljava/lang/String;Ljava/util/List;)Ljava/util/List;
      43: pop
      44: aload_1
      45: bipush        88
      47: bipush        49
      49: iconst_0
      50: iconst_4
      51: aconst_null
      52: invokestatic  #108                // Method kotlin/text/StringsKt.replaceFirst$default:(Ljava/lang/String;CCZILjava/lang/Object;)Ljava/lang/String;
      55: astore        4
      57: aload_2
      58: aload         4
      60: invokeinterface #111,  2          // InterfaceMethod java/util/List.contains:(Ljava/lang/Object;)Z
      65: ifne          76
      68: aload_0
      69: aload         4
      71: aload_2
      72: invokespecial #95                 // Method getFloatingPermutations:(Ljava/lang/String;Ljava/util/List;)Ljava/util/List;
      75: pop
      76: aload_1
      77: checkcast     #27                 // class java/lang/CharSequence
      80: bipush        88
      82: iconst_0
      83: iconst_2
      84: aconst_null
      85: invokestatic  #104                // Method kotlin/text/StringsKt.contains$default:(Ljava/lang/CharSequence;CZILjava/lang/Object;)Z
      88: ifne          99
      91: aload_2
      92: aload_1
      93: invokeinterface #112,  2          // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      98: pop
      99: aload_2
     100: areturn

  private final java.lang.String to36bit(int);
    Code:
       0: iload_1
       1: iconst_2
       2: invokestatic  #124                // Method kotlin/text/CharsKt.checkRadix:(I)I
       5: invokestatic  #130                // Method java/lang/Integer.toString:(II)Ljava/lang/String;
       8: dup
       9: ldc           #132                // String toString(...)
      11: invokestatic  #135                // Method kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue:(Ljava/lang/Object;Ljava/lang/String;)V
      14: bipush        36
      16: bipush        48
      18: invokestatic  #139                // Method kotlin/text/StringsKt.padStart:(Ljava/lang/String;IC)Ljava/lang/String;
      21: areturn

  private final java.lang.String component1();
    Code:
       0: aload_0
       1: getfield      #21                 // Field mask:Ljava/lang/String;
       4: areturn

  public final io.github.tomplum.aoc.ferry.docking.program.BitMask copy(java.lang.String);
    Code:
       0: aload_1
       1: ldc           #9                  // String mask
       3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: new           #2                  // class io/github/tomplum/aoc/ferry/docking/program/BitMask
       9: dup
      10: aload_1
      11: invokespecial #146                // Method "<init>":(Ljava/lang/String;)V
      14: areturn

  public static io.github.tomplum.aoc.ferry.docking.program.BitMask copy$default(io.github.tomplum.aoc.ferry.docking.program.BitMask, java.lang.String, int, java.lang.Object);
    Code:
       0: iload_2
       1: iconst_1
       2: iand
       3: ifeq          11
       6: aload_0
       7: getfield      #21                 // Field mask:Ljava/lang/String;
      10: astore_1
      11: aload_0
      12: aload_1
      13: invokevirtual #150                // Method copy:(Ljava/lang/String;)Lio/github/tomplum/aoc/ferry/docking/program/BitMask;
      16: areturn

  public java.lang.String toString();
    Code:
       0: new           #152                // class java/lang/StringBuilder
       3: dup
       4: invokespecial #153                // Method java/lang/StringBuilder."<init>":()V
       7: ldc           #155                // String BitMask(mask=
       9: invokevirtual #159                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      12: aload_0
      13: getfield      #21                 // Field mask:Ljava/lang/String;
      16: invokevirtual #159                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: bipush        41
      21: invokevirtual #162                // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
      24: invokevirtual #164                // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      27: areturn

  public int hashCode();
    Code:
       0: aload_0
       1: getfield      #21                 // Field mask:Ljava/lang/String;
       4: invokevirtual #167                // Method java/lang/String.hashCode:()I
       7: ireturn

  public boolean equals(java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: if_acmpne     7
       5: iconst_1
       6: ireturn
       7: aload_1
       8: instanceof    #2                  // class io/github/tomplum/aoc/ferry/docking/program/BitMask
      11: ifne          16
      14: iconst_0
      15: ireturn
      16: aload_1
      17: checkcast     #2                  // class io/github/tomplum/aoc/ferry/docking/program/BitMask
      20: astore_2
      21: aload_0
      22: getfield      #21                 // Field mask:Ljava/lang/String;
      25: aload_2
      26: getfield      #21                 // Field mask:Ljava/lang/String;
      29: invokestatic  #173                // Method kotlin/jvm/internal/Intrinsics.areEqual:(Ljava/lang/Object;Ljava/lang/Object;)Z
      32: ifne          37
      35: iconst_0
      36: ireturn
      37: iconst_1
      38: ireturn
}